92 lines
3.6 KiB
PHP
92 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace Modules\Core\Catalog\Services;
|
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Lunar\Models\Collection;
|
|
use Lunar\Models\Product;
|
|
use Lunar\Search\CollectionIndexer as BaseCollectionIndexer;
|
|
|
|
/**
|
|
* Extends Lunar's own indexer so Modules\Core\Catalog\Services\CollectionService can
|
|
* serve category browsing/nav AND single-collection lookups from Meilisearch alone,
|
|
* the same reasoning as Modules\Core\Catalog\Services\ProductIndexer. Lunar's base
|
|
* indexer only carries `id`/`name`/`created_at` — nowhere near enough for a storefront
|
|
* category page or a nav tree. Adds:
|
|
* - parent_id, _lft, _rgt (filterable/sortable) — the nested-set tree position, so
|
|
* CollectionService can resolve "children of X" or build a full tree without a
|
|
* database read
|
|
* - collection_group_id (filterable) — mirrors Collection::scopeInGroup()
|
|
* - slugs (filterable) — every locale's Url::slug, so getBySlug() resolves from the
|
|
* index directly, no database read
|
|
* - thumbnail (display) — the collection's thumbnail image URL
|
|
* - ancestors (display) — [{id, name}, ...] ordered root-first, so a breadcrumb can
|
|
* render directly from a single indexed document with zero extra queries
|
|
* - product_count (display) — how many products are in this collection or any of
|
|
* its descendants, read from the *product* Meilisearch index at collection-index
|
|
* time (via `collection_ids`, see Modules\Core\Catalog\Services\ProductIndexer) —
|
|
* matches what ProductService::list(ProductFilters(collectionId: ...)) would
|
|
* return, not just direct assignment. Reflects the product index's state as of
|
|
* the last collection reindex, so re-run `lunar:search:index --refresh` after a
|
|
* product reindex if this needs to be current.
|
|
*
|
|
* New fields aren't filterable/sortable in Meilisearch until `php artisan
|
|
* lunar:meilisearch:setup` re-syncs index settings, and existing documents need
|
|
* `lunar:search:index --refresh` to pick up the new shape.
|
|
*/
|
|
class CollectionIndexer extends BaseCollectionIndexer
|
|
{
|
|
public function getFilterableFields(): array
|
|
{
|
|
return [
|
|
...parent::getFilterableFields(),
|
|
'id',
|
|
'parent_id',
|
|
'_lft',
|
|
'collection_group_id',
|
|
'slugs',
|
|
];
|
|
}
|
|
|
|
public function getSortableFields(): array
|
|
{
|
|
return [
|
|
...parent::getSortableFields(),
|
|
'_lft',
|
|
];
|
|
}
|
|
|
|
public function makeAllSearchableUsing(Builder $query): Builder
|
|
{
|
|
return parent::makeAllSearchableUsing($query)->with(['urls', 'media', 'ancestors']);
|
|
}
|
|
|
|
public function toSearchableArray(Model $model): array
|
|
{
|
|
/** @var Collection $model */
|
|
$data = parent::toSearchableArray($model);
|
|
|
|
$data['parent_id'] = $model->parent_id;
|
|
$data['_lft'] = $model->_lft;
|
|
$data['_rgt'] = $model->_rgt;
|
|
$data['collection_group_id'] = $model->collection_group_id;
|
|
$data['slugs'] = $model->urls->pluck('slug')->unique()->values()->all();
|
|
$data['thumbnail'] = $model->getThumbnailImage() ?: null;
|
|
$data['ancestors'] = $model->ancestors
|
|
->sortBy('_lft')
|
|
->map(fn ($ancestor) => [
|
|
'id' => $ancestor->id,
|
|
'name' => $ancestor->translateAttribute('name'),
|
|
])
|
|
->values()
|
|
->all();
|
|
$data['product_count'] = Product::search('')
|
|
->options(['filter' => "collection_ids = \"{$model->id}\""])
|
|
->paginateRaw(perPage: 1, page: 1)
|
|
->total();
|
|
|
|
return $data;
|
|
}
|
|
}
|