Skip to content

Commit 81efd16

Browse files
hungthai1401tpetry
andauthored
feat: StrListContains expressions to replace MySQL's FIND_IN_SET() (tpetry#3)
Co-authored-by: tpetry <[email protected]>
1 parent 98c8cd0 commit 81efd16

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ use Illuminate\Contracts\Database\Query\Expression;
157157
use Tpetry\QueryExpressions\Operator\Comparison\{
158158
Between, DistinctFrom, Equal, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, NotDistinctFrom, NotEqual
159159
};
160+
use Tpetry\QueryExpressions\Function\Comparison\{
161+
StrListContains
162+
};
160163
use Tpetry\QueryExpressions\Operator\Logical\{
161164
CondAnd, CondNot, CondOr, CondXor
162165
};
@@ -171,6 +174,8 @@ new LessThanOrEqual(string|Expression $value1, string|Expression $value2);
171174
new NotDistinctFrom(string|Expression $value1, string|Expression $value2);
172175
new NotEqual(string|Expression $value1, string|Expression $value2);
173176

177+
new StrListContains(string|Expression $strList, string|Expression $str);
178+
174179
new CondAnd(string|Expression $value1, string|Expression $value2);
175180
new CondNot(string|Expression $value);
176181
new CondOr(string|Expression $value1, string|Expression $value2);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\QueryExpressions\Function\Comparison;
6+
7+
use Illuminate\Contracts\Database\Query\ConditionExpression;
8+
use Illuminate\Contracts\Database\Query\Expression;
9+
use Illuminate\Database\Grammar;
10+
use Tpetry\QueryExpressions\Concerns\IdentifiesDriver;
11+
use Tpetry\QueryExpressions\Concerns\StringizeExpression;
12+
13+
class StrListContains implements ConditionExpression
14+
{
15+
use IdentifiesDriver;
16+
use StringizeExpression;
17+
18+
public function __construct(
19+
private readonly string|Expression $strList,
20+
private readonly string|Expression $str,
21+
) {
22+
}
23+
24+
public function getValue(Grammar $grammar)
25+
{
26+
$strList = $this->stringize($grammar, $this->strList);
27+
$str = $this->stringize($grammar, $this->str);
28+
29+
// PostgreSQL: The string_to_array is not used because citext values would be cast to case-sensitive text type
30+
return match ($this->identify($grammar)) {
31+
'mysql' => "FIND_IN_SET({$str}, {$strList}) > 0",
32+
'pgsql', 'sqlite' => "({$strList} like {$str} or {$strList} like (({$str})||',%') or {$strList} like ('%,'||({$str})||',%') or {$strList} like ('%,'||({$str})))",
33+
'sqlsrv' => "({$strList} like {$str} or {$strList} like concat({$str},',%') or {$strList} like concat('%,',{$str},',%') or {$strList} like concat('%,',{$str}))",
34+
};
35+
}
36+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Illuminate\Database\Query\Expression;
6+
use Tpetry\QueryExpressions\Function\Comparison\StrListContains;
7+
8+
it('can check for existence of a column within a column string list')
9+
->expect(new StrListContains('haystack', 'needle'))
10+
->toBeExecutable(['haystack varchar(255)', 'needle varchar(255)'], options: [
11+
'sqlsrv' => ['position' => 'where'],
12+
])
13+
->toBeMysql('FIND_IN_SET(`needle`, `haystack`) > 0')
14+
->toBePgsql("(\"haystack\" like \"needle\" or \"haystack\" like ((\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")))")
15+
->toBeSqlite("(\"haystack\" like \"needle\" or \"haystack\" like ((\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")))")
16+
->toBeSqlsrv("([haystack] like [needle] or [haystack] like concat([needle],',%') or [haystack] like concat('%,',[needle],',%') or [haystack] like concat('%,',[needle]))");
17+
18+
it('can check for existence of an expression within an expression string list')
19+
->expect(new StrListContains(new Expression("'a,b,c'"), new Expression("'a'")))
20+
->toBeExecutable(options: [
21+
'sqlsrv' => ['position' => 'where'],
22+
])
23+
->toBeMysql("FIND_IN_SET('a', 'a,b,c') > 0")
24+
->toBePgsql("('a,b,c' like 'a' or 'a,b,c' like (('a')||',%') or 'a,b,c' like ('%,'||('a')||',%') or 'a,b,c' like ('%,'||('a')))")
25+
->toBeSqlite("('a,b,c' like 'a' or 'a,b,c' like (('a')||',%') or 'a,b,c' like ('%,'||('a')||',%') or 'a,b,c' like ('%,'||('a')))")
26+
->toBeSqlsrv("('a,b,c' like 'a' or 'a,b,c' like concat('a',',%') or 'a,b,c' like concat('%,','a',',%') or 'a,b,c' like concat('%,','a'))");
27+
28+
it('can check for existence of a column within an expression string list')
29+
->expect(new StrListContains(new Expression("'a,b,c'"), 'needle'))
30+
->toBeExecutable(['needle varchar(255)'], options: [
31+
'sqlsrv' => ['position' => 'where'],
32+
])
33+
->toBeMysql("FIND_IN_SET(`needle`, 'a,b,c') > 0")
34+
->toBePgsql("('a,b,c' like \"needle\" or 'a,b,c' like ((\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")))")
35+
->toBeSqlite("('a,b,c' like \"needle\" or 'a,b,c' like ((\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")))")
36+
->toBeSqlsrv("('a,b,c' like [needle] or 'a,b,c' like concat([needle],',%') or 'a,b,c' like concat('%,',[needle],',%') or 'a,b,c' like concat('%,',[needle]))");
37+
38+
it('can check for existence of an expression within a column string list')
39+
->expect(new StrListContains('haystack', new Expression("'a'")))
40+
->toBeExecutable(['haystack varchar(255)'], options: [
41+
'sqlsrv' => ['position' => 'where'],
42+
])
43+
->toBeMysql("FIND_IN_SET('a', `haystack`) > 0")
44+
->toBePgsql("(\"haystack\" like 'a' or \"haystack\" like (('a')||',%') or \"haystack\" like ('%,'||('a')||',%') or \"haystack\" like ('%,'||('a')))")
45+
->toBeSqlite("(\"haystack\" like 'a' or \"haystack\" like (('a')||',%') or \"haystack\" like ('%,'||('a')||',%') or \"haystack\" like ('%,'||('a')))")
46+
->toBeSqlsrv("([haystack] like 'a' or [haystack] like concat('a',',%') or [haystack] like concat('%,','a',',%') or [haystack] like concat('%,','a'))");

0 commit comments

Comments
 (0)