Skip to content

Commit

Permalink
[11.x] Add ->whereJsonOverlaps() for mysql (#51288)
Browse files Browse the repository at this point in the history
* add mysql json_overlaps

* update error message

* update comments

* formatting

---------

Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
parkourben99 and taylorotwell authored May 6, 2024
1 parent 279c444 commit 2a32afb
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/Illuminate/Database/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
31 changes: 31 additions & 0 deletions src/Illuminate/Database/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
14 changes: 14 additions & 0 deletions src/Illuminate/Database/Query/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
31 changes: 31 additions & 0 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 2a32afb

Please sign in to comment.