diff --git a/README.md b/README.md index a4077db..c21de41 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,9 @@ use Illuminate\Contracts\Database\Query\Expression; use Tpetry\QueryExpressions\Operator\Comparison\{ Between, DistinctFrom, Equal, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, NotDistinctFrom, NotEqual }; +use Tpetry\QueryExpressions\Function\Comparison\{ + StrListContains +}; use Tpetry\QueryExpressions\Operator\Logical\{ CondAnd, CondNot, CondOr, CondXor }; @@ -171,6 +174,8 @@ new LessThanOrEqual(string|Expression $value1, string|Expression $value2); new NotDistinctFrom(string|Expression $value1, string|Expression $value2); new NotEqual(string|Expression $value1, string|Expression $value2); +new StrListContains(string|Expression $strList, string|Expression $str); + new CondAnd(string|Expression $value1, string|Expression $value2); new CondNot(string|Expression $value); new CondOr(string|Expression $value1, string|Expression $value2); diff --git a/src/Function/Comparison/StrListContains.php b/src/Function/Comparison/StrListContains.php new file mode 100644 index 0000000..c098d5a --- /dev/null +++ b/src/Function/Comparison/StrListContains.php @@ -0,0 +1,36 @@ +stringize($grammar, $this->strList); + $str = $this->stringize($grammar, $this->str); + + // PostgreSQL: The string_to_array is not used because citext values would be cast to case-sensitive text type + return match ($this->identify($grammar)) { + 'mysql' => "FIND_IN_SET({$str}, {$strList}) > 0", + 'pgsql', 'sqlite' => "({$strList} like {$str} or {$strList} like (({$str})||',%') or {$strList} like ('%,'||({$str})||',%') or {$strList} like ('%,'||({$str})))", + 'sqlsrv' => "({$strList} like {$str} or {$strList} like concat({$str},',%') or {$strList} like concat('%,',{$str},',%') or {$strList} like concat('%,',{$str}))", + }; + } +} diff --git a/tests/Function/Comparison/StrListContainsTest.php b/tests/Function/Comparison/StrListContainsTest.php new file mode 100644 index 0000000..f48ec28 --- /dev/null +++ b/tests/Function/Comparison/StrListContainsTest.php @@ -0,0 +1,46 @@ +expect(new StrListContains('haystack', 'needle')) + ->toBeExecutable(['haystack varchar(255)', 'needle varchar(255)'], options: [ + 'sqlsrv' => ['position' => 'where'], + ]) + ->toBeMysql('FIND_IN_SET(`needle`, `haystack`) > 0') + ->toBePgsql("(\"haystack\" like \"needle\" or \"haystack\" like ((\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")))") + ->toBeSqlite("(\"haystack\" like \"needle\" or \"haystack\" like ((\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")||',%') or \"haystack\" like ('%,'||(\"needle\")))") + ->toBeSqlsrv("([haystack] like [needle] or [haystack] like concat([needle],',%') or [haystack] like concat('%,',[needle],',%') or [haystack] like concat('%,',[needle]))"); + +it('can check for existence of an expression within an expression string list') + ->expect(new StrListContains(new Expression("'a,b,c'"), new Expression("'a'"))) + ->toBeExecutable(options: [ + 'sqlsrv' => ['position' => 'where'], + ]) + ->toBeMysql("FIND_IN_SET('a', 'a,b,c') > 0") + ->toBePgsql("('a,b,c' like 'a' or 'a,b,c' like (('a')||',%') or 'a,b,c' like ('%,'||('a')||',%') or 'a,b,c' like ('%,'||('a')))") + ->toBeSqlite("('a,b,c' like 'a' or 'a,b,c' like (('a')||',%') or 'a,b,c' like ('%,'||('a')||',%') or 'a,b,c' like ('%,'||('a')))") + ->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'))"); + +it('can check for existence of a column within an expression string list') + ->expect(new StrListContains(new Expression("'a,b,c'"), 'needle')) + ->toBeExecutable(['needle varchar(255)'], options: [ + 'sqlsrv' => ['position' => 'where'], + ]) + ->toBeMysql("FIND_IN_SET(`needle`, 'a,b,c') > 0") + ->toBePgsql("('a,b,c' like \"needle\" or 'a,b,c' like ((\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")))") + ->toBeSqlite("('a,b,c' like \"needle\" or 'a,b,c' like ((\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")||',%') or 'a,b,c' like ('%,'||(\"needle\")))") + ->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]))"); + +it('can check for existence of an expression within a column string list') + ->expect(new StrListContains('haystack', new Expression("'a'"))) + ->toBeExecutable(['haystack varchar(255)'], options: [ + 'sqlsrv' => ['position' => 'where'], + ]) + ->toBeMysql("FIND_IN_SET('a', `haystack`) > 0") + ->toBePgsql("(\"haystack\" like 'a' or \"haystack\" like (('a')||',%') or \"haystack\" like ('%,'||('a')||',%') or \"haystack\" like ('%,'||('a')))") + ->toBeSqlite("(\"haystack\" like 'a' or \"haystack\" like (('a')||',%') or \"haystack\" like ('%,'||('a')||',%') or \"haystack\" like ('%,'||('a')))") + ->toBeSqlsrv("([haystack] like 'a' or [haystack] like concat('a',',%') or [haystack] like concat('%,','a',',%') or [haystack] like concat('%,','a'))");