Skip to content

Commit

Permalink
Add whereLike clause to query builder (#52147)
Browse files Browse the repository at this point in the history
* Add database grammer for whereLike method

* Add whereLike to database query builder

* Add whereLike clause to MySQL grammar

* Add whereLike clause to Postgres grammar

* Add whereLike clause to SQLite grammar

* Add whereLike query builder test for sql server

* Run whereLike integration test on all databases

* Skip sqlsrv where like integration test

* Fic styling

* formatting

---------

Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
einar-hansen and taylorotwell authored Jul 18, 2024
1 parent 92a78ce commit 95107a0
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 0 deletions.
65 changes: 65 additions & 0 deletions src/Illuminate/Database/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,71 @@ public function orWhereRaw($sql, $bindings = [])
return $this->whereRaw($sql, $bindings, 'or');
}

/**
* Add a "where like" clause to the query.
*
* @param string $column
* @param string $value
* @param bool $caseSensitive
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereLike($column, $value, $caseSensitive = false, $boolean = 'and', $not = false)
{
$type = 'Like';

$this->wheres[] = compact('type', 'column', 'value', 'caseSensitive', 'boolean', 'not');

if (method_exists($this->grammar, 'prepareWhereLikeBinding')) {
$value = $this->grammar->prepareWhereLikeBinding($value, $caseSensitive);
}

$this->addBinding($value);

return $this;
}

/**
* Add an "or where like" clause to the query.
*
* @param string $column
* @param string $value
* @param bool $caseSensitive
* @return $this
*/
public function orWhereLike($column, $value, $caseSensitive = false)
{
return $this->whereLike($column, $value, $caseSensitive, 'or', false);
}

/**
* Add a "where not like" clause to the query.
*
* @param string $column
* @param string $value
* @param bool $caseSensitive
* @param string $boolean
* @return $this
*/
public function whereNotLike($column, $value, $caseSensitive = false, $boolean = 'and')
{
return $this->whereLike($column, $value, $caseSensitive, $boolean, true);
}

/**
* Add an "or where not like" clause to the query.
*
* @param string $columns
* @param string $value
* @param bool $caseSensitive
* @return $this
*/
public function orWhereNotLike($column, $value, $caseSensitive = false)
{
return $this->whereNotLike($column, $value, $caseSensitive, 'or');
}

/**
* Add a "where in" clause to the query.
*
Expand Down
18 changes: 18 additions & 0 deletions src/Illuminate/Database/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,24 @@ protected function whereBitwise(Builder $query, $where)
return $this->whereBasic($query, $where);
}

/**
* Compile a "where like" clause.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $where
* @return string
*/
protected function whereLike(Builder $query, $where)
{
if ($where['caseSensitive']) {
throw new RuntimeException('This database engine does not support case sensitive like operations.');
}

$where['operator'] = $where['not'] ? 'not like' : 'like';

return $this->whereBasic($query, $where);
}

/**
* Compile a "where in" clause.
*
Expand Down
16 changes: 16 additions & 0 deletions src/Illuminate/Database/Query/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ class MySqlGrammar extends Grammar
*/
protected $operators = ['sounds like'];

/**
* Compile a "where like" clause.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $where
* @return string
*/
protected function whereLike(Builder $query, $where)
{
$where['operator'] = $where['not'] ? 'not ' : '';

$where['operator'] .= $where['caseSensitive'] ? 'like binary' : 'like';

return $this->whereBasic($query, $where);
}

/**
* Add a "where null" clause to the query.
*
Expand Down
16 changes: 16 additions & 0 deletions src/Illuminate/Database/Query/Grammars/PostgresGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ protected function whereBitwise(Builder $query, $where)
return '('.$this->wrap($where['column']).' '.$operator.' '.$value.')::bool';
}

/**
* Compile a "where like" clause.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $where
* @return string
*/
protected function whereLike(Builder $query, $where)
{
$where['operator'] = $where['not'] ? 'not ' : '';

$where['operator'] .= $where['caseSensitive'] ? 'like' : 'ilike';

return $this->whereBasic($query, $where);
}

/**
* Compile a "where date" clause.
*
Expand Down
33 changes: 33 additions & 0 deletions src/Illuminate/Database/Query/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,39 @@ protected function wrapUnion($sql)
return 'select * from ('.$sql.')';
}

/**
* Compile a "where like" clause.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $where
* @return string
*/
protected function whereLike(Builder $query, $where)
{
if ($where['caseSensitive'] == false) {
return parent::whereLike($query, $where);
}
$where['operator'] = $where['not'] ? 'not glob' : 'glob';

return $this->whereBasic($query, $where);
}

