Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/Illuminate/Database/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -966,15 +966,16 @@ protected function wrapJsonSelector($value)
* Split the given JSON selector into the field and the optional path and wrap them separately.
*
* @param string $column
* @param string $delimiter
* @return array
*/
protected function wrapJsonFieldAndPath($column)
protected function wrapJsonFieldAndPath($column, $delimiter = '->')
{
$parts = explode('->', $column, 2);
$parts = explode($delimiter, $column, 2);

$field = $this->wrap($parts[0]);

$path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1]) : '';
$path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1], $delimiter) : '';

return [$field, $path];
}
Expand All @@ -983,11 +984,12 @@ protected function wrapJsonFieldAndPath($column)
* Wrap the given JSON path.
*
* @param string $value
* @param string $delimiter
* @return string
*/
protected function wrapJsonPath($value)
protected function wrapJsonPath($value, $delimiter = '->')
{
return '\'$."'.str_replace('->', '"."', $value).'"\'';
return '\'$."'.str_replace($delimiter, '"."', $value).'"\'';
}

/**
Expand Down
20 changes: 11 additions & 9 deletions src/Illuminate/Database/Query/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ public function compileSelect(Builder $query)
*/
protected function compileJsonContains($column, $value)
{
return 'json_contains('.$this->wrap($column).', '.$value.')';
list($field, $path) = $this->wrapJsonFieldAndPath($column);

return 'json_contains('.$field.', '.$value.$path.')';
}

