Skip to content

Commit

Permalink
feat: like() supports RawSql
Browse files Browse the repository at this point in the history
  • Loading branch information
kenjis committed Mar 31, 2022
1 parent 3397f41 commit a1eabc7
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 14 deletions.
68 changes: 54 additions & 14 deletions system/Database/BaseBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,7 @@ protected function _whereIn(?string $key = null, $values = null, bool $not = fal
* Generates a %LIKE% portion of the query.
* Separates multiple calls with 'AND'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -945,7 +945,7 @@ public function like($field, string $match = '', string $side = 'both', ?bool $e
* Generates a NOT LIKE portion of the query.
* Separates multiple calls with 'AND'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -958,7 +958,7 @@ public function notLike($field, string $match = '', string $side = 'both', ?bool
* Generates a %LIKE% portion of the query.
* Separates multiple calls with 'OR'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -971,7 +971,7 @@ public function orLike($field, string $match = '', string $side = 'both', ?bool
* Generates a NOT LIKE portion of the query.
* Separates multiple calls with 'OR'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -984,7 +984,7 @@ public function orNotLike($field, string $match = '', string $side = 'both', ?bo
* Generates a %LIKE% portion of the query.
* Separates multiple calls with 'AND'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -997,7 +997,7 @@ public function havingLike($field, string $match = '', string $side = 'both', ?b
* Generates a NOT LIKE portion of the query.
* Separates multiple calls with 'AND'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -1010,7 +1010,7 @@ public function notHavingLike($field, string $match = '', string $side = 'both',
* Generates a %LIKE% portion of the query.
* Separates multiple calls with 'OR'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -1023,7 +1023,7 @@ public function orHavingLike($field, string $match = '', string $side = 'both',
* Generates a NOT LIKE portion of the query.
* Separates multiple calls with 'OR'.
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
Expand All @@ -1042,20 +1042,54 @@ public function orNotHavingLike($field, string $match = '', string $side = 'both
* @used-by notHavingLike()
* @used-by orNotHavingLike()
*
* @param mixed $field
* @param array|RawSql|string $field
*
* @return $this
*/
protected function _like($field, string $match = '', string $type = 'AND ', string $side = 'both', string $not = '', ?bool $escape = null, bool $insensitiveSearch = false, string $clause = 'QBWhere')
{
if (! is_array($field)) {
$field = [$field => $match];
}

$escape = is_bool($escape) ? $escape : $this->db->protectIdentifiers;
$side = strtolower($side);

foreach ($field as $k => $v) {
if ($field instanceof RawSql) {
$k = (string) $field;
$v = $match;
$insensitiveSearch = false;

$prefix = empty($this->{$clause}) ? $this->groupGetType('') : $this->groupGetType($type);

if ($side === 'none') {
$bind = $this->setBind($field->getBindingKey(), $v, $escape);
} elseif ($side === 'before') {
$bind = $this->setBind($field->getBindingKey(), "%{$v}", $escape);
} elseif ($side === 'after') {
$bind = $this->setBind($field->getBindingKey(), "{$v}%", $escape);
} else {
$bind = $this->setBind($field->getBindingKey(), "%{$v}%", $escape);
}

$likeStatement = $this->_like_statement($prefix, $k, $not, $bind, $insensitiveSearch);

// some platforms require an escape sequence definition for LIKE wildcards
if ($escape === true && $this->db->likeEscapeStr !== '') {
$likeStatement .= sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar);
}

$this->{$clause}[] = [
'condition' => $field->with($likeStatement),
'escape' => $escape,
];

return $this;
}

if (! is_array($field)) {
$keyValue = [$field => $match];
} else {
$keyValue = $field;
}

foreach ($keyValue as $k => $v) {
if ($insensitiveSearch === true) {
$v = strtolower($v);
}
Expand Down Expand Up @@ -2430,6 +2464,12 @@ protected function compileWhereHaving(string $qbKey): string
continue;
}

if ($qbkey['condition'] instanceof RawSql) {
$qbkey = $qbkey['condition'];

continue;
}

if ($qbkey['escape'] === false) {
$qbkey = $qbkey['condition'];

Expand Down
24 changes: 24 additions & 0 deletions tests/system/Database/Builder/LikeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace CodeIgniter\Database\Builder;

use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Database\RawSql;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockConnection;

Expand Down Expand Up @@ -47,6 +48,29 @@ public function testSimpleLike()
$this->assertSame($expectedBinds, $builder->getBinds());
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/3970
*/
public function testLikeWithRawSql()
{
$builder = new BaseBuilder('users', $this->db);

$sql = "concat(users.name, ' ', IF(users.surname IS NULL or users.surname = '', '', users.surname))";
$rawSql = new RawSql($sql);
$builder->like($rawSql, 'value', 'both');

$expectedSQL = "SELECT * FROM \"users\" WHERE {$sql} LIKE '%value%' ESCAPE '!' ";
$expectedBinds = [
$rawSql->getBindingKey() => [
'%value%',
true,
],
];

$this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect()));
$this->assertSame($expectedBinds, $builder->getBinds());
}

public function testLikeNoSide()
{
$builder = new BaseBuilder('job', $this->db);
Expand Down

0 comments on commit a1eabc7

Please sign in to comment.