/**
* Convert a LIKE pattern to a GLOB pattern using simple string replacement.
*
* @param string $value
* @param bool $caseSensitive
* @return string
*/
public function prepareWhereLikeBinding($value, $caseSensitive)
{
return $caseSensitive === false ? $value : str_replace(
['*', '?', '%', '_'],
['[*]', '[?]', '*', '?'],
$value
);
}

/**
* Compile a "where date" clause.
*
Expand Down
122 changes: 122 additions & 0 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,128 @@ public function testWhereLikePostgres()
$this->assertEquals([0 => '1'], $builder->getBindings());
}

public function testWhereLikeClausePostgres()
{
$builder = $this->getPostgresBuilder();
$builder->select('*')->from('users')->whereLike('id', '1');
$this->assertSame('select * from "users" where "id"::text ilike ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getPostgresBuilder();
$builder->select('*')->from('users')->whereLike('id', '1', false);
$this->assertSame('select * from "users" where "id"::text ilike ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getPostgresBuilder();
$builder->select('*')->from('users')->whereLike('id', '1', true);
$this->assertSame('select * from "users" where "id"::text like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getPostgresBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1');
$this->assertSame('select * from "users" where "id"::text not ilike ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getPostgresBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1', false);
$this->assertSame('select * from "users" where "id"::text not ilike ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getPostgresBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1', true);
$this->assertSame('select * from "users" where "id"::text not like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());
}

public function testWhereLikeClauseMysql()
{
$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereLike('id', '1');
$this->assertSame('select * from `users` where `id` like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereLike('id', '1', false);
$this->assertSame('select * from `users` where `id` like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereLike('id', '1', true);
$this->assertSame('select * from `users` where `id` like binary ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1');
$this->assertSame('select * from `users` where `id` not like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1', false);
$this->assertSame('select * from `users` where `id` not like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getMySqlBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1', true);
$this->assertSame('select * from `users` where `id` not like binary ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());
}

public function testWhereLikeClauseSqlite()
{
$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereLike('id', '1');
$this->assertSame('select * from "users" where "id" like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereLike('id', '1', true);
$this->assertSame('select * from "users" where "id" glob ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereLike('description', 'Hell* _orld?%', true);
$this->assertSame('select * from "users" where "description" glob ?', $builder->toSql());
$this->assertEquals([0 => 'Hell[*] ?orld[?]*'], $builder->getBindings());

$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1');
$this->assertSame('select * from "users" where "id" not like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereNotLike('description', 'Hell* _orld?%', true);
$this->assertSame('select * from "users" where "description" not glob ?', $builder->toSql());
$this->assertEquals([0 => 'Hell[*] ?orld[?]*'], $builder->getBindings());

$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereLike('name', 'John%', true)->whereNotLike('name', '%Doe%', true);
$this->assertSame('select * from "users" where "name" glob ? and "name" not glob ?', $builder->toSql());
$this->assertEquals([0 => 'John*', 1 => '*Doe*'], $builder->getBindings());

$builder = $this->getSQLiteBuilder();
$builder->select('*')->from('users')->whereLike('name', 'John%')->orWhereLike('name', 'Jane%', true);
$this->assertSame('select * from "users" where "name" like ? or "name" glob ?', $builder->toSql());
$this->assertEquals([0 => 'John%', 1 => 'Jane*'], $builder->getBindings());
}

public function testWhereLikeClauseSqlServer()
{
$builder = $this->getSqlServerBuilder();
$builder->select('*')->from('users')->whereLike('id', '1');
$this->assertSame('select * from [users] where [id] like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());

$builder = $this->getSqlServerBuilder();
$builder->select('*')->from('users')->whereLike('id', '1')->orWhereLike('id', '2');
$this->assertSame('select * from [users] where [id] like ? or [id] like ?', $builder->toSql());
$this->assertEquals([0 => '1', 1 => '2'], $builder->getBindings());

$builder = $this->getSqlServerBuilder();
$builder->select('*')->from('users')->whereNotLike('id', '1');
$this->assertSame('select * from [users] where [id] not like ?', $builder->toSql());
$this->assertEquals([0 => '1'], $builder->getBindings());
}

public function testWhereDateSqlite()
{
$builder = $this->getSQLiteBuilder();
Expand Down
Loading

0 comments on commit 95107a0

Please sign in to comment.