diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 72fc88ccd448..25d43ccc8cef 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1921,6 +1921,65 @@ public function orWhereJsonDoesntContain($column, $value) return $this->whereJsonDoesntContain($column, $value, 'or'); } + /** + * Add a "where JSON overlaps" clause to the query. + * + * @param string $column + * @param mixed $value + * @param string $boolean + * @param bool $not + * @return $this + */ + public function whereJsonOverlaps($column, $value, $boolean = 'and', $not = false) + { + $type = 'JsonOverlaps'; + + $this->wheres[] = compact('type', 'column', 'value', 'boolean', 'not'); + + if (! $value instanceof ExpressionContract) { + $this->addBinding($this->grammar->prepareBindingForJsonContains($value)); + } + + return $this; + } + + /** + * Add an "or where JSON overlaps" clause to the query. + * + * @param string $column + * @param mixed $value + * @return $this + */ + public function orWhereJsonOverlaps($column, $value) + { + return $this->whereJsonOverlaps($column, $value, 'or'); + } + + /** + * Add a "where JSON not overlap" clause to the query. + * + * @param string $column + * @param mixed $value + * @param string $boolean + * @return $this + */ + public function whereJsonDoesntOverlap($column, $value, $boolean = 'and') + { + return $this->whereJsonOverlaps($column, $value, $boolean, true); + } + + /** + * Add an "or where JSON not overlap" clause to the query. + * + * @param string $column + * @param mixed $value + * @return $this + */ + public function orWhereJsonDoesntOverlap($column, $value) + { + return $this->whereJsonDoesntOverlap($column, $value, 'or'); + } + /** * Add a clause that determines if a JSON path exists to the query. * diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index f2ce92c28ff3..42c9102b4c62 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -643,6 +643,37 @@ protected function compileJsonContains($column, $value) throw new RuntimeException('This database engine does not support JSON contains operations.'); } + /** + * Compile a "where JSON overlaps" clause. + * + * @param \Illuminate\Database\Query\Builder $query + * @param array $where + * @return string + */ + protected function whereJsonOverlaps(Builder $query, $where) + { + $not = $where['not'] ? 'not ' : ''; + + return $not.$this->compileJsonOverlaps( + $where['column'], + $this->parameter($where['value']) + ); + } + + /** + * Compile a "JSON overlaps" statement into SQL. + * + * @param string $column + * @param string $value + * @return string + * + * @throws \RuntimeException + */ + protected function compileJsonOverlaps($column, $value) + { + throw new RuntimeException('This database engine does not support JSON overlaps operations.'); + } + /** * Prepare the binding for a "JSON contains" statement. * diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index 65ab6991cc9a..8c1521f60d31 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -210,6 +210,20 @@ protected function compileJsonContains($column, $value) return 'json_contains('.$field.', '.$value.$path.')'; } + /** + * Compile a "JSON overlaps" statement into SQL. + * + * @param string $column + * @param string $value + * @return string + */ + protected function compileJsonOverlaps($column, $value) + { + [$field, $path] = $this->wrapJsonFieldAndPath($column); + + return 'json_overlaps('.$field.', '.$value.$path.')'; + } + /** * Compile a "JSON contains key" statement into SQL. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 0d44b6b6b15a..a045f41548c6 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -5675,6 +5675,24 @@ public function testWhereJsonContainsMySql() $this->assertEquals([1], $builder->getBindings()); } + public function testWhereJsonOverlapsMySql() + { + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->whereJsonOverlaps('options', ['en', 'fr']); + $this->assertSame('select * from `users` where json_overlaps(`options`, ?)', $builder->toSql()); + $this->assertEquals(['["en","fr"]'], $builder->getBindings()); + + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->whereJsonOverlaps('users.options->languages', ['en', 'fr']); + $this->assertSame('select * from `users` where json_overlaps(`users`.`options`, ?, \'$."languages"\')', $builder->toSql()); + $this->assertEquals(['["en","fr"]'], $builder->getBindings()); + + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonOverlaps('options->languages', new Raw("'[\"en\", \"fr\"]'")); + $this->assertSame('select * from `users` where `id` = ? or json_overlaps(`options`, \'["en", "fr"]\', \'$."languages"\')', $builder->toSql()); + $this->assertEquals([1], $builder->getBindings()); + } + public function testWhereJsonContainsPostgres() { $builder = $this->getPostgresBuilder(); @@ -5737,6 +5755,19 @@ public function testWhereJsonDoesntContainMySql() $this->assertEquals([1], $builder->getBindings()); } + public function testWhereJsonDoesntOverlapMySql() + { + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->whereJsonDoesntOverlap('options->languages', ['en', 'fr']); + $this->assertSame('select * from `users` where not json_overlaps(`options`, ?, \'$."languages"\')', $builder->toSql()); + $this->assertEquals(['["en","fr"]'], $builder->getBindings()); + + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonDoesntOverlap('options->languages', new Raw("'[\"en\", \"fr\"]'")); + $this->assertSame('select * from `users` where `id` = ? or not json_overlaps(`options`, \'["en", "fr"]\', \'$."languages"\')', $builder->toSql()); + $this->assertEquals([1], $builder->getBindings()); + } + public function testWhereJsonDoesntContainPostgres() { $builder = $this->getPostgresBuilder();