From 327cf9b5b3720a02948c29d021cd7edd9828a9a2 Mon Sep 17 00:00:00 2001 From: talpx1 Date: Thu, 14 Mar 2024 19:13:50 +0100 Subject: [PATCH] fixed incorrect sql LIKE escaping for some db drivers while performing partial filter --- src/Filters/FiltersPartial.php | 12 +++++++++++- tests/FilterTest.php | 25 ++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Filters/FiltersPartial.php b/src/Filters/FiltersPartial.php index e0106549..8a85401e 100644 --- a/src/Filters/FiltersPartial.php +++ b/src/Filters/FiltersPartial.php @@ -3,6 +3,7 @@ namespace Spatie\QueryBuilder\Filters; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Facades\DB; /** * @template TModelClass of \Illuminate\Database\Eloquent\Model @@ -47,7 +48,7 @@ protected function getWhereRawParameters($value, string $property): array $value = mb_strtolower((string) $value, 'UTF8'); return [ - "LOWER({$property}) LIKE ?", + "LOWER({$property}) LIKE ?".self::maybeSpecifyEscapeChar(), ['%'.self::escapeLike($value).'%'], ]; } @@ -60,4 +61,13 @@ private static function escapeLike(string $value): string $value, ); } + + private static function maybeSpecifyEscapeChar(): string + { + if(!in_array(DB::getDriverName(), ["sqlite","pgsql","sqlsrv"])){ + return ""; + } + + return " ESCAPE '\'"; + } } diff --git a/tests/FilterTest.php b/tests/FilterTest.php index c9f9f0c1..354ce834 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -5,6 +5,7 @@ use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; +use Pest\Expectation; use function PHPUnit\Framework\assertObjectHasProperty; use Spatie\QueryBuilder\AllowedFilter; @@ -86,9 +87,31 @@ ->where(DB::raw('LOWER(`test_models`.`name`)'), 'LIKE', 'john') ->toSql(); - expect($queryBuilderSql)->toEqual($expectedSql); + expect($queryBuilderSql)->toContain($expectedSql); }); +it('specifies escape character in supported databases', function (string $dbDriver) { + $fakeConnection = "test_{$dbDriver}"; + + DB::connectUsing($fakeConnection, [ + 'driver' => $dbDriver, + 'database' => null, + ]); + + DB::usingConnection($fakeConnection, function() use ($dbDriver){ + $request = new Request([ + 'filter' => ['name' => 'to_find'], + ]); + + $queryBuilderSql = QueryBuilder::for(TestModel::select('id', 'name'), $request) + ->allowedFilters('name', 'id') + ->toSql(); + + expect($queryBuilderSql)->when(in_array($dbDriver, ["sqlite","pgsql","sqlsrv"]), fn(Expectation $query) => $query->toContain("ESCAPE '\'")); + expect($queryBuilderSql)->when($dbDriver === 'mysql', fn(Expectation $query) => $query->not->toContain("ESCAPE '\'")); + }); +})->with(['sqlite', 'mysql', 'pgsql', 'sqlsrv']); + it('can filter results based on the existence of a property in an array', function () { $results = createQueryFromFilterRequest([ 'id' => '1,2',