diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9d35c4d --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,36 @@ +name: PHP Composer + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run test suite + run: composer test diff --git a/composer.json b/composer.json index 87a4949..c68c9c6 100644 --- a/composer.json +++ b/composer.json @@ -20,13 +20,16 @@ "require": { "php": "^8.0", "http-interop/http-factory-guzzle": "^1.2", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", "laravel/scout": "^8.0|^9.0|^10.0", "meilisearch/meilisearch-php": "^1.11" }, "require-dev": { - "orchestra/testbench": "^9.5", - "phpunit/phpunit": "^9.6|^10.5|^11.4" + "laravel/pint": "^1.22", + "orchestra/testbench": "^9.5|^10.0", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.6|^10.5|^11.4", + "rector/rector": "^2.0" }, "autoload": { "psr-4": { @@ -45,7 +48,18 @@ } }, "scripts": { - "test": "phpunit tests" + "refactor": "rector", + "lint": "pint", + "test:refactor": "rector --dry-run", + "test:lint": "pint --test", + "test:types": "phpstan analyse --ansi", + "test:unit": "phpunit tests", + "test": [ + "@test:refactor", + "@test:lint", + "@test:types", + "@test:unit" + ] }, "minimum-stability": "dev", "prefer-stable": true diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..6e3585e --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,6 @@ +parameters: + level: 5 + paths: + - src + + reportUnmatchedIgnoredErrors: true \ No newline at end of file diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..1ed0c95 --- /dev/null +++ b/pint.json @@ -0,0 +1,44 @@ +{ + "preset": "laravel", + "rules": { + "array_push": true, + "backtick_to_shell_exec": true, + "declare_strict_types": true, + "final_class": true, + "fully_qualified_strict_types": true, + "global_namespace_import": { + "import_classes": true, + "import_constants": true, + "import_functions": true + }, + "ordered_class_elements": { + "order": [ + "use_trait", + "case", + "constant", + "constant_public", + "constant_protected", + "constant_private", + "property_public", + "property_protected", + "property_private", + "construct", + "destruct", + "magic", + "phpunit", + "method_abstract", + "method_public_static", + "method_public", + "method_protected_static", + "method_protected", + "method_private_static", + "method_private" + ], + "sort_algorithm": "none" + }, + "ordered_interfaces": true, + "ordered_traits": true, + "protected_to_private": true, + "strict_comparison": true + } +} \ No newline at end of file diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..9c415d4 --- /dev/null +++ b/rector.php @@ -0,0 +1,20 @@ +withPaths([ + __DIR__.'/src', + __DIR__.'/tests', + ]) + ->withPreparedSets( + deadCode: true, + codeQuality: true, + typeDeclarations: true, + privatization: true, + earlyReturn: true, + strictBooleans: true, + ) + ->withPhpSets(); diff --git a/src/Contracts/QueryBuilder.php b/src/Contracts/QueryBuilder.php index 7b4ae20..d8e9d57 100644 --- a/src/Contracts/QueryBuilder.php +++ b/src/Contracts/QueryBuilder.php @@ -1,5 +1,7 @@ 'eq', + '!=' => 'neq', + 'in' => 'in', + 'not in' => 'nin', + '>=' => 'gte', + '<=' => 'lte', + '>' => 'gt', + '<' => 'lt', + 'to' => 'to', + 'not' => 'not', + 'exists' => 'exists', + 'is empty' => 'empty', + 'is null' => 'null', + default => 'eq' + }; + } + + private function build(string $operator, string $field, $value): string { return sprintf("%s {$operator} %s", $field, $this->escape($value)); } - protected function gte(string $field, mixed $value): string + private function gte(string $field, mixed $value): string { return $this->build('>=', $field, $value); } - protected function lte(string $field, mixed $value): string + private function lte(string $field, mixed $value): string { return $this->build('<=', $field, $value); } - protected function eq(string $field, mixed $value): string + private function eq(string $field, mixed $value): string { return $this->build('=', $field, $value); } - protected function not(string $field, mixed $value): string + private function not(string $field, mixed $value): string { return 'NOT '.$this->build('=', $field, $value); } - protected function neq(string $field, mixed $value): string + private function neq(string $field, mixed $value): string { return $this->build('!=', $field, $value); } - protected function in(string $field, array $array): string + private function in(string $field, array $array): string { $values = collect($array) ->map(fn ($value) => $this->escape($value)) @@ -61,7 +83,7 @@ protected function in(string $field, array $array): string return sprintf('%s IN [%s]', $field, $values); } - protected function nin(string $field, array $array): string + private function nin(string $field, array $array): string { $values = collect($array) ->map(fn ($value) => $this->escape($value)) @@ -70,27 +92,27 @@ protected function nin(string $field, array $array): string return sprintf('%s NOT IN [%s]', $field, $values); } - protected function exists(string $field): string + private function exists(string $field): string { return sprintf('%s EXISTS', $field); } - protected function null(string $field): string + private function null(string $field): string { return sprintf('%s IS NULL', $field); } - protected function empty(string $field): string + private function empty(string $field): string { return sprintf('%s IS EMPTY', $field); } - protected function to(string $field, mixed $value): string + private function to(string $field, mixed $value): string { return sprintf('%s %s TO %s', $field, ...$value); } - protected function bool(string $field, bool $bool): string + private function bool(string $field, bool $bool): string { return sprintf('%s = %s', $field, $this->escape($bool)); } @@ -105,24 +127,4 @@ private function escape($data) is_int($data) || is_float($data) ? $data : "'{$data}'" ); } - - public function operatorFunc(?string $operator = null): string - { - return match (strtolower($operator ?? '')) { - '=' => 'eq', - '!=' => 'neq', - 'in' => 'in', - 'not in' => 'nin', - '>=' => 'gte', - '<=' => 'lte', - '>' => 'gt', - '<' => 'lt', - 'to' => 'to', - 'not' => 'not', - 'exists' => 'exists', - 'is empty' => 'empty', - 'is null' => 'null', - default => 'eq' - }; - } } diff --git a/src/MeilisearchQuery.php b/src/MeilisearchQuery.php index 85c6dc8..836cf22 100644 --- a/src/MeilisearchQuery.php +++ b/src/MeilisearchQuery.php @@ -1,25 +1,26 @@ segments[] = new NestedExpression( - $column(new self(false))->segments, $boolean, empty($this->segments) + $column(new self(false))->segments, $boolean, $this->segments === [] ); return $this; @@ -75,7 +76,7 @@ public function where( ); $this->segments[] = new Expression( - $column, $value, $operator, $boolean, empty($this->segments) + $column, $value, $operator, $boolean, $this->segments === [] ); return $this; @@ -94,6 +95,7 @@ public function compile(): string|self */ public function search(string $term = ''): \Laravel\Scout\Builder { + /** @phpstan-ignore staticMethod.notFound */ return $this->model::search($term, $this->callback()); } @@ -145,43 +147,6 @@ public function orWhereGeoBoundingBox(float $latitude1, float $longitude1, float return $this->orWhereRaw("_geoBoundingBox([$latitude1, $longitude1], [$latitude2, $longitude2])"); } - /** - * Add raw expression to the builder. - */ - protected function raw(string $query, string $boolean = 'AND'): self - { - $this->segments[] = new RawExpression($query, $boolean, empty($this->segments)); - - return $this; - } - - /** - * Ensure that the for() method has been called before proceeding. - * - * @throws InvalidArgumentException - */ - protected function ensureModelIsSet() - { - if (! $this->model && $this->compilable) { - throw new InvalidArgumentException('You must call MeilisearchQuery::for() with a valid model before using this method.'); - } - } - - /** - * Return a callback for Scout based on the compiled query. - */ - protected function callback(): Closure - { - $filter = $this->compile(); - - return function (Indexes $meilisearch, $query, $options) use ($filter) { - $options['filter'] = $filter; - $options['sort'] = $this->sort; - - return $meilisearch->search($query, $options); - }; - } - /** * Add an "or where" clause to the segments array. */ @@ -326,26 +291,64 @@ public function inspect(): array return [ 'segments' => $this->segments, 'sort' => $this->sort, - 'model' => $this->model + 'model' => $this->model, ]; } /** * Dump the builder properties. */ - public function dump() + public function dump(): void { dump($this->inspect()); } + /** + * Add raw expression to the builder. + */ + private function raw(string $query, string $boolean = 'AND'): self + { + $this->segments[] = new RawExpression($query, $boolean, $this->segments === []); + + return $this; + } + + /** + * Ensure that the for() method has been called before proceeding. + * + * @throws InvalidArgumentException + */ + private function ensureModelIsSet(): void + { + if (! $this->model instanceof Model && $this->compilable) { + throw new InvalidArgumentException('You must call MeilisearchQuery::for() with a valid model before using this method.'); + } + } + + /** + * Return a callback for Scout based on the compiled query. + */ + private function callback(): Closure + { + $filter = $this->compile(); + + return function (Indexes $meilisearch, $query, array $options) use ($filter) { + $options['filter'] = $filter; + $options['sort'] = $this->sort; + + return $meilisearch->search($query, $options); + }; + } + /** * Prepare the value and operator for a where clause. */ - protected function prepareValueAndOperator(mixed $value, mixed $operator, bool $useDefault = false): array + private function prepareValueAndOperator(mixed $value, mixed $operator, bool $useDefault = false): array { if ($useDefault) { return [$operator, '=']; - } elseif ($this->invalidOperatorAndValue($operator, $value)) { + } + if ($this->invalidOperatorAndValue($operator, $value)) { throw new InvalidArgumentException('Illegal operator and value combination.'); } @@ -355,9 +358,13 @@ protected function prepareValueAndOperator(mixed $value, mixed $operator, bool $ /** * Determine if the given operator and arg count combination should use the default operator and value. */ - protected function shouldUseDefaultValueAndOperator(int $argCount, ?string $operator): bool + private function shouldUseDefaultValueAndOperator(int $argCount, mixed $operator): bool { - return $argCount === 2 && ! in_array(strtolower($operator ?? ''), self::OPERATORS_COLUMN_ONLY); + if (is_string($operator) === false) { + return true; + } + + return $argCount === 2 && ! in_array(strtolower($operator), self::OPERATORS_COLUMN_ONLY); } /** @@ -365,8 +372,10 @@ protected function shouldUseDefaultValueAndOperator(int $argCount, ?string $oper * * Prevents using Null values with invalid operators. */ - protected function invalidOperatorAndValue(?string $operator, mixed $value): bool + private function invalidOperatorAndValue(mixed $operator, mixed $value): bool { - return is_null($value) && in_array(strtolower($operator ?? ''), self::OPERATORS); + $operator = is_string($operator) ? strtolower($operator) : $operator; + + return is_null($value) && in_array($operator ?? '', self::OPERATORS); } } diff --git a/src/NestedExpression.php b/src/NestedExpression.php index 4b119cd..d67eb9a 100644 --- a/src/NestedExpression.php +++ b/src/NestedExpression.php @@ -1,10 +1,12 @@ init ? '' : $this->boolean, - collect($this->expressions)->map->compile()->implode(' ') + collect($this->expressions) + ->map(fn (QuerySegment $expression): string => $expression->compile()) + ->implode(' ') )); } } diff --git a/src/QueryCompiler.php b/src/QueryCompiler.php index 86dd9bc..8da3de5 100644 --- a/src/QueryCompiler.php +++ b/src/QueryCompiler.php @@ -1,5 +1,7 @@ whereInstanceOf(QuerySegment::class) - ->map->compile() + ->map(fn (QuerySegment $segment): string => $segment->compile()) ->unique() ->filter() ->implode(' '); diff --git a/src/RawExpression.php b/src/RawExpression.php index a13b532..7304cd9 100644 --- a/src/RawExpression.php +++ b/src/RawExpression.php @@ -1,10 +1,12 @@ init ? '' : $this->boolean, $this->rawQuery)); } -} \ No newline at end of file +} diff --git a/tests/MeilisearchQueryTest.php b/tests/MeilisearchQueryTest.php index 932e492..f9f52eb 100644 --- a/tests/MeilisearchQueryTest.php +++ b/tests/MeilisearchQueryTest.php @@ -1,49 +1,53 @@ assertSame( MeilisearchQuery::class, - get_class(MeilisearchQuery::for(User::class)) + MeilisearchQuery::for(User::class)::class ); } - public function testForNonEloquentModel() + public function test_for_non_eloquent_model(): void { $this->expectException(InvalidArgumentException::class); - MeilisearchQuery::for(\stdClass::class); + MeilisearchQuery::for(stdClass::class); } - public function testForNonSearchableEloquentModel() + public function test_for_non_searchable_eloquent_model(): void { $this->expectException(InvalidArgumentException::class); MeilisearchQuery::for(NonSearchableUser::class); } - public function testBasicQuery() + public function test_basic_query(): void { $compiled = MeilisearchQuery::for(User::class)->where('verified', true)->compile(); $this->assertSame("verified = 'true'", $compiled); } - public function testBasicQueryWithExplicitOperator() + public function test_basic_query_with_explicit_operator(): void { $compiled = MeilisearchQuery::for(User::class)->where('verified', '=', true)->compile(); $this->assertSame("verified = 'true'", $compiled); } - public function testBasicNestedQuery() + public function test_basic_nested_query(): void { $compiled = MeilisearchQuery::for(User::class)->where(fn ($query) => $query ->where('name', 'Chris') @@ -53,7 +57,7 @@ public function testBasicNestedQuery() $this->assertSame("(name = 'Chris' OR name = 'Bob') AND verified = 'true'", $compiled); } - public function testBasicNestedQueryWithExplicitOperator() + public function test_basic_nested_query_with_explicit_operator(): void { $compiled = MeilisearchQuery::for(User::class)->where(fn ($query) => $query ->where('name', '=', 'Chris') @@ -63,21 +67,21 @@ public function testBasicNestedQueryWithExplicitOperator() $this->assertSame("(name = 'Chris' OR name = 'Bob') AND verified = 'true'", $compiled); } - public function testBasicWhereInQuery() + public function test_basic_where_in_query(): void { $compiled = MeilisearchQuery::for(User::class)->whereIn('name', ['Chris', 'Bob'])->compile(); $this->assertSame("name IN ['Chris','Bob']", $compiled); } - public function testBasicOrWhereInQuery() + public function test_basic_or_where_in_query(): void { $compiled = MeilisearchQuery::for(User::class)->orWhereIn('name', ['Chris', 'Bob'])->compile(); $this->assertSame("name IN ['Chris','Bob']", $compiled); } - public function testBasicWhereInNestedQuery() + public function test_basic_where_in_nested_query(): void { $compiled = MeilisearchQuery::for(User::class)->where(fn ($query) => $query ->whereIn('name', ['Chris', 'Bob']) @@ -88,21 +92,21 @@ public function testBasicWhereInNestedQuery() $this->assertSame("(name IN ['Chris','Bob']) OR email = 'chris@example.com'", $compiled); } - public function testBasicWhereNotInQuery() + public function test_basic_where_not_in_query(): void { $compiled = MeilisearchQuery::for(User::class)->whereNotIn('name', ['Chris', 'Bob'])->compile(); $this->assertSame("name NOT IN ['Chris','Bob']", $compiled); } - public function testBasicWhereNotQuery() + public function test_basic_where_not_query() { $compiled = MeilisearchQuery::for(User::class)->whereNot('name', 'Chris')->compile(); return $this->assertSame("NOT name = 'Chris'", $compiled); } - public function testBasicWhereNotNestedQuery() + public function test_basic_where_not_nested_query(): void { $compiled = MeilisearchQuery::for(User::class)->where(fn ($query) => $query ->whereNot('name', 'Chris') @@ -114,55 +118,55 @@ public function testBasicWhereNotNestedQuery() $this->assertSame("(NOT name = 'Chris' AND email = 'chris@example.com') OR email = 'bob@example.com'", $compiled); } - public function testBasicWhereExistsQuery() + public function test_basic_where_exists_query(): void { $this->assertSame('name EXISTS', MeilisearchQuery::for(User::class)->whereExists('name')->compile()); } - public function testBasicWhereIsNullQuery() + public function test_basic_where_is_null_query(): void { $this->assertSame('name IS NULL', MeilisearchQuery::for(User::class)->whereIsNull('name')->compile()); } - public function testBasicWhereIsEmptyQuery() + public function test_basic_where_is_empty_query(): void { $this->assertSame('name IS EMPTY', MeilisearchQuery::for(User::class)->whereIsEmpty('name')->compile()); } - public function testBasicOrWhereExistsQuery() + public function test_basic_or_where_exists_query(): void { $this->assertSame('name EXISTS', MeilisearchQuery::for(User::class)->orWhereExists('name')->compile()); } - public function testBasicOrWhereIsNullQuery() + public function test_basic_or_where_is_null_query(): void { $this->assertSame('name IS NULL', MeilisearchQuery::for(User::class)->orWhereIsNull('name')->compile()); } - public function testBasicOrWhereIsEmptyQuery() + public function test_basic_or_where_is_empty_query(): void { $this->assertSame('name IS EMPTY', MeilisearchQuery::for(User::class)->orWhereIsEmpty('name')->compile()); } - public function testBasicOrWhereToQuery() + public function test_basic_or_where_to_query(): void { $this->assertSame('count 1 TO 10', MeilisearchQuery::for(User::class)->orWhereTo('count', 1, 10)->compile()); } - public function testBasicWhereGteQuery() + public function test_basic_where_gte_query(): void { - $this->assertSame('count >= 10', MeilisearchQuery::for(User::class)->where('count', ">=", 10)->compile()); + $this->assertSame('count >= 10', MeilisearchQuery::for(User::class)->where('count', '>=', 10)->compile()); } - public function testBasicWhereLteQuery() + public function test_basic_where_lte_query(): void { - $this->assertSame('count <= 10', MeilisearchQuery::for(User::class)->where('count', "<=", 10)->compile()); + $this->assertSame('count <= 10', MeilisearchQuery::for(User::class)->where('count', '<=', 10)->compile()); } - public function testMultipleNestedOperators() + public function test_multiple_nested_operators(): void { $compiled = MeilisearchQuery::for(User::class)->where(fn ($query) => $query - ->where('count', ">=", 10) + ->where('count', '>=', 10) ->where('count', '<=', 100) ->orWhere(fn ($subQuery) => $subQuery ->where('name', 'Chris') @@ -176,15 +180,15 @@ public function testMultipleNestedOperators() $this->assertSame("(count >= 10 AND count <= 100 OR (name = 'Chris' OR name IS EMPTY OR email IS NULL)) OR name = 'Bob'", $compiled); } - public function testNestedInception() + public function test_nested_inception(): void { $compiled = MeilisearchQuery::for(User::class)->where('name', 'Chris') ->where(fn ($query) => $query->where('name', 'Bob')->where('verified', true)) - ->orWhere(fn ($query) => $query->where('name', 'Erin')) - ->orWhere(fn ($query) => $query->where('email', '!=', 'chris@example.com') - ->orWhere('email', 'test@example.com')) - ->orWhere(fn ($query) => $query->where('email', 'erin@example.com')) - ->compile(); + ->orWhere(fn ($query) => $query->where('name', 'Erin')) + ->orWhere(fn ($query) => $query->where('email', '!=', 'chris@example.com') + ->orWhere('email', 'test@example.com')) + ->orWhere(fn ($query) => $query->where('email', 'erin@example.com')) + ->compile(); $this->assertSame( "name = 'Chris' AND (name = 'Bob' AND verified = 'true') OR (name = 'Erin') OR (email != 'chris@example.com' OR email = 'test@example.com') OR (email = 'erin@example.com')", @@ -192,17 +196,17 @@ public function testNestedInception() ); } - public function testNestedQueryWithoutCompileResolvesToConcreteClass() + public function test_nested_query_without_compile_resolves_to_concrete_class(): void { $builder = MeilisearchQuery::for(User::class)->where(fn ($query) => $query ->where('name', 'Chris') ->orWhere('name', 'Bob') ); - $this->assertInstanceOf(\Chr15k\MeilisearchAdvancedQuery\MeilisearchQuery::class, $builder); + $this->assertInstanceOf(MeilisearchQuery::class, $builder); } - public function testSingleSort() + public function test_single_sort(): void { $instance = MeilisearchQuery::for(User::class) ->where('name', 'Chris') @@ -213,7 +217,7 @@ public function testSingleSort() $this->assertSame(['name:desc'], $instance['sort']); } - public function testMultipleSort() + public function test_multiple_sort(): void { $instance = MeilisearchQuery::for(User::class) ->where('name', 'Chris') @@ -224,14 +228,14 @@ public function testMultipleSort() $this->assertSame(['name:desc', 'email:asc'], $instance['sort']); } - public function testBasicRawQuery() + public function test_basic_raw_query(): void { $compiled = MeilisearchQuery::for(User::class)->whereRaw("name = 'Chris'")->compile(); $this->assertSame("name = 'Chris'", $compiled); } - public function testBasicMultipleRawOrQuery() + public function test_basic_multiple_raw_or_query(): void { $compiled = MeilisearchQuery::for(User::class) ->whereRaw("name = 'Chris'") @@ -241,7 +245,7 @@ public function testBasicMultipleRawOrQuery() $this->assertSame("name = 'Chris' OR name = 'Bob'", $compiled); } - public function testBasicRawAndQuery() + public function test_basic_raw_and_query(): void { $compiled = MeilisearchQuery::for(User::class) ->whereRaw("name = 'Chris'") @@ -251,7 +255,7 @@ public function testBasicRawAndQuery() $this->assertSame("name = 'Chris' AND name = 'Bob'", $compiled); } - public function testSingleBasicRawQuery() + public function test_single_basic_raw_query(): void { $compiled = MeilisearchQuery::for(User::class) ->whereRaw("name = 'Chris' OR name = 'Bob'") @@ -260,7 +264,7 @@ public function testSingleBasicRawQuery() $this->assertSame("name = 'Chris' OR name = 'Bob'", $compiled); } - public function testMixedRawNestedQuery() + public function test_mixed_raw_nested_query(): void { $compiled = MeilisearchQuery::for(User::class) ->where('email', 'chris@example.com') @@ -274,16 +278,16 @@ public function testMixedRawNestedQuery() $this->assertSame("email = 'chris@example.com' AND (name = 'Chris' OR name = 'Bob') AND verified = 'true'", $compiled); } - public function testWhereGeoRadius() + public function test_where_geo_radius(): void { $compiled = MeilisearchQuery::for(User::class) ->whereGeoRadius(48.8566, 2.3522, 1000) ->compile(); - $this->assertSame("_geoRadius(48.8566, 2.3522, 1000)", $compiled); + $this->assertSame('_geoRadius(48.8566, 2.3522, 1000)', $compiled); } - public function testOrWhereGeoRadius() + public function test_or_where_geo_radius(): void { $compiled = MeilisearchQuery::for(User::class) ->where('name', 'Chris') @@ -293,16 +297,16 @@ public function testOrWhereGeoRadius() $this->assertSame("name = 'Chris' OR _geoRadius(48.8566, 2.3522, 1000)", $compiled); } - public function testWhereGeoBoundingBox() + public function test_where_geo_bounding_box(): void { $compiled = MeilisearchQuery::for(User::class) ->whereGeoBoundingBox(48.8566, 2.3522, 48.9, 2.4) ->compile(); - $this->assertSame("_geoBoundingBox([48.8566, 2.3522], [48.9, 2.4])", $compiled); + $this->assertSame('_geoBoundingBox([48.8566, 2.3522], [48.9, 2.4])', $compiled); } - public function testOrWhereGeoBoundingBox() + public function test_or_where_geo_bounding_box(): void { $compiled = MeilisearchQuery::for(User::class) ->where('name', 'Chris') diff --git a/tests/Models/NonSearchableUser.php b/tests/Models/NonSearchableUser.php index ea9a012..68d3d89 100644 --- a/tests/Models/NonSearchableUser.php +++ b/tests/Models/NonSearchableUser.php @@ -1,7 +1,9 @@