diff --git a/src/boost/docs/scout.md b/src/boost/docs/scout.md index 6cf1d6474..142f05d45 100644 --- a/src/boost/docs/scout.md +++ b/src/boost/docs/scout.md @@ -57,7 +57,7 @@ After installing Scout, you should publish the Scout configuration file using th php artisan vendor:publish --tag=scout-config ``` -Finally, add the `Hypervel\Scout\Searchable` trait and the `Hypervel\Scout\Contracts\SearchableInterface` contract to the model you would like to make searchable. The trait will register a model observer that will automatically keep the model in sync with your search driver: +Finally, add the `Hypervel\Scout\Searchable` trait to the model you would like to make searchable. The trait will register a model observer that will automatically keep the model in sync with your search driver: ```php ### Database Engine -Scout's built-in database engine performs full-text and `LIKE` searches against your existing database — no external service or extra infrastructure required. Simply add the `Searchable` trait to your model, implement the `SearchableInterface` contract, and define a `toSearchableArray` method that returns the columns you want to be searchable. +Scout's built-in database engine performs full-text and `LIKE` searches against your existing database — no external service or extra infrastructure required. Simply add the `Searchable` trait to your model and define a `toSearchableArray` method that returns the columns you want to be searchable. You may use PHP attributes to control the search strategy for each column. `SearchUsingFullText` will use your database's full-text index, `SearchUsingPrefix` will only match from the beginning of the string (`example%`), and any columns without an attribute use a default `LIKE` strategy with wildcards on both sides (`%example%`): @@ -168,10 +168,9 @@ namespace App\Models; use Hypervel\Database\Eloquent\Model; use Hypervel\Scout\Attributes\SearchUsingFullText; use Hypervel\Scout\Attributes\SearchUsingPrefix; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; -class Article extends Model implements SearchableInterface +class Article extends Model { use Searchable; diff --git a/src/scout/src/Builder.php b/src/scout/src/Builder.php index e7b546238..5d1df2acc 100644 --- a/src/scout/src/Builder.php +++ b/src/scout/src/Builder.php @@ -27,7 +27,7 @@ /** * Fluent search query builder for searchable models. * - * @template TModel of Model&SearchableInterface + * @template TModel of Model */ class Builder { @@ -38,7 +38,7 @@ class Builder /** * The model instance. * - * @var TModel + * @var SearchableInterface&TModel */ public Model $model; @@ -118,6 +118,7 @@ public function __construct( ?Closure $callback = null, bool $softDelete = false ) { + /** @var SearchableInterface&TModel $model */ $this->model = $model; $this->query = $query; $this->callback = $callback; diff --git a/src/scout/src/Console/IndexCommand.php b/src/scout/src/Console/IndexCommand.php index 5c28276f1..d757f293c 100644 --- a/src/scout/src/Console/IndexCommand.php +++ b/src/scout/src/Console/IndexCommand.php @@ -70,7 +70,7 @@ public function handle(EngineManager $manager, Repository $config): int if ($model !== null && $config->boolean('scout.soft_delete', false) - && in_array(SoftDeletes::class, class_uses_recursive($model))) { + && in_array(SoftDeletes::class, class_uses_recursive($model), true)) { $settings = $engine->configureSoftDeleteFilter($settings); } diff --git a/src/scout/src/Console/QueueImportCommand.php b/src/scout/src/Console/QueueImportCommand.php index 3c6990183..5f22f6c93 100644 --- a/src/scout/src/Console/QueueImportCommand.php +++ b/src/scout/src/Console/QueueImportCommand.php @@ -6,6 +6,7 @@ use Hypervel\Config\Repository; use Hypervel\Console\Command; +use Hypervel\Database\Eloquent\Model; use Hypervel\Scout\Console\Traits\ResolvesScoutModelClass; use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Exceptions\ScoutException; @@ -45,7 +46,7 @@ public function handle(Repository $config): int { $class = $this->resolveModelClass((string) $this->argument('model')); - /** @var SearchableInterface $model */ + /** @var Model&SearchableInterface $model */ $model = new $class; $chunk = max(1, (int) ($this->option('chunk') ?? $config->integer('scout.chunk.searchable', 500))); @@ -67,8 +68,9 @@ public function handle(Repository $config): int /** * Dispatch range jobs for an integer-keyed model using min/max arithmetic. */ - protected function dispatchIntegerRange(string $class, SearchableInterface $model, int $chunk, string $order, ?string $queueName, ?string $connection): int + protected function dispatchIntegerRange(string $class, Model $model, int $chunk, string $order, ?string $queueName, ?string $connection): int { + /** @var Model&SearchableInterface $model */ $query = $class::makeAllSearchableQuery(); $keyName = $model->getScoutKeyName(); $qualified = $query->qualifyColumn($keyName); @@ -142,8 +144,9 @@ protected function dispatchIntegerRange(string $class, SearchableInterface $mode * dispatches one MakeRangeSearchable per chunk with the first/last keys * in that chunk. Workers re-query their range via whereBetween. */ - protected function dispatchStringRange(string $class, SearchableInterface $model, int $chunk, string $order, ?string $queueName, ?string $connection): int + protected function dispatchStringRange(string $class, Model $model, int $chunk, string $order, ?string $queueName, ?string $connection): int { + /** @var Model&SearchableInterface $model */ $query = $class::makeAllSearchableQuery(); $keyName = $model->getScoutKeyName(); $qualified = $query->qualifyColumn($keyName); diff --git a/src/scout/src/Console/SyncIndexSettingsCommand.php b/src/scout/src/Console/SyncIndexSettingsCommand.php index f07a4b915..0d721836c 100644 --- a/src/scout/src/Console/SyncIndexSettingsCommand.php +++ b/src/scout/src/Console/SyncIndexSettingsCommand.php @@ -69,7 +69,7 @@ public function handle(EngineManager $manager, Repository $config): int if ($model !== null && $config->boolean('scout.soft_delete', false) - && in_array(SoftDeletes::class, class_uses_recursive($model))) { + && in_array(SoftDeletes::class, class_uses_recursive($model), true)) { $settings = $engine->configureSoftDeleteFilter($settings); } diff --git a/src/scout/src/Contracts/SearchableInterface.php b/src/scout/src/Contracts/SearchableInterface.php index dd6b3bb8f..2f15901a1 100644 --- a/src/scout/src/Contracts/SearchableInterface.php +++ b/src/scout/src/Contracts/SearchableInterface.php @@ -7,36 +7,49 @@ use Closure; use Hypervel\Database\Eloquent\Builder as EloquentBuilder; use Hypervel\Database\Eloquent\Collection; +use Hypervel\Database\Eloquent\Model; use Hypervel\Scout\Builder; use Hypervel\Scout\Engines\Engine; /** - * Contract for models that can be indexed and searched. + * Internal shape for models that can be indexed and searched. * - * This interface defines the public API that searchable models must implement. - * The Searchable trait provides a default implementation of these methods. + * Application models gain this capability through the Searchable trait and do + * not need to implement this interface directly. Keep this shape compatible + * with the trait while widening collection boundaries to Model for engines. * - * @phpstan-require-extends \Hypervel\Database\Eloquent\Model + * @phpstan-require-extends Model */ interface SearchableInterface { /** * Perform a search against the model's indexed data. + * + * @return Builder */ public static function search(string $query = '', ?Closure $callback = null): Builder; /** * Get the requested models from an array of object IDs. + * + * @param array $ids + * @return Collection */ public function getScoutModelsByIds(Builder $builder, array $ids): Collection; /** * Get a query builder for retrieving the requested models from an array of object IDs. + * + * @param array $ids + * @return EloquentBuilder */ public function queryScoutModelsByIds(Builder $builder, array $ids): EloquentBuilder; /** * Modify the collection of models being made searchable. + * + * @param Collection $models + * @return Collection */ public function makeSearchableUsing(Collection $models): Collection; diff --git a/src/scout/src/Engines/AlgoliaEngine.php b/src/scout/src/Engines/AlgoliaEngine.php index aa5a5f77d..a3082b223 100644 --- a/src/scout/src/Engines/AlgoliaEngine.php +++ b/src/scout/src/Engines/AlgoliaEngine.php @@ -55,7 +55,7 @@ public function __construct( /** * Update the given models in the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models * @throws AlgoliaException */ public function update(EloquentCollection $models): void @@ -64,7 +64,7 @@ public function update(EloquentCollection $models): void return; } - /** @var Model&SearchableInterface $firstModel */ + /** @var EloquentCollection $models */ $firstModel = $models->first(); $index = $firstModel->indexableAs(); @@ -73,7 +73,6 @@ public function update(EloquentCollection $models): void } $objects = $models->map(function (Model $model) { - /** @var Model&SearchableInterface $model */ $searchableData = $model->toSearchableArray(); if (empty($searchableData)) { @@ -100,7 +99,7 @@ public function update(EloquentCollection $models): void /** * Remove the given models from the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ public function delete(EloquentCollection $models): void { @@ -108,12 +107,12 @@ public function delete(EloquentCollection $models): void return; } - /** @var Model&SearchableInterface $firstModel */ + /** @var EloquentCollection $models */ $firstModel = $models->first(); $keys = $models instanceof RemoveableScoutCollection ? $models->pluck($firstModel->getScoutKeyName()) - : $models->map(fn (SearchableInterface $model) => $model->getScoutKey()); + : $models->map(fn (Model $model) => $model->getScoutKey()); $this->algolia->deleteObjects($firstModel->indexableAs(), $keys->all()); } @@ -342,11 +341,10 @@ public function mapIds(mixed $results): Collection /** * Map the given results to instances of the given model. - * - * @param Model&SearchableInterface $model */ public function map(Builder $builder, mixed $results, Model $model): EloquentCollection { + /** @var Model&SearchableInterface $model */ if (count($results['hits']) === 0) { return $model->newCollection(); } @@ -356,13 +354,12 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var EloquentCollection $scoutModels */ $scoutModels = $model->getScoutModelsByIds($builder, $objectIds); + // Search engines serialize numeric Scout keys as strings. $mapped = $scoutModels - ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds)) + ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds, false)) ->map(function ($m) use ($results, $objectIdPositions) { - /** @var Model&SearchableInterface $m */ $result = $results['hits'][$objectIdPositions[$m->getScoutKey()]] ?? []; foreach ($result as $key => $value) { @@ -381,11 +378,10 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** * Map the given results to instances of the given model via a lazy collection. - * - * @param Model&SearchableInterface $model */ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCollection { + /** @var Model&SearchableInterface $model */ if (count($results['hits']) === 0) { return LazyCollection::empty(); } @@ -395,13 +391,11 @@ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var LazyCollection $cursor */ $cursor = $model->queryScoutModelsByIds($builder, $objectIds)->cursor(); return $cursor - ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds)) + ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds, false)) ->map(function ($m) use ($results, $objectIdPositions) { - /** @var Model&SearchableInterface $m */ $result = $results['hits'][$objectIdPositions[$m->getScoutKey()]] ?? []; foreach ($result as $key => $value) { @@ -426,11 +420,10 @@ public function getTotalCount(mixed $results): int /** * Flush all of the model's records from the engine. - * - * @param Model&SearchableInterface $model */ public function flush(Model $model): void { + /** @var Model&SearchableInterface $model */ $this->algolia->clearObjects($model->indexableAs()); } @@ -539,7 +532,7 @@ public function configureSoftDeleteFilter(array $settings = []): array */ protected function usesSoftDelete(Model $model): bool { - return in_array(SoftDeletes::class, class_uses_recursive($model)); + return in_array(SoftDeletes::class, class_uses_recursive($model), true); } /** diff --git a/src/scout/src/Engines/CollectionEngine.php b/src/scout/src/Engines/CollectionEngine.php index 32de4208e..e979fd3f9 100644 --- a/src/scout/src/Engines/CollectionEngine.php +++ b/src/scout/src/Engines/CollectionEngine.php @@ -115,7 +115,7 @@ protected function searchModels(Builder $builder): EloquentCollection ); }); - /** @var EloquentCollection $models */ + /** @var EloquentCollection $models */ $models = $this->ensureSoftDeletesAreHandled($builder, $query) ->get() ->values(); @@ -127,7 +127,6 @@ protected function searchModels(Builder $builder): EloquentCollection /** @var Model&SearchableInterface $firstModel */ $firstModel = $models->first(); - /** @var EloquentCollection $searchableModels */ $searchableModels = $firstModel->makeSearchableUsing($models); return $searchableModels @@ -179,7 +178,7 @@ protected function ensureSoftDeletesAreHandled(Builder $builder, EloquentBuilder return $query->onlyTrashed(); } - if (in_array(SoftDeletes::class, class_uses_recursive(get_class($builder->model))) + if (in_array(SoftDeletes::class, class_uses_recursive(get_class($builder->model)), true) && $this->getScoutConfig('soft_delete', false) ) { /* @phpstan-ignore method.notFound (SoftDeletingScope adds this method) */ @@ -206,11 +205,10 @@ public function mapIds(mixed $results): Collection /** * Map the given results to instances of the given model. - * - * @param Model&SearchableInterface $model */ public function map(Builder $builder, mixed $results, Model $model): EloquentCollection { + /** @var Model&SearchableInterface $model */ $results = $results['results']; if (count($results) === 0) { @@ -225,22 +223,21 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var EloquentCollection $scoutModels */ $scoutModels = $model->getScoutModelsByIds($builder, $objectIds); + // Scout keys may be cast differently between results and hydrated models. return $scoutModels - ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds)) + ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds, false)) ->sortBy(fn ($m) => $objectIdPositions[$m->getScoutKey()]) ->values(); } /** * Map the given results to instances of the given model via a lazy collection. - * - * @param Model&SearchableInterface $model */ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCollection { + /** @var Model&SearchableInterface $model */ $results = $results['results']; if (count($results) === 0) { @@ -255,11 +252,10 @@ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var LazyCollection $cursor */ $cursor = $model->queryScoutModelsByIds($builder, $objectIds)->cursor(); return $cursor - ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds)) + ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds, false)) ->sortBy(fn ($m) => $objectIdPositions[$m->getScoutKey()]) ->values(); } diff --git a/src/scout/src/Engines/DatabaseEngine.php b/src/scout/src/Engines/DatabaseEngine.php index ff76468bf..959ed1e42 100644 --- a/src/scout/src/Engines/DatabaseEngine.php +++ b/src/scout/src/Engines/DatabaseEngine.php @@ -203,9 +203,9 @@ protected function initializeSearchQuery( return $query->where(function (EloquentBuilder $query) use ($connectionType, $builder, $columns, $prefixColumns, $fullTextColumns): void { $canSearchPrimaryKey = ctype_digit((string) $builder->query) - && in_array($builder->model->getKeyType(), ['int', 'integer']) + && in_array($builder->model->getKeyType(), ['int', 'integer'], true) && ($connectionType !== 'pgsql' || (int) $builder->query <= PHP_INT_MAX) - && in_array($builder->model->getScoutKeyName(), $columns); + && in_array($builder->model->getScoutKeyName(), $columns, true); if ($canSearchPrimaryKey) { $query->orWhere($builder->model->getQualifiedKeyName(), $builder->query); @@ -214,7 +214,7 @@ protected function initializeSearchQuery( $likeOperator = $connectionType === 'pgsql' ? 'ilike' : 'like'; foreach ($columns as $column) { - if (in_array($column, $fullTextColumns)) { + if (in_array($column, $fullTextColumns, true)) { continue; } @@ -222,7 +222,7 @@ protected function initializeSearchQuery( continue; } - $pattern = in_array($column, $prefixColumns) + $pattern = in_array($column, $prefixColumns, true) ? $builder->query . '%' : '%' . $builder->query . '%'; @@ -341,7 +341,8 @@ protected function constrainForSoftDeletes(Builder $builder, EloquentBuilder $qu $usesSoftDeletes = in_array( SoftDeletes::class, - class_uses_recursive(get_class($builder->model)) + class_uses_recursive(get_class($builder->model)), + true ); if ($usesSoftDeletes && $this->getConfig('soft_delete', false)) { @@ -445,7 +446,6 @@ public function mapIds(mixed $results): Collection /** * Map the given results to instances of the given model. * - * @param Model&SearchableInterface $model * @return EloquentCollection */ public function map(Builder $builder, mixed $results, Model $model): EloquentCollection @@ -455,8 +455,6 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** * Map the given results to instances of the given model via a lazy collection. - * - * @param Model&SearchableInterface $model */ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCollection { @@ -479,7 +477,7 @@ public function getTotalCount(mixed $results): int * * The database engine doesn't need to update an external index. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ public function update(EloquentCollection $models): void { @@ -491,7 +489,7 @@ public function update(EloquentCollection $models): void * * The database engine doesn't need to remove from an external index. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ public function delete(EloquentCollection $models): void { @@ -500,8 +498,6 @@ public function delete(EloquentCollection $models): void /** * Flush all of the model's records from the engine. - * - * @param Model&SearchableInterface $model */ public function flush(Model $model): void { diff --git a/src/scout/src/Engines/Engine.php b/src/scout/src/Engines/Engine.php index 0be587c06..094ca7dce 100644 --- a/src/scout/src/Engines/Engine.php +++ b/src/scout/src/Engines/Engine.php @@ -7,7 +7,6 @@ use Hypervel\Database\Eloquent\Collection as EloquentCollection; use Hypervel\Database\Eloquent\Model; use Hypervel\Scout\Builder; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Support\Collection; use Hypervel\Support\LazyCollection; @@ -22,14 +21,14 @@ abstract class Engine /** * Update the given models in the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ abstract public function update(EloquentCollection $models): void; /** * Remove the given models from the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ abstract public function delete(EloquentCollection $models): void; @@ -50,15 +49,11 @@ abstract public function mapIds(mixed $results): Collection; /** * Map the given results to instances of the given model. - * - * @param Model&SearchableInterface $model */ abstract public function map(Builder $builder, mixed $results, Model $model): EloquentCollection; /** * Map the given results to instances of the given model via a lazy collection. - * - * @param Model&SearchableInterface $model */ abstract public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCollection; @@ -69,8 +64,6 @@ abstract public function getTotalCount(mixed $results): int; /** * Flush all of the model's records from the engine. - * - * @param Model&SearchableInterface $model */ abstract public function flush(Model $model): void; diff --git a/src/scout/src/Engines/MeilisearchEngine.php b/src/scout/src/Engines/MeilisearchEngine.php index ea861cd00..9caed2ffe 100644 --- a/src/scout/src/Engines/MeilisearchEngine.php +++ b/src/scout/src/Engines/MeilisearchEngine.php @@ -54,7 +54,7 @@ public function __construct( /** * Update the given models in the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models * @throws ApiException */ public function update(EloquentCollection $models): void @@ -63,7 +63,7 @@ public function update(EloquentCollection $models): void return; } - /** @var Model&SearchableInterface $firstModel */ + /** @var EloquentCollection $models */ $firstModel = $models->first(); $index = $this->meilisearch->index($firstModel->indexableAs()); @@ -72,7 +72,6 @@ public function update(EloquentCollection $models): void } $objects = $models->map(function (Model $model) { - /** @var Model&SearchableInterface $model */ $searchableData = $model->toSearchableArray(); if (empty($searchableData)) { @@ -99,7 +98,7 @@ public function update(EloquentCollection $models): void /** * Remove the given models from the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ public function delete(EloquentCollection $models): void { @@ -107,13 +106,13 @@ public function delete(EloquentCollection $models): void return; } - /** @var Model&SearchableInterface $firstModel */ + /** @var EloquentCollection $models */ $firstModel = $models->first(); $index = $this->meilisearch->index($firstModel->indexableAs()); $keys = $models instanceof RemoveableScoutCollection ? $models->pluck($firstModel->getScoutKeyName())->values()->all() - : $models->map(fn (SearchableInterface $model) => $model->getScoutKey())->values()->all(); + : $models->map(fn (Model $model) => $model->getScoutKey())->values()->all(); $index->deleteDocuments($keys); } @@ -310,11 +309,10 @@ public function keys(Builder $builder): Collection /** * Map the given results to instances of the given model. - * - * @param Model&SearchableInterface $model */ public function map(Builder $builder, mixed $results, Model $model): EloquentCollection { + /** @var Model&SearchableInterface $model */ if ($results === null || count($results['hits']) === 0) { return $model->newCollection(); } @@ -327,13 +325,12 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var EloquentCollection $scoutModels */ $scoutModels = $model->getScoutModelsByIds($builder, $objectIds); + // Search engines serialize numeric Scout keys as strings. $mapped = $scoutModels - ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds)) + ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds, false)) ->map(function ($m) use ($results, $objectIdPositions) { - /** @var Model&SearchableInterface $m */ $result = $results['hits'][$objectIdPositions[$m->getScoutKey()]] ?? []; foreach ($result as $key => $value) { @@ -352,11 +349,10 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** * Map the given results to instances of the given model via a lazy collection. - * - * @param Model&SearchableInterface $model */ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCollection { + /** @var Model&SearchableInterface $model */ if (count($results['hits']) === 0) { return LazyCollection::empty(); } @@ -369,13 +365,11 @@ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var LazyCollection $cursor */ $cursor = $model->queryScoutModelsByIds($builder, $objectIds)->cursor(); return $cursor - ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds)) + ->filter(fn ($m) => in_array($m->getScoutKey(), $objectIds, false)) ->map(function ($m) use ($results, $objectIdPositions) { - /** @var Model&SearchableInterface $m */ $result = $results['hits'][$objectIdPositions[$m->getScoutKey()]] ?? []; foreach ($result as $key => $value) { @@ -400,11 +394,10 @@ public function getTotalCount(mixed $results): int /** * Flush all of the model's records from the engine. - * - * @param Model&SearchableInterface $model */ public function flush(Model $model): void { + /** @var Model&SearchableInterface $model */ $index = $this->meilisearch->index($model->indexableAs()); $index->deleteAllDocuments(); @@ -588,7 +581,7 @@ public function generateTenantToken( */ protected function usesSoftDelete(Model $model): bool { - return in_array(SoftDeletes::class, class_uses_recursive($model)); + return in_array(SoftDeletes::class, class_uses_recursive($model), true); } /** diff --git a/src/scout/src/Engines/TypesenseEngine.php b/src/scout/src/Engines/TypesenseEngine.php index 0c5a0e9e5..5f6c0c8cb 100644 --- a/src/scout/src/Engines/TypesenseEngine.php +++ b/src/scout/src/Engines/TypesenseEngine.php @@ -49,7 +49,7 @@ public function __construct( /** * Update the given models in the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models * @throws TypesenseClientError */ public function update(EloquentCollection $models): void @@ -58,7 +58,7 @@ public function update(EloquentCollection $models): void return; } - /** @var Model&SearchableInterface $firstModel */ + /** @var EloquentCollection $models */ $firstModel = $models->first(); if ($this->usesSoftDelete($firstModel) && $this->getConfig('soft_delete', false)) { @@ -66,7 +66,6 @@ public function update(EloquentCollection $models): void } $objects = $models->map(function (Model $model): ?array { - /** @var Model&SearchableInterface $model */ $searchableData = $model->toSearchableArray(); if (empty($searchableData)) { @@ -149,13 +148,13 @@ protected function createImportSortingDataObject(array $document): stdClass /** * Remove the given models from the search index. * - * @param EloquentCollection $models + * @param EloquentCollection $models * @throws TypesenseClientError */ public function delete(EloquentCollection $models): void { + /** @var EloquentCollection $models */ $models->each(function (Model $model) use ($models): void { - /** @var Model&SearchableInterface $model */ $modelId = $models instanceof RemoveableScoutCollection ? $model->getAttribute($model->getScoutKeyName()) : $model->getScoutKey(); @@ -495,11 +494,11 @@ public function mapIds(mixed $results): Collection /** * Map the given results to instances of the given model. * - * @param Model&SearchableInterface $model * @return EloquentCollection */ public function map(Builder $builder, mixed $results, Model $model): EloquentCollection { + /** @var Model&SearchableInterface $model */ if ($this->getTotalCount($results) === 0) { return $model->newCollection(); } @@ -520,16 +519,13 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** @var array $objectIds */ $objectIdPositions = array_flip($objectIds); - /** @var EloquentCollection $scoutModels */ $scoutModels = $model->getScoutModelsByIds($builder, $objectIds); return $scoutModels ->filter(static function (Model $m) use ($objectIds): bool { - /** @var Model&SearchableInterface $m */ return in_array($m->getScoutKey(), $objectIds, false); }) ->sortBy(static function (Model $m) use ($objectIdPositions): int { - /** @var Model&SearchableInterface $m */ return $objectIdPositions[$m->getScoutKey()]; }) ->values(); @@ -537,11 +533,10 @@ public function map(Builder $builder, mixed $results, Model $model): EloquentCol /** * Map the given results to instances of the given model via a lazy collection. - * - * @param Model&SearchableInterface $model */ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCollection { + /** @var Model&SearchableInterface $model */ if ((int) ($results['found'] ?? 0) === 0) { return LazyCollection::empty(); } @@ -557,11 +552,9 @@ public function lazyMap(Builder $builder, mixed $results, Model $model): LazyCol return $model->queryScoutModelsByIds($builder, $objectIds) ->cursor() ->filter(static function (Model $m) use ($objectIds): bool { - /** @var Model&SearchableInterface $m */ return in_array($m->getScoutKey(), $objectIds, false); }) ->sortBy(static function (Model $m) use ($objectIdPositions): int { - /** @var Model&SearchableInterface $m */ return $objectIdPositions[$m->getScoutKey()]; }) ->values(); @@ -578,11 +571,11 @@ public function getTotalCount(mixed $results): int /** * Flush all of the model's records from the engine. * - * @param Model&SearchableInterface $model * @throws TypesenseClientError */ public function flush(Model $model): void { + /** @var Model&SearchableInterface $model */ try { $this->collection($model->indexableAs())->delete(); } catch (ObjectNotFound) { diff --git a/src/scout/src/Events/ModelsFlushed.php b/src/scout/src/Events/ModelsFlushed.php index 8552389e3..c33df7866 100644 --- a/src/scout/src/Events/ModelsFlushed.php +++ b/src/scout/src/Events/ModelsFlushed.php @@ -6,12 +6,11 @@ use Hypervel\Database\Eloquent\Collection; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; /** * Event fired when models are flushed from the search index. * - * @template TModel of Model&SearchableInterface + * @template TModel of Model */ class ModelsFlushed { diff --git a/src/scout/src/Events/ModelsImported.php b/src/scout/src/Events/ModelsImported.php index 5bad87985..ff9754382 100644 --- a/src/scout/src/Events/ModelsImported.php +++ b/src/scout/src/Events/ModelsImported.php @@ -6,12 +6,11 @@ use Hypervel\Database\Eloquent\Collection; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; /** * Event fired when models are imported to the search index. * - * @template TModel of Model&SearchableInterface + * @template TModel of Model */ class ModelsImported { diff --git a/src/scout/src/Jobs/MakeSearchable.php b/src/scout/src/Jobs/MakeSearchable.php index c6d931a04..baa7731b4 100644 --- a/src/scout/src/Jobs/MakeSearchable.php +++ b/src/scout/src/Jobs/MakeSearchable.php @@ -22,7 +22,7 @@ class MakeSearchable implements ShouldQueue /** * Create a new job instance. * - * @param Collection $models + * @param Collection $models */ public function __construct( public Collection $models diff --git a/src/scout/src/Jobs/RemoveFromSearch.php b/src/scout/src/Jobs/RemoveFromSearch.php index b9c371d2a..4ee382b24 100644 --- a/src/scout/src/Jobs/RemoveFromSearch.php +++ b/src/scout/src/Jobs/RemoveFromSearch.php @@ -28,7 +28,7 @@ class RemoveFromSearch implements ShouldQueue /** * Create a new job instance. * - * @param Collection $models + * @param Collection $models */ public function __construct(Collection $models) { diff --git a/src/scout/src/Jobs/RemoveableScoutCollection.php b/src/scout/src/Jobs/RemoveableScoutCollection.php index 42617fdb8..915aca2b9 100644 --- a/src/scout/src/Jobs/RemoveableScoutCollection.php +++ b/src/scout/src/Jobs/RemoveableScoutCollection.php @@ -16,7 +16,7 @@ * rather than their database IDs, as the models may already be deleted. * * @template TKey of array-key - * @template TModel of Model&SearchableInterface + * @template TModel of Model * @extends Collection */ class RemoveableScoutCollection extends Collection @@ -34,8 +34,11 @@ public function getQueueableIds(): array $first = $this->first(); - if (in_array(Searchable::class, class_uses_recursive($first))) { - return $this->map(fn (SearchableInterface $model) => $model->getScoutKey())->all(); + if (in_array(Searchable::class, class_uses_recursive($first), true)) { + return $this->map(function (Model $model) { + /** @var Model&SearchableInterface $model */ + return $model->getScoutKey(); + })->all(); } return parent::getQueueableIds(); diff --git a/src/scout/src/ModelObserver.php b/src/scout/src/ModelObserver.php index e5205c3b5..257496ebe 100644 --- a/src/scout/src/ModelObserver.php +++ b/src/scout/src/ModelObserver.php @@ -189,6 +189,6 @@ protected function whileForcingUpdate(Closure $callback): mixed */ protected function usesSoftDelete(Model $model): bool { - return in_array(SoftDeletes::class, class_uses_recursive($model)); + return in_array(SoftDeletes::class, class_uses_recursive($model), true); } } diff --git a/src/scout/src/Scout.php b/src/scout/src/Scout.php index 5445303e1..30716183c 100644 --- a/src/scout/src/Scout.php +++ b/src/scout/src/Scout.php @@ -8,7 +8,6 @@ use Hypervel\Context\CoroutineContext; use Hypervel\Database\Eloquent\Collection as EloquentCollection; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Engines\Engine; use Hypervel\Scout\Jobs\MakeSearchable; use Hypervel\Scout\Jobs\RemoveFromSearch; @@ -262,7 +261,7 @@ public static function isImporting(): bool /** * Run the given callback with an import progress reporter in the current coroutine. * - * @param callable(EloquentCollection): void $reporter + * @param callable(EloquentCollection): void $reporter */ public static function whileReportingImportProgress(callable $reporter, callable $callback): mixed { @@ -285,7 +284,7 @@ public static function whileReportingImportProgress(callable $reporter, callable /** * Report imported models to the current coroutine's scout:import progress reporter. * - * @param EloquentCollection $models + * @param EloquentCollection $models */ public static function reportImportProgress(EloquentCollection $models): void { diff --git a/src/scout/src/Searchable.php b/src/scout/src/Searchable.php index c8a9e1e2d..0a4cea816 100644 --- a/src/scout/src/Searchable.php +++ b/src/scout/src/Searchable.php @@ -350,6 +350,9 @@ public function wasSearchableBeforeDelete(): bool /** * Get the requested models from an array of object IDs. + * + * @param array $ids + * @return Collection */ public function getScoutModelsByIds(Builder $builder, array $ids): Collection { @@ -358,6 +361,9 @@ public function getScoutModelsByIds(Builder $builder, array $ids): Collection /** * Get a query builder for retrieving the requested models from an array of object IDs. + * + * @param array $ids + * @return EloquentBuilder */ public function queryScoutModelsByIds(Builder $builder, array $ids): EloquentBuilder { @@ -369,7 +375,7 @@ public function queryScoutModelsByIds(Builder $builder, array $ids): EloquentBui call_user_func($builder->queryCallback, $query); } - $whereIn = in_array($this->getScoutKeyType(), ['int', 'integer']) + $whereIn = in_array($this->getScoutKeyType(), ['int', 'integer'], true) ? 'whereIntegerInRaw' : 'whereIn'; @@ -578,7 +584,7 @@ public static function waitForSearchableJobs(): void */ protected static function usesSoftDelete(): bool { - return in_array(SoftDeletes::class, class_uses_recursive(static::class)); + return in_array(SoftDeletes::class, class_uses_recursive(static::class), true); } /** diff --git a/src/scout/src/SearchableScope.php b/src/scout/src/SearchableScope.php index 6fec11a27..bbbc52dd6 100644 --- a/src/scout/src/SearchableScope.php +++ b/src/scout/src/SearchableScope.php @@ -40,9 +40,14 @@ public function extend(EloquentBuilder $builder): void $chunkSize = $chunk ?? config('scout.chunk.searchable', 500); $builder->chunkById($chunkSize, function (Collection $models) { - /** @var EloquentCollection $models */ - /* @phpstan-ignore method.notFound (searchable() added via Searchable trait) */ - $models->filter(fn ($m) => $m->shouldBeSearchable())->searchable(); + /** @var EloquentCollection $models */ + $searchableModels = $models->filter(function (Model $model): bool { + /** @var Model&SearchableInterface $model */ + return $model->shouldBeSearchable(); + }); + + /* @phpstan-ignore method.notFound (searchable() macro is registered by the Searchable trait) */ + $searchableModels->searchable(); // @phpstan-ignore staticMethod.notFound (local macros retain their lexical class scope at runtime) static::dispatchEvent(ModelsImported::class, $models); @@ -57,8 +62,8 @@ public function extend(EloquentBuilder $builder): void $chunkSize = $chunk ?? config('scout.chunk.unsearchable', 500); $builder->chunkById($chunkSize, function (Collection $models) { - /** @var EloquentCollection $models */ - /* @phpstan-ignore method.notFound (unsearchable() added via Searchable trait) */ + /** @var EloquentCollection $models */ + /* @phpstan-ignore method.notFound (unsearchable() macro is registered by the Searchable trait) */ $models->unsearchable(); // @phpstan-ignore staticMethod.notFound (local macros retain their lexical class scope at runtime) diff --git a/src/scout/src/Traits/UniqueByScoutKeys.php b/src/scout/src/Traits/UniqueByScoutKeys.php index 8863efc0b..e467a6312 100644 --- a/src/scout/src/Traits/UniqueByScoutKeys.php +++ b/src/scout/src/Traits/UniqueByScoutKeys.php @@ -23,7 +23,8 @@ public function uniqueId(): string { return hash('sha256', json_encode([ $this->models->getQueueableClass(), - $this->models->map(function (Model&SearchableInterface $model) { + $this->models->map(function (Model $model) { + /** @var Model&SearchableInterface $model */ return $model->getScoutKey(); })->sort()->values()->all(), ], JSON_THROW_ON_ERROR)); diff --git a/tests/Scout/Feature/CoroutineSafetyTest.php b/tests/Scout/Feature/CoroutineSafetyTest.php index f008a2081..db546da62 100644 --- a/tests/Scout/Feature/CoroutineSafetyTest.php +++ b/tests/Scout/Feature/CoroutineSafetyTest.php @@ -8,7 +8,6 @@ use Hypervel\Coroutine\WaitGroup; use Hypervel\Database\Eloquent\Collection; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Jobs\MakeSearchable; use Hypervel\Scout\Jobs\RemoveFromSearch; use Hypervel\Scout\ModelObserver; @@ -485,7 +484,7 @@ public function testWhileImportingIsNestingSafe(): void } } -class ForceSavingSearchableModel extends Model implements SearchableInterface +class ForceSavingSearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Feature/SearchableScopeTest.php b/tests/Scout/Feature/SearchableScopeTest.php index 84b598a60..190bd340b 100644 --- a/tests/Scout/Feature/SearchableScopeTest.php +++ b/tests/Scout/Feature/SearchableScopeTest.php @@ -7,7 +7,6 @@ use Hypervel\Database\Eloquent\Collection; use Hypervel\Database\Eloquent\Model; use Hypervel\Database\Eloquent\Relations\HasManyThrough; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Events\ModelsFlushed; use Hypervel\Scout\Events\ModelsImported; use Hypervel\Scout\Scout; @@ -236,7 +235,7 @@ class ScoutThroughIntermediate extends Model protected array $guarded = []; } -class ScoutThroughSearchableModel extends Model implements SearchableInterface +class ScoutThroughSearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Models/ConditionalSearchableModel.php b/tests/Scout/Models/ConditionalSearchableModel.php index 0e2e597eb..03b620437 100644 --- a/tests/Scout/Models/ConditionalSearchableModel.php +++ b/tests/Scout/Models/ConditionalSearchableModel.php @@ -5,13 +5,12 @@ namespace Hypervel\Tests\Scout\Models; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model that uses shouldBeSearchable() for conditional indexing. */ -class ConditionalSearchableModel extends Model implements SearchableInterface +class ConditionalSearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Models/ConfigBasedTypesenseModel.php b/tests/Scout/Models/ConfigBasedTypesenseModel.php index a1f7aad2a..7e86167d3 100644 --- a/tests/Scout/Models/ConfigBasedTypesenseModel.php +++ b/tests/Scout/Models/ConfigBasedTypesenseModel.php @@ -5,7 +5,6 @@ namespace Hypervel\Tests\Scout\Models; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** @@ -14,7 +13,7 @@ * This model does NOT define typesenseCollectionSchema() or typesenseSearchParameters(), * so the engine must read settings from config. */ -class ConfigBasedTypesenseModel extends Model implements SearchableInterface +class ConfigBasedTypesenseModel extends Model { use Searchable; diff --git a/tests/Scout/Models/CustomScoutKeyModel.php b/tests/Scout/Models/CustomScoutKeyModel.php index 791099c08..11d28c42f 100644 --- a/tests/Scout/Models/CustomScoutKeyModel.php +++ b/tests/Scout/Models/CustomScoutKeyModel.php @@ -5,13 +5,12 @@ namespace Hypervel\Tests\Scout\Models; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model with a custom Scout key. */ -class CustomScoutKeyModel extends Model implements SearchableInterface +class CustomScoutKeyModel extends Model { use Searchable; diff --git a/tests/Scout/Models/FilteringSearchableModel.php b/tests/Scout/Models/FilteringSearchableModel.php index a57677beb..2c00edab6 100644 --- a/tests/Scout/Models/FilteringSearchableModel.php +++ b/tests/Scout/Models/FilteringSearchableModel.php @@ -6,13 +6,12 @@ use Hypervel\Database\Eloquent\Collection; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model that filters models in makeSearchableUsing(). */ -class FilteringSearchableModel extends Model implements SearchableInterface +class FilteringSearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Models/PrefixSearchableModel.php b/tests/Scout/Models/PrefixSearchableModel.php index d7dd8191d..a8418443d 100644 --- a/tests/Scout/Models/PrefixSearchableModel.php +++ b/tests/Scout/Models/PrefixSearchableModel.php @@ -6,13 +6,12 @@ use Hypervel\Database\Eloquent\Model; use Hypervel\Scout\Attributes\SearchUsingPrefix; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model that uses prefix search on the title column. */ -class PrefixSearchableModel extends Model implements SearchableInterface +class PrefixSearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Models/SearchableModel.php b/tests/Scout/Models/SearchableModel.php index d322c4b76..54491f6db 100644 --- a/tests/Scout/Models/SearchableModel.php +++ b/tests/Scout/Models/SearchableModel.php @@ -5,13 +5,12 @@ namespace Hypervel\Tests\Scout\Models; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model for Scout tests. */ -class SearchableModel extends Model implements SearchableInterface +class SearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Models/SoftDeletableSearchableModel.php b/tests/Scout/Models/SoftDeletableSearchableModel.php index 971f1b015..862dcec98 100644 --- a/tests/Scout/Models/SoftDeletableSearchableModel.php +++ b/tests/Scout/Models/SoftDeletableSearchableModel.php @@ -6,13 +6,12 @@ use Hypervel\Database\Eloquent\Model; use Hypervel\Database\Eloquent\SoftDeletes; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model with soft deletes for Scout tests. */ -class SoftDeletableSearchableModel extends Model implements SearchableInterface +class SoftDeletableSearchableModel extends Model { use Searchable; use SoftDeletes; diff --git a/tests/Scout/Models/SoftDeleteSearchableModel.php b/tests/Scout/Models/SoftDeleteSearchableModel.php index 0baeea8bc..8569f98a3 100644 --- a/tests/Scout/Models/SoftDeleteSearchableModel.php +++ b/tests/Scout/Models/SoftDeleteSearchableModel.php @@ -6,13 +6,12 @@ use Hypervel\Database\Eloquent\Model; use Hypervel\Database\Eloquent\SoftDeletes; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * Test model for Scout soft delete integration tests. */ -class SoftDeleteSearchableModel extends Model implements SearchableInterface +class SoftDeleteSearchableModel extends Model { use Searchable; use SoftDeletes; diff --git a/tests/Scout/Models/TypesenseSearchableModel.php b/tests/Scout/Models/TypesenseSearchableModel.php index e1fa3b79b..bb41f5baf 100644 --- a/tests/Scout/Models/TypesenseSearchableModel.php +++ b/tests/Scout/Models/TypesenseSearchableModel.php @@ -5,7 +5,6 @@ namespace Hypervel\Tests\Scout\Models; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** @@ -13,7 +12,7 @@ * * Includes typesenseCollectionSchema() for proper schema definition. */ -class TypesenseSearchableModel extends Model implements SearchableInterface +class TypesenseSearchableModel extends Model { use Searchable; diff --git a/tests/Scout/Models/TypesenseSoftDeleteSearchableModel.php b/tests/Scout/Models/TypesenseSoftDeleteSearchableModel.php index e1d9c6c4d..703972c20 100644 --- a/tests/Scout/Models/TypesenseSoftDeleteSearchableModel.php +++ b/tests/Scout/Models/TypesenseSoftDeleteSearchableModel.php @@ -6,7 +6,6 @@ use Hypervel\Database\Eloquent\Model; use Hypervel\Database\Eloquent\SoftDeletes; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** @@ -14,7 +13,7 @@ * * Includes typesenseCollectionSchema() with __soft_deleted field. */ -class TypesenseSoftDeleteSearchableModel extends Model implements SearchableInterface +class TypesenseSoftDeleteSearchableModel extends Model { use Searchable; use SoftDeletes; diff --git a/tests/Scout/Models/UuidSearchableModel.php b/tests/Scout/Models/UuidSearchableModel.php index 6df8e8f59..0abc3282e 100644 --- a/tests/Scout/Models/UuidSearchableModel.php +++ b/tests/Scout/Models/UuidSearchableModel.php @@ -6,13 +6,12 @@ use Hypervel\Database\Eloquent\Concerns\HasUuids; use Hypervel\Database\Eloquent\Model; -use Hypervel\Scout\Contracts\SearchableInterface; use Hypervel\Scout\Searchable; /** * UUID7-keyed test fixture for the string-key path in scout:queue-import. */ -class UuidSearchableModel extends Model implements SearchableInterface +class UuidSearchableModel extends Model { use HasUuids; use Searchable; diff --git a/tests/Scout/Unit/Engines/AlgoliaEngineTest.php b/tests/Scout/Unit/Engines/AlgoliaEngineTest.php index 4f617e337..e26180c96 100644 --- a/tests/Scout/Unit/Engines/AlgoliaEngineTest.php +++ b/tests/Scout/Unit/Engines/AlgoliaEngineTest.php @@ -1300,7 +1300,7 @@ protected function createSoftDeleteSearchableModelMock(): m\MockInterface /** * Test model for AlgoliaEngine tests. */ -class AlgoliaTestSearchableModel extends Model implements SearchableInterface +class AlgoliaTestSearchableModel extends Model { use Searchable; @@ -1312,7 +1312,7 @@ class AlgoliaTestSearchableModel extends Model implements SearchableInterface /** * Test model with soft deletes for AlgoliaEngine tests. */ -class AlgoliaTestSoftDeleteModel extends Model implements SearchableInterface +class AlgoliaTestSoftDeleteModel extends Model { use Searchable; use SoftDeletes; diff --git a/tests/Scout/Unit/Engines/MeilisearchEngineTest.php b/tests/Scout/Unit/Engines/MeilisearchEngineTest.php index b3b92f68f..e80b469c6 100644 --- a/tests/Scout/Unit/Engines/MeilisearchEngineTest.php +++ b/tests/Scout/Unit/Engines/MeilisearchEngineTest.php @@ -1145,7 +1145,7 @@ protected function createSoftDeleteSearchableModelMock(): m\MockInterface /** * Test model for MeilisearchEngine tests. */ -class MeilisearchTestSearchableModel extends Model implements SearchableInterface +class MeilisearchTestSearchableModel extends Model { use Searchable; @@ -1157,7 +1157,7 @@ class MeilisearchTestSearchableModel extends Model implements SearchableInterfac /** * Test model with soft deletes for MeilisearchEngine tests. */ -class MeilisearchTestSoftDeleteModel extends Model implements SearchableInterface +class MeilisearchTestSoftDeleteModel extends Model { use Searchable; use SoftDeletes; diff --git a/tests/Scout/Unit/Engines/TypesenseEngineTest.php b/tests/Scout/Unit/Engines/TypesenseEngineTest.php index af8738ed7..6fb832d5e 100644 --- a/tests/Scout/Unit/Engines/TypesenseEngineTest.php +++ b/tests/Scout/Unit/Engines/TypesenseEngineTest.php @@ -470,8 +470,10 @@ public function testDeleteUsesStoredScoutKeyFromRemoveableCollection(): void $client = m::mock(TypesenseClient::class); $client->shouldReceive('getCollections')->once()->andReturn($collections); - $model = new TypesenseLifecycleModel; - $model->setRawAttributes(['scout_id' => 'stored-scout-key']); + $model = $this->createSearchableModelMock(); + $model->shouldReceive('getScoutKeyName')->andReturn('scout_id'); + $model->shouldReceive('getAttribute')->with('scout_id')->andReturn('stored-scout-key'); + $model->shouldReceive('indexableAs')->andReturn('write_index'); $this->createEngine($client)->delete(new RemoveableScoutCollection([$model])); }