From dedec48260d21989115ee3dcefaed8b086679886 Mon Sep 17 00:00:00 2001 From: Markus Fischer Date: Fri, 15 Dec 2017 23:38:27 +0100 Subject: [PATCH] Add builder/grammar support for row values in where condition --- src/Illuminate/Database/Query/Builder.php | 41 +++++++++++++++++++ .../Database/Query/Grammars/Grammar.php | 15 +++++++ tests/Database/DatabaseQueryBuilderTest.php | 20 +++++++++ 3 files changed, 76 insertions(+) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 5b8f0574e8d2..9458f231f511 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1237,6 +1237,47 @@ public function addWhereExistsQuery(self $query, $boolean = 'and', $not = false) return $this; } + /** + * Adds a where condition using row values. + * + * This is mostly used with "keyset pagination" aka "seek method". + * + * @param array $columns + * @param string $operator + * @param array $values + * @param string $boolean + * @return $this + */ + public function whereRowValues($columns, $operator, $values, $boolean = 'and') + { + if (count($columns) != count($values)) { + throw new InvalidArgumentException('The number of columns must match the number of values'); + } + + $type = 'RowValues'; + + $this->wheres[] = compact('type', 'columns', 'operator', 'values', 'boolean'); + + $this->addBinding($values); + + return $this; + } + + /** + * Adds a or where condition using row values. + * + * This is mostly used with "keyset pagination" aka "seek method". + * + * @param array $columns + * @param string $operator + * @param array $values + * @return $this + */ + public function orWhereRowValues($columns, $operator, $values) + { + return $this->whereRowValues($columns, $operator, $values, 'or'); + } + /** * Handles dynamic "where" clauses to the query. * diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index 7e2ce556325c..fd0c36b0a31b 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -473,6 +473,21 @@ protected function whereNotExists(Builder $query, $where) return 'not exists ('.$this->compileSelect($where['query']).')'; } + /** + * Compile a where row values condition. + * + * @param \Illuminate\Database\Query\Builder $query + * @param array $where + * @return string + */ + protected function whereRowValues(Builder $query, $where) + { + $columns = join(', ', $where['columns']); + $values = $this->parameterize($where['values']); + + return '('.$columns.') '.$where['operator'].' ('.$values.')'; + } + /** * Compile the "group by" portions of the query. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 1def5cd7860b..3c70b5033297 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -2363,6 +2363,26 @@ public function testPaginateWhenNoResults() ]), $result); } + public function testWhereRowValues() + { + $builder = $this->getBuilder(); + $builder->select('*')->from('orders')->whereRowValues(['last_update', 'order_number'], '<', [1, 2]); + $this->assertEquals('select * from "orders" where (last_update, order_number) < (?, ?)', $builder->toSql()); + + $builder = $this->getBuilder(); + $builder->select('*')->from('orders')->where('company_id', 1)->orWhereRowValues(['last_update', 'order_number'], '<', [1, 2]); + $this->assertEquals('select * from "orders" where "company_id" = ? or (last_update, order_number) < (?, ?)', $builder->toSql()); + } + + public function testWhereRowValuesArityMismatch() + { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('The number of columns must match the number of values'); + + $builder = $this->getBuilder(); + $builder->select('*')->from('orders')->whereRowValues(['last_update'], '<', [1, 2]); + } + protected function getBuilder() { $grammar = new \Illuminate\Database\Query\Grammars\Grammar;