Skip to content

Commit 8c05660

Browse files
committed
feat: like() supports RawSql
1 parent 04c68b4 commit 8c05660

2 files changed

Lines changed: 78 additions & 14 deletions

File tree

system/Database/BaseBuilder.php

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,7 @@ protected function _whereIn(?string $key = null, $values = null, bool $not = fal
932932
* Generates a %LIKE% portion of the query.
933933
* Separates multiple calls with 'AND'.
934934
*
935-
* @param mixed $field
935+
* @param array|RawSql|string $field
936936
*
937937
* @return $this
938938
*/
@@ -945,7 +945,7 @@ public function like($field, string $match = '', string $side = 'both', ?bool $e
945945
* Generates a NOT LIKE portion of the query.
946946
* Separates multiple calls with 'AND'.
947947
*
948-
* @param mixed $field
948+
* @param array|RawSql|string $field
949949
*
950950
* @return $this
951951
*/
@@ -958,7 +958,7 @@ public function notLike($field, string $match = '', string $side = 'both', ?bool
958958
* Generates a %LIKE% portion of the query.
959959
* Separates multiple calls with 'OR'.
960960
*
961-
* @param mixed $field
961+
* @param array|RawSql|string $field
962962
*
963963
* @return $this
964964
*/
@@ -971,7 +971,7 @@ public function orLike($field, string $match = '', string $side = 'both', ?bool
971971
* Generates a NOT LIKE portion of the query.
972972
* Separates multiple calls with 'OR'.
973973
*
974-
* @param mixed $field
974+
* @param array|RawSql|string $field
975975
*
976976
* @return $this
977977
*/
@@ -984,7 +984,7 @@ public function orNotLike($field, string $match = '', string $side = 'both', ?bo
984984
* Generates a %LIKE% portion of the query.
985985
* Separates multiple calls with 'AND'.
986986
*
987-
* @param mixed $field
987+
* @param array|RawSql|string $field
988988
*
989989
* @return $this
990990
*/
@@ -997,7 +997,7 @@ public function havingLike($field, string $match = '', string $side = 'both', ?b
997997
* Generates a NOT LIKE portion of the query.
998998
* Separates multiple calls with 'AND'.
999999
*
1000-
* @param mixed $field
1000+
* @param array|RawSql|string $field
10011001
*
10021002
* @return $this
10031003
*/
@@ -1010,7 +1010,7 @@ public function notHavingLike($field, string $match = '', string $side = 'both',
10101010
* Generates a %LIKE% portion of the query.
10111011
* Separates multiple calls with 'OR'.
10121012
*
1013-
* @param mixed $field
1013+
* @param array|RawSql|string $field
10141014
*
10151015
* @return $this
10161016
*/
@@ -1023,7 +1023,7 @@ public function orHavingLike($field, string $match = '', string $side = 'both',
10231023
* Generates a NOT LIKE portion of the query.
10241024
* Separates multiple calls with 'OR'.
10251025
*
1026-
* @param mixed $field
1026+
* @param array|RawSql|string $field
10271027
*
10281028
* @return $this
10291029
*/
@@ -1042,20 +1042,54 @@ public function orNotHavingLike($field, string $match = '', string $side = 'both
10421042
* @used-by notHavingLike()
10431043
* @used-by orNotHavingLike()
10441044
*
1045-
* @param mixed $field
1045+
* @param array|RawSql|string $field
10461046
*
10471047
* @return $this
10481048
*/
10491049
protected function _like($field, string $match = '', string $type = 'AND ', string $side = 'both', string $not = '', ?bool $escape = null, bool $insensitiveSearch = false, string $clause = 'QBWhere')
10501050
{
1051-
if (! is_array($field)) {
1052-
$field = [$field => $match];
1053-
}
1054-
10551051
$escape = is_bool($escape) ? $escape : $this->db->protectIdentifiers;
10561052
$side = strtolower($side);
10571053

1058-
foreach ($field as $k => $v) {
1054+
if ($field instanceof RawSql) {
1055+
$k = (string) $field;
1056+
$v = $match;
1057+
$insensitiveSearch = false;
1058+
1059+
$prefix = empty($this->{$clause}) ? $this->groupGetType('') : $this->groupGetType($type);
1060+
1061+
if ($side === 'none') {
1062+
$bind = $this->setBind($field->getBindingKey(), $v, $escape);
1063+
} elseif ($side === 'before') {
1064+
$bind = $this->setBind($field->getBindingKey(), "%{$v}", $escape);
1065+
} elseif ($side === 'after') {
1066+
$bind = $this->setBind($field->getBindingKey(), "{$v}%", $escape);
1067+
} else {
1068+
$bind = $this->setBind($field->getBindingKey(), "%{$v}%", $escape);
1069+
}
1070+
1071+
$likeStatement = $this->_like_statement($prefix, $k, $not, $bind, $insensitiveSearch);
1072+
1073+
// some platforms require an escape sequence definition for LIKE wildcards
1074+
if ($escape === true && $this->db->likeEscapeStr !== '') {
1075+
$likeStatement .= sprintf($this->db->likeEscapeStr, $this->db->likeEscapeChar);
1076+
}
1077+
1078+
$this->{$clause}[] = [
1079+
'condition' => $field->with($likeStatement),
1080+
'escape' => $escape,
1081+
];
1082+
1083+
return $this;
1084+
}
1085+
1086+
if (! is_array($field)) {
1087+
$keyValue = [$field => $match];
1088+
} else {
1089+
$keyValue = $field;
1090+
}
1091+
1092+
foreach ($keyValue as $k => $v) {
10591093
if ($insensitiveSearch === true) {
10601094
$v = strtolower($v);
10611095
}
@@ -2430,6 +2464,12 @@ protected function compileWhereHaving(string $qbKey): string
24302464
continue;
24312465
}
24322466

2467+
if ($qbkey['condition'] instanceof RawSql) {
2468+
$qbkey = $qbkey['condition'];
2469+
2470+
continue;
2471+
}
2472+
24332473
if ($qbkey['escape'] === false) {
24342474
$qbkey = $qbkey['condition'];
24352475

tests/system/Database/Builder/LikeTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace CodeIgniter\Database\Builder;
1313

1414
use CodeIgniter\Database\BaseBuilder;
15+
use CodeIgniter\Database\RawSql;
1516
use CodeIgniter\Test\CIUnitTestCase;
1617
use CodeIgniter\Test\Mock\MockConnection;
1718

@@ -47,6 +48,29 @@ public function testSimpleLike()
4748
$this->assertSame($expectedBinds, $builder->getBinds());
4849
}
4950

51+
/**
52+
* @see https://github.com/codeigniter4/CodeIgniter4/issues/3970
53+
*/
54+
public function testLikeWithRawSql()
55+
{
56+
$builder = new BaseBuilder('users', $this->db);
57+
58+
$sql = "concat(users.name, ' ', IF(users.surname IS NULL or users.surname = '', '', users.surname))";
59+
$rawSql = new RawSql($sql);
60+
$builder->like($rawSql, 'value', 'both');
61+
62+
$expectedSQL = "SELECT * FROM \"users\" WHERE {$sql} LIKE '%value%' ESCAPE '!' ";
63+
$expectedBinds = [
64+
$rawSql->getBindingKey() => [
65+
'%value%',
66+
true,
67+
],
68+
];
69+
70+
$this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect()));
71+
$this->assertSame($expectedBinds, $builder->getBinds());
72+
}
73+
5074
public function testLikeNoSide()
5175
{
5276
$builder = new BaseBuilder('job', $this->db);

0 commit comments

Comments
 (0)