From aaa8a64fe6d6817b1c07eaccb72fa659ca75d153 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 4 Jun 2020 16:59:21 +0200 Subject: [PATCH 1/3] Where: allow variadic use This allows to use those methods as follows in a backward-compatible way: ->where('a = ? AND (b = ? OR c = ?)', 1, 2, 3); ->notWhere('? < INTERVAL(a, ?)', 1, [2, 3]); --- src/Where.php | 36 ++++++++++++++++++++++++++++++++---- src/WhereInterface.php | 22 ++++++++++------------ tests/WhereTest.php | 17 +++++++++++++++++ 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/Where.php b/src/Where.php index 1bd5851..1cf6d07 100644 --- a/src/Where.php +++ b/src/Where.php @@ -15,29 +15,33 @@ public function getWhere() return $this->where; } - public function where($condition, $operator = Sql::ALL) + public function where($condition, ...$args) { + list($condition, $operator) = $this->prepareConditionArguments($condition, $args); $this->mergeCondition($this->where, $this->buildCondition($condition, $operator), Sql::ALL); return $this; } - public function orWhere($condition, $operator = Sql::ALL) + public function orWhere($condition, ...$args) { + list($condition, $operator) = $this->prepareConditionArguments($condition, $args); $this->mergeCondition($this->where, $this->buildCondition($condition, $operator), Sql::ANY); return $this; } - public function notWhere($condition, $operator = Sql::ALL) + public function notWhere($condition, ...$args) { + list($condition, $operator) = $this->prepareConditionArguments($condition, $args); $this->mergeCondition($this->where, $this->buildCondition($condition, $operator), Sql::NOT_ALL); return $this; } - public function orNotWhere($condition, $operator = Sql::ALL) + public function orNotWhere($condition, ...$args) { + list($condition, $operator) = $this->prepareConditionArguments($condition, $args); $this->mergeCondition($this->where, $this->buildCondition($condition, $operator), Sql::NOT_ANY); return $this; @@ -92,6 +96,30 @@ protected function mergeCondition(&$base, array $condition, $operator) } } + /** + * This prepares arguments in a backward-compatible way according the + * description in the WhereInterface + * + * @param mixed $condition + * @param array $args + * @return array + */ + protected function prepareConditionArguments($condition, $args) + { + if (\is_string($condition) && \count($args)) { + $condition = [$condition => $args]; + $operator = Sql::ALL; + } else { + $operator = \array_shift($args); + } + + if ($operator === null) { + $operator = Sql::ALL; // default operator + } + + return [$condition, $operator]; + } + /** * Clone the properties provided by this trait * diff --git a/src/WhereInterface.php b/src/WhereInterface.php index 2cd1075..48eebfd 100644 --- a/src/WhereInterface.php +++ b/src/WhereInterface.php @@ -31,12 +31,13 @@ public function getWhere(); * the specified WHERE part using the AND operator. * * @param string|ExpressionInterface|Select|array $condition The WHERE condition - * @param string $operator The operator to combine multiple conditions with, - * if the condition is in the array format + * @param mixed $args When condition is a string, this allows to pass parameter values for eventual placeholders + * When condition is an array, the only allowed parameter is the operator that should be used + * to combine those conditions. This operator defaults to Sql::ALL (AND) * * @return $this */ - public function where($condition, $operator = Sql::ALL); + public function where($condition, ...$args); /** * Add a OR part to the WHERE part of the query @@ -44,12 +45,11 @@ public function where($condition, $operator = Sql::ALL); * Please see {@link where()} for the supported formats and restrictions regarding quoting of the field names. * * @param string|ExpressionInterface|Select|array $condition The WHERE condition - * @param string $operator The operator to combine multiple conditions with, - * if the condition is in the array format + * @param mixed ...$args Please see {@link where()} for details * * @return $this */ - public function orWhere($condition, $operator = Sql::ALL); + public function orWhere($condition, ...$args); /** * Add a AND NOT part to the WHERE part of the query @@ -57,12 +57,11 @@ public function orWhere($condition, $operator = Sql::ALL); * Please see {@link where()} for the supported formats and restrictions regarding quoting of the field names. * * @param string|ExpressionInterface|Select|array $condition The WHERE condition - * @param string $operator The operator to combine multiple conditions with, - * if the condition is in the array format + * @param mixed ...$args Please see {@link where()} for details * * @return $this */ - public function notWhere($condition, $operator = Sql::ALL); + public function notWhere($condition, ...$args); /** * Add a OR NOT part to the WHERE part of the query @@ -70,10 +69,9 @@ public function notWhere($condition, $operator = Sql::ALL); * Please see {@link where()} for the supported formats and restrictions regarding quoting of the field names. * * @param string|ExpressionInterface|Select|array $condition The WHERE condition - * @param string $operator The operator to combine multiple conditions with, - * if the condition is in the array format + * @param mixed ...$args Please see {@link where()} for details * * @return $this */ - public function orNotWhere($condition, $operator = Sql::ALL); + public function orNotWhere($condition, ...$args); } diff --git a/tests/WhereTest.php b/tests/WhereTest.php index 555d2e1..a368ad4 100644 --- a/tests/WhereTest.php +++ b/tests/WhereTest.php @@ -62,6 +62,23 @@ public function testSingleNotWhere() $this->assertCorrectStatementAndValues('WHERE NOT (foo = bar)', []); } + public function testVariadicWhereUsedVariadic() + { + $this->query->where('a IN (?) AND b < ?', [1, 2, 3], 4); + $this->assertCorrectStatementAndValues('WHERE a IN (?, ?, ?) AND b < ?', [1, 2, 3, 4]); + } + + public function testNotWhereCombiningVariadicAndArrayStyle() + { + $this->query->where('a = ?', 1); + $this->query->notWhere('a IN (?) AND b < ?', [2, 3, 4], 5); + $this->query->notWhere(['a = ?' => 6, 'b = ?' => 7], Sql::ANY); + $this->assertCorrectStatementAndValues( + 'WHERE ((a = ?) AND (NOT (a IN (?, ?, ?) AND b < ?))) AND (NOT ((a = ?) OR (b = ?)))', + [1, 2, 3, 4, 5, 6, 7] + ); + } + public function testNotWhereArrayFormat() { $this->query->notWhere(['c1 = x']); From d2ee0aa9426017fea1ebda0de152fb64be40416b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Jul 2020 16:08:23 +0200 Subject: [PATCH 2/3] Support classes implementing __toString() in where --- src/Where.php | 22 +++++++++---------- tests/ExpressionThatCanBeRenderedToString.php | 18 +++++++++++++++ tests/WhereTest.php | 11 ++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 tests/ExpressionThatCanBeRenderedToString.php diff --git a/src/Where.php b/src/Where.php index 1cf6d07..4cda175 100644 --- a/src/Where.php +++ b/src/Where.php @@ -97,24 +97,24 @@ protected function mergeCondition(&$base, array $condition, $operator) } /** - * This prepares arguments in a backward-compatible way according the - * description in the WhereInterface + * Prepare condition arguments from the different supported where styles * * @param mixed $condition * @param array $args + * * @return array */ - protected function prepareConditionArguments($condition, $args) + protected function prepareConditionArguments($condition, array $args) { - if (\is_string($condition) && \count($args)) { - $condition = [$condition => $args]; - $operator = Sql::ALL; - } else { - $operator = \array_shift($args); - } + // Default operator + $operator = Sql::ALL; - if ($operator === null) { - $operator = Sql::ALL; // default operator + if (! is_array($condition) && ! empty($args)) { + // Variadic + $condition = [(string) $condition => $args]; + } else { + // Array or string format + $operator = array_shift($args) ?: $operator; } return [$condition, $operator]; diff --git a/tests/ExpressionThatCanBeRenderedToString.php b/tests/ExpressionThatCanBeRenderedToString.php new file mode 100644 index 0000000..8805be8 --- /dev/null +++ b/tests/ExpressionThatCanBeRenderedToString.php @@ -0,0 +1,18 @@ +expression = $expression; + } + + public function __toString() + { + return $this->expression; + } +} diff --git a/tests/WhereTest.php b/tests/WhereTest.php index a368ad4..66c723a 100644 --- a/tests/WhereTest.php +++ b/tests/WhereTest.php @@ -226,6 +226,17 @@ public function testWhereWithSelectAndExpression() $this->assertCorrectStatementAndValues('WHERE EXISTS (SELECT 1 FROM t1 WHERE c2 = ? LIMIT 1)', [1]); } + public function testWhereWithExpressionThatCanBeRenderedToString() + { + $this->query->where( + new ExpressionThatCanBeRenderedToString("COALESCE('a', ?) = ?"), + [1, 2], + 1 + ); + + $this->assertCorrectStatementAndValues("WHERE COALESCE('a', ?, ?) = ?", [1, 2, 1]); + } + public function testResetWhere() { $this->query->where('c1 = x'); From 90317a2d5ad9c74c9be596e19fc7ae7ff9c4df66 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 6 Jul 2020 16:13:36 +0200 Subject: [PATCH 3/3] Refine docs for the supported where styles --- src/WhereInterface.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WhereInterface.php b/src/WhereInterface.php index 48eebfd..8a0a9c4 100644 --- a/src/WhereInterface.php +++ b/src/WhereInterface.php @@ -18,8 +18,8 @@ public function getWhere(); * Add a WHERE part of the query * * This method lets you specify the WHERE part of the query using one of the two following supported formats: - * * String format, e.g. 'id = 1' - * * Array format, e.g. ['id = ?' => 1, ...] + * * String format, e.g. 'id = 1', i.e. `where(string $condition [, mixed ...$args])` + * * Array format, e.g. ['id = ?' => 1, ...], i.e. `where(array $condition [, string $operator])` * * This method does NOT quote the columns you specify for the WHERE. * If you allow user input here, you must protected yourself against SQL injection using @@ -31,9 +31,9 @@ public function getWhere(); * the specified WHERE part using the AND operator. * * @param string|ExpressionInterface|Select|array $condition The WHERE condition - * @param mixed $args When condition is a string, this allows to pass parameter values for eventual placeholders - * When condition is an array, the only allowed parameter is the operator that should be used - * to combine those conditions. This operator defaults to Sql::ALL (AND) + * @param mixed $args If condition is a string, parameter values for placeholders in the condition can be passed. + * If condition is an array, the only argument that is allowed is the operator to use to combine + * these conditions. By default, this operator is {@link Sql::ALL} (AND) * * @return $this */