diff --git a/system/Database/BaseBuilder.php b/system/Database/BaseBuilder.php index 0a27b0b33abc..df2fa411ca36 100644 --- a/system/Database/BaseBuilder.php +++ b/system/Database/BaseBuilder.php @@ -764,14 +764,18 @@ protected function whereHaving(string $qbKey, $key, $value = null, string $type $keyValue = $key; } + if ($keyValue === []) { + return $this; + } + // If the escape value was not set will base it on the global setting if (! is_bool($escape)) { $escape = $this->db->protectIdentifiers; } - foreach ($keyValue as $k => $v) { - $prefix = empty($this->{$qbKey}) ? $this->groupGetType('') : $this->groupGetType($type); + $prefix = empty($this->{$qbKey}) ? $this->groupGetType('') : $this->groupGetType($type); + foreach ($keyValue as $k => $v) { if ($rawSqlOnly) { $k = ''; $op = ''; @@ -830,6 +834,8 @@ protected function whereHaving(string $qbKey, $key, $value = null, string $type 'escape' => $escape, ]; } + + $prefix = $type; } return $this; @@ -1152,13 +1158,17 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri $keyValue = is_array($field) ? $field : [$field => $match]; + if ($keyValue === []) { + return $this; + } + + $prefix = $this->{$clause} === [] ? $this->groupGetType('') : $this->groupGetType($type); + foreach ($keyValue as $k => $v) { if ($insensitiveSearch) { $v = mb_strtolower($v, 'UTF-8'); } - $prefix = empty($this->{$clause}) ? $this->groupGetType('') : $this->groupGetType($type); - if ($side === 'none') { $bind = $this->setBind($k, $v, $escape); } elseif ($side === 'before') { @@ -1180,6 +1190,8 @@ protected function _like($field, string $match = '', string $type = 'AND ', stri 'condition' => $likeStatement, 'escape' => $escape, ]; + + $prefix = $type; } return $this; diff --git a/tests/system/Database/Builder/LikeTest.php b/tests/system/Database/Builder/LikeTest.php index d2a534b5bf27..f9d7cd91f65e 100644 --- a/tests/system/Database/Builder/LikeTest.php +++ b/tests/system/Database/Builder/LikeTest.php @@ -17,6 +17,7 @@ use CodeIgniter\Database\RawSql; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\Mock\MockConnection; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; /** @@ -231,4 +232,114 @@ public function testDBPrefixAndCoulmnWithTablename(): void $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect())); $this->assertSame($expectedBinds, $builder->getBinds()); } + + public function testLikeMultipleFields(): void + { + $builder = new BaseBuilder('job', $this->db); + + $builder->like([ + 'name' => 'veloper', + 'title' => 'dev', + ]); + + $expectedSQL = "SELECT * FROM \"job\" WHERE \"name\" LIKE '%veloper%' ESCAPE '!' AND \"title\" LIKE '%dev%' ESCAPE '!'"; + $expectedBinds = [ + 'name' => [ + '%veloper%', + true, + ], + 'title' => [ + '%dev%', + true, + ], + ]; + + $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect())); + $this->assertSame($expectedBinds, $builder->getBinds()); + } + + public function testLikeMultipleCallsWithRawSqlAndString(): void + { + $builder = new BaseBuilder('users', $this->db); + + $sql = "concat(users.name, ' ', users.surname)"; + $rawSql = new RawSql($sql); + + $builder->like($rawSql, 'value')->like('name', 'veloper'); + + $expectedSQL = "SELECT * FROM \"users\" WHERE {$sql} LIKE '%value%' ESCAPE '!' AND \"name\" LIKE '%veloper%' ESCAPE '!'"; + $expectedBinds = [ + $rawSql->getBindingKey() => [ + '%value%', + true, + ], + 'name' => [ + '%veloper%', + true, + ], + ]; + + $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect())); + $this->assertSame($expectedBinds, $builder->getBinds()); + } + + public function testOrLikeMultipleFields(): void + { + $builder = new BaseBuilder('job', $this->db); + + $builder->orLike([ + 'name' => 'veloper', + 'title' => 'dev', + ]); + + $expectedSQL = "SELECT * FROM \"job\" WHERE \"name\" LIKE '%veloper%' ESCAPE '!' OR \"title\" LIKE '%dev%' ESCAPE '!'"; + $expectedBinds = [ + 'name' => [ + '%veloper%', + true, + ], + 'title' => [ + '%dev%', + true, + ], + ]; + + $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect())); + $this->assertSame($expectedBinds, $builder->getBinds()); + } + + #[DataProvider('provideLikeMethodsWithEmptyArray')] + public function testLikeMethodsWithEmptyArray(string $method): void + { + $builder = new BaseBuilder('job', $this->db); + + $builder->groupStart() + ->{$method}([]) + ->where('id', 1) + ->groupEnd(); + + $expectedSQL = 'SELECT * FROM "job" WHERE ( "id" = 1 )'; + $expectedBinds = [ + 'id' => [ + 1, + true, + ], + ]; + + $this->assertSame($expectedSQL, str_replace("\n", ' ', $builder->getCompiledSelect())); + $this->assertSame($expectedBinds, $builder->getBinds()); + } + + /** + * @return array + */ + public static function provideLikeMethodsWithEmptyArray(): iterable + { + return [ + 'like' => ['like'], + 'orLike' => ['orLike'], + 'notLike' => ['notLike'], + 'orNotLike' => ['orNotLike'], + ]; + } } diff --git a/utils/phpstan-baseline/empty.notAllowed.neon b/utils/phpstan-baseline/empty.notAllowed.neon index 0f92e89f912e..e526a441d344 100644 --- a/utils/phpstan-baseline/empty.notAllowed.neon +++ b/utils/phpstan-baseline/empty.notAllowed.neon @@ -34,7 +34,7 @@ parameters: - message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' - count: 28 + count: 27 path: ../../system/Database/BaseBuilder.php -