/**
Expand Down Expand Up @@ -317,16 +319,16 @@ protected function wrapValue($value)
*/
protected function wrapJsonSelector($value)
{
$delimiter = Str::contains($value, '->>')
? '->>'
: '->';
$delimiter = Str::contains($value, '->>') ? '->>' : '->';

$path = explode($delimiter, $value);
list($field, $path) = $this->wrapJsonFieldAndPath($value, $delimiter);

$field = $this->wrapSegments(explode('.', array_shift($path)));
$selector = 'json_extract('.$field.$path.')';

if ($delimiter === '->>') {
$selector = 'json_unquote('.$selector.')';
}

return sprintf('%s'.$delimiter.'\'$.%s\'', $field, collect($path)->map(function ($part) {
return '"'.$part.'"';
})->implode('.'));
return $selector;
}
}
6 changes: 1 addition & 5 deletions src/Illuminate/Database/Query/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,7 @@ public function compileTruncate(Builder $query)
*/
protected function wrapJsonSelector($value)
{
$parts = explode('->', $value, 2);

$field = $this->wrap($parts[0]);

$path = count($parts) > 1 ? ', '.$this->wrapJsonPath($parts[1]) : '';
list($field, $path) = $this->wrapJsonFieldAndPath($value);

$selector = 'json_extract('.$field.$path.')';

Expand Down
6 changes: 2 additions & 4 deletions src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -476,11 +476,9 @@ protected function wrapValue($value)
*/
protected function wrapJsonSelector($value)
{
$parts = explode('->', $value, 2);
list($field, $path) = $this->wrapJsonFieldAndPath($value);

$field = $this->wrapSegments(explode('.', array_shift($parts)));

return 'json_value('.$field.', '.$this->wrapJsonPath($parts[0]).')';
return 'json_value('.$field.$path.')';
}

/**
Expand Down
32 changes: 19 additions & 13 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2003,7 +2003,7 @@ public function testMySqlWrappingJsonWithString()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->sku', '=', 'foo-bar');
$this->assertEquals('select * from `users` where `items`->\'$."sku"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."sku"\') = ?', $builder->toSql());
$this->assertCount(1, $builder->getRawBindings()['where']);
$this->assertEquals('foo-bar', $builder->getRawBindings()['where'][0]);
}
Expand All @@ -2012,35 +2012,41 @@ public function testMySqlWrappingJsonWithInteger()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->price', '=', 1);
$this->assertEquals('select * from `users` where `items`->\'$."price"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."price"\') = ?', $builder->toSql());
}

public function testMySqlWrappingJsonWithDouble()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->price', '=', 1.5);
$this->assertEquals('select * from `users` where `items`->\'$."price"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."price"\') = ?', $builder->toSql());
}

public function testMySqlWrappingJsonWithBoolean()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->available', '=', true);
$this->assertEquals('select * from `users` where `items`->\'$."available"\' = true', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."available"\') = true', $builder->toSql());
}

public function testMySqlWrappingJsonWithBooleanAndIntegerThatLooksLikeOne()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->available', '=', true)->where('items->active', '=', false)->where('items->number_available', '=', 0);
$this->assertEquals('select * from `users` where `items`->\'$."available"\' = true and `items`->\'$."active"\' = false and `items`->\'$."number_available"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."available"\') = true and json_extract(`items`, \'$."active"\') = false and json_extract(`items`, \'$."number_available"\') = ?', $builder->toSql());
}

public function testMySqlWrappingJsonWithoutQuote()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->>sku', '=', 'foo-bar');
$this->assertEquals('select * from `users` where `items`->>\'$."sku"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_unquote(json_extract(`items`, \'$."sku"\')) = ?', $builder->toSql());
$this->assertCount(1, $builder->getRawBindings()['where']);
$this->assertEquals('foo-bar', $builder->getRawBindings()['where'][0]);

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->>price->>in_usd', '=', 'foo-bar');
$this->assertEquals('select * from `users` where json_unquote(json_extract(`items`, \'$."price"."in_usd"\')) = ?', $builder->toSql());
$this->assertCount(1, $builder->getRawBindings()['where']);
$this->assertEquals('foo-bar', $builder->getRawBindings()['where'][0]);
}
Expand All @@ -2053,15 +2059,15 @@ public function testMySqlWrappingJson()

$builder = $this->getMySqlBuilder();
$builder->select('items->price')->from('users')->where('users.items->price', '=', 1)->orderBy('items->price');
$this->assertEquals('select `items`->\'$."price"\' from `users` where `users`.`items`->\'$."price"\' = ? order by `items`->\'$."price"\' asc', $builder->toSql());
$this->assertEquals('select json_extract(`items`, \'$."price"\') from `users` where json_extract(`users`.`items`, \'$."price"\') = ? order by json_extract(`items`, \'$."price"\') asc', $builder->toSql());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->price->in_usd', '=', 1);
$this->assertEquals('select * from `users` where `items`->\'$."price"."in_usd"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."price"."in_usd"\') = ?', $builder->toSql());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('items->price->in_usd', '=', 1)->where('items->age', '=', 2);
$this->assertEquals('select * from `users` where `items`->\'$."price"."in_usd"\' = ? and `items`->\'$."age"\' = ?', $builder->toSql());
$this->assertEquals('select * from `users` where json_extract(`items`, \'$."price"."in_usd"\') = ? and json_extract(`items`, \'$."age"\') = ?', $builder->toSql());
}

public function testPostgresWrappingJson()
Expand Down Expand Up @@ -2689,12 +2695,12 @@ public function testWhereJsonContainsMySql()

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereJsonContains('users.options->languages', ['en']);
$this->assertEquals('select * from `users` where json_contains(`users`.`options`->\'$."languages"\', ?)', $builder->toSql());
$this->assertEquals('select * from `users` where json_contains(`users`.`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('select * from `users` where `id` = ? or json_contains(`options`, \'["en"]\', \'$."languages"\')', $builder->toSql());
$this->assertEquals([1], $builder->getBindings());
}

Expand Down Expand Up @@ -2747,12 +2753,12 @@ public function testWhereJsonDoesntContainMySql()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereJsonDoesntContain('options->languages', ['en']);
$this->assertEquals('select * from `users` where not json_contains(`options`->\'$."languages"\', ?)', $builder->toSql());
$this->assertEquals('select * from `users` where not json_contains(`options`, ?, \'$."languages"\')', $builder->toSql());
$this->assertEquals(['["en"]'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->where('id', '=', 1)->orWhereJsonDoesntContain('options->languages', new Raw("'[\"en\"]'"));
$this->assertEquals('select * from `users` where `id` = ? or not json_contains(`options`->\'$."languages"\', \'["en"]\')', $builder->toSql());
$this->assertEquals('select * from `users` where `id` = ? or not json_contains(`options`, \'["en"]\', \'$."languages"\')', $builder->toSql());
$this->assertEquals([1], $builder->getBindings());
}

Expand Down