diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index cd1c75b52bdf..b6ace2aedd46 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1433,6 +1433,39 @@ public function orWhereRowValues($columns, $operator, $values) return $this->whereRowValues($columns, $operator, $values, 'or'); } + /** + * Add a "where JSON contains" clause to the query. + * + * @param string $column + * @param mixed $value + * @param string $boolean + * @return $this + */ + public function whereJsonContains($column, $value, $boolean = 'and') + { + $type = 'JsonContains'; + + $this->wheres[] = compact('type', 'column', 'value', 'boolean'); + + if (! $value instanceof Expression) { + $this->addBinding(json_encode($value)); + } + + return $this; + } + + /** + * Add a "or where JSON contains" clause to the query. + * + * @param string $column + * @param mixed $value + * @return $this + */ + public function orWhereJsonContains($column, $value) + { + return $this->whereJsonContains($column, $value, '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 b3742b0f6ad2..f15bfff4bcdc 100755 --- a/src/Illuminate/Database/Query/Grammars/Grammar.php +++ b/src/Illuminate/Database/Query/Grammars/Grammar.php @@ -2,6 +2,7 @@ namespace Illuminate\Database\Query\Grammars; +use RuntimeException; use Illuminate\Support\Arr; use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\JoinClause; @@ -489,6 +490,35 @@ protected function whereRowValues(Builder $query, $where) return '('.implode(', ', $where['columns']).') '.$where['operator'].' ('.$values.')'; } + /** + * Compile a "where JSON contains" clause. + * + * @param \Illuminate\Database\Query\Builder $query + * @param array $where + * @return string + */ + protected function whereJsonContains(Builder $query, $where) + { + $column = $this->wrap($where['column']); + + $value = $this->parameter($where['value']); + + return $this->compileJsonContains($column, $value); + } + + /** + * Compile a "JSON contains" statement into SQL. + * + * @param string $column + * @param string $value + * @return string + * @throws \RuntimeException + */ + protected function compileJsonContains($column, $value) + { + throw new RuntimeException('The database engine in use is not yet suppported.'); + } + /** * Compile the "group by" portions of the query. * diff --git a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php index e4333896198e..c577bbc04acd 100755 --- a/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php @@ -52,6 +52,18 @@ public function compileSelect(Builder $query) return $sql; } + /** + * Compile a "JSON contains" statement into SQL. + * + * @param string $column + * @param string $value + * @return string + */ + protected function compileJsonContains($column, $value) + { + return 'json_contains('.$column.', '.$value.')'; + } + /** * Compile a single union statement. * diff --git a/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php index b72d2299e95c..d7c06f5478ba 100755 --- a/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Query/Grammars/PostgresGrammar.php @@ -64,6 +64,20 @@ protected function dateBasedWhere($type, Builder $query, $where) return 'extract('.$type.' from '.$this->wrap($where['column']).') '.$where['operator'].' '.$value; } + /** + * Compile a "JSON contains" statement into SQL. + * + * @param string $column + * @param string $value + * @return string + */ + protected function compileJsonContains($column, $value) + { + $column = str_replace('->>', '->', $column); + + return '('.$column.')::jsonb @> '.$value; + } + /** * Compile the lock into SQL. * diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php index 411bb356234a..dc683c04dcb9 100755 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ b/tests/Database/DatabaseQueryBuilderTest.php @@ -2612,6 +2612,50 @@ public function testWhereRowValuesArityMismatch() $builder->select('*')->from('orders')->whereRowValues(['last_update'], '<', [1, 2]); } + public function testWhereJsonContainsMySql() + { + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->whereJsonContains('options->languages', ['en']); + $this->assertEquals('select * from `users` where json_contains(`options`->\'$."languages"\', ?)', $builder->toSql()); + $this->assertEquals(['["en"]'], $builder->getBindings()); + + $builder = $this->getMySqlBuilder(); + $builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonContains('options->languages', new Raw("'[\"en\"]'")); + $this->assertEquals('select * from `users` where `id` = ? or json_contains(`options`->\'$."languages"\', \'["en"]\')', $builder->toSql()); + $this->assertEquals([1], $builder->getBindings()); + } + + public function testWhereJsonContainsPostgres() + { + $builder = $this->getPostgresBuilder(); + $builder->select('*')->from('users')->whereJsonContains('options->languages', ['en']); + $this->assertEquals('select * from "users" where ("options"->\'languages\')::jsonb @> ?', $builder->toSql()); + $this->assertEquals(['["en"]'], $builder->getBindings()); + + $builder = $this->getPostgresBuilder(); + $builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonContains('options->languages', new Raw("'[\"en\"]'")); + $this->assertEquals('select * from "users" where "id" = ? or ("options"->\'languages\')::jsonb @> \'["en"]\'', $builder->toSql()); + $this->assertEquals([1], $builder->getBindings()); + } + + /** + * @expectedException \RuntimeException + */ + public function testWhereJsonContainsSqlite() + { + $builder = $this->getSQLiteBuilder(); + $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(); + } + public function testFromSub() { $builder = $this->getBuilder();