From 8038c3d7eeb8d1cdbec8924bd0b4b41c3234f4d8 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Mon, 4 Jun 2018 21:08:07 +0200 Subject: [PATCH] Add whereJsonContains() to SQL Server --- src/Illuminate/Database/Query/Builder.php | 2 +- .../Database/Query/Grammars/Grammar.php | 11 ++++++ .../Query/Grammars/SqlServerGrammar.php | 27 +++++++++++++ tests/Database/DatabaseQueryBuilderTest.php | 39 +++++++++++++++---- 4 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 6e1f863e4643..ea83b59c6f56 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1449,7 +1449,7 @@ public function whereJsonContains($column, $value, $boolean = 'and', $not = fals $this->wheres[] = compact('type', 'column', 'value', 'boolean', 'not'); if (! $value instanceof Expression) { - $this->addBinding(json_encode($value)); + $this->addBinding($this->grammar->prepareBindingForJsonContains($value)); } return $this; diff --git a/src/Illuminate/Database/Query/Grammars/Grammar.php b/src/Illuminate/Database/Query/Grammars/Grammar.php index 134fec69b8b6..63792122ffb0 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -523,6 +523,17 @@ protected function compileJsonContains($column, $value) throw new RuntimeException('This database engine does not support JSON contains operations.'); } + /** + * Prepare the binding for a "JSON contains" statement. + * + * @param mixed $binding + * @return string + */ + public function prepareBindingForJsonContains($binding) + { + return json_encode($binding); + } + /** * Compile the "group by" portions of the query. * diff --git a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php index dc1364e4cfe4..977a3354e635 100755 --- a/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php @@ -104,6 +104,33 @@ protected function whereDate(Builder $query, $where) return 'cast('.$this->wrap($where['column']).' as date) '.$where['operator'].' '.$value; } + /** + * Compile a "JSON contains" statement into SQL. + * + * @param string $column + * @param string $value + * @return string + */ + protected function compileJsonContains($column, $value) + { + $from = $column[0] == '[' ? + 'openjson('.$column.')' : + substr_replace($column, 'openjson', 0, strlen('json_value')); + + return $value.' in (select [value] from '.$from.')'; + } + + /** + * Prepare the binding for a "JSON contains" statement. + * + * @param mixed $binding + * @return string + */ + public function prepareBindingForJsonContains($binding) + { + return is_bool($binding) ? json_encode($binding) : $binding; + } + /** * Create a full ANSI offset clause for the query. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index db69c3a9ffbd..b84626c90025 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -2634,6 +2634,11 @@ public function testWhereRowValuesArityMismatch() public function testWhereJsonContainsMySql() { + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->whereJsonContains('options', ['en']); + $this->assertEquals('select * from `users` where json_contains(`options`, ?)', $builder->toSql()); + $this->assertEquals(['["en"]'], $builder->getBindings()); + $builder = $this->getMySqlBuilder(); $builder->select('*')->from('users')->whereJsonContains('options->languages', ['en']); $this->assertEquals('select * from `users` where json_contains(`options`->\'$."languages"\', ?)', $builder->toSql()); @@ -2647,6 +2652,11 @@ public function testWhereJsonContainsMySql() public function testWhereJsonContainsPostgres() { + $builder = $this->getPostgresBuilder(); + $builder->select('*')->from('users')->whereJsonContains('options', ['en']); + $this->assertEquals('select * from "users" where ("options")::jsonb @> ?', $builder->toSql()); + $this->assertEquals(['["en"]'], $builder->getBindings()); + $builder = $this->getPostgresBuilder(); $builder->select('*')->from('users')->whereJsonContains('options->languages', ['en']); $this->assertEquals('select * from "users" where ("options"->\'languages\')::jsonb @> ?', $builder->toSql()); @@ -2667,13 +2677,22 @@ public function testWhereJsonContainsSqlite() $builder->select('*')->from('users')->whereJsonContains('options->languages', ['en'])->toSql(); } - /** - * @expectedException \RuntimeException - */ public function testWhereJsonContainsSqlServer() { $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('users')->whereJsonContains('options->languages', ['en'])->toSql(); + $builder->select('*')->from('users')->whereJsonContains('options', true); + $this->assertEquals('select * from [users] where ? in (select [value] from openjson([options]))', $builder->toSql()); + $this->assertEquals(['true'], $builder->getBindings()); + + $builder = $this->getSqlServerBuilder(); + $builder->select('*')->from('users')->whereJsonContains('options->languages', 'en'); + $this->assertEquals('select * from [users] where ? in (select [value] from openjson([options], \'$."languages"\'))', $builder->toSql()); + $this->assertEquals(['en'], $builder->getBindings()); + + $builder = $this->getSqlServerBuilder(); + $builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonContains('options->languages', new Raw("'en'")); + $this->assertEquals('select * from [users] where [id] = ? or \'en\' in (select [value] from openjson([options], \'$."languages"\'))', $builder->toSql()); + $this->assertEquals([1], $builder->getBindings()); } public function testWhereJsonDoesntContainMySql() @@ -2711,13 +2730,17 @@ public function testWhereJsonDoesntContainSqlite() $builder->select('*')->from('users')->whereJsonDoesntContain('options->languages', ['en'])->toSql(); } - /** - * @expectedException \RuntimeException - */ public function testWhereJsonDoesntContainSqlServer() { $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('users')->whereJsonDoesntContain('options->languages', ['en'])->toSql(); + $builder->select('*')->from('users')->whereJsonDoesntContain('options->languages', 'en'); + $this->assertEquals('select * from [users] where not ? in (select [value] from openjson([options], \'$."languages"\'))', $builder->toSql()); + $this->assertEquals(['en'], $builder->getBindings()); + + $builder = $this->getSqlServerBuilder(); + $builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonDoesntContain('options->languages', new Raw("'en'")); + $this->assertEquals('select * from [users] where [id] = ? or not \'en\' in (select [value] from openjson([options], \'$."languages"\'))', $builder->toSql()); + $this->assertEquals([1], $builder->getBindings()); } public function testFromSub()