Feat: Restructuring Products into the Concern Catalog, for future Collection services

This commit is contained in:
2026-08-27 11:42:13 +03:00
parent 63caaf55c7
commit ba5a9523c8
20 changed files with 797 additions and 29 deletions
+11 -11
View File
@@ -1,10 +1,10 @@
# Product Listing
`Modules\Core\Product\Services\ProductService` provides catalog browsing/filtering AND single-product
`Modules\Core\Catalog\Services\ProductService` provides catalog browsing/filtering AND single-product
lookup for a storefront — `list()`, `getById()`, `getBySlug()` — all reading directly from the
Meilisearch index rather than the database. One data source for everything this service does.
This is separate from `Modules\Core\Product\Services\ProductSearchService` (see `product-search.md`), which
This is separate from `Modules\Core\Catalog\Services\ProductSearchService` (see `product-search.md`), which
handles free-text query search. `ProductService` is for browsing/lookup without a search term.
---
@@ -14,7 +14,7 @@ handles free-text query search. `ProductService` is for browsing/lookup without
Every method here reads Meilisearch documents directly and returns plain arrays — never Scout's
`->get()`, which would re-hydrate Eloquent models from the database. This means the index has to
carry everything a detail page needs (variants, prices, options, media, reviews — see below), not
just the trimmed fields a listing page needs. `Modules\Core\Product\Services\ProductIndexer` is built to
just the trimmed fields a listing page needs. `Modules\Core\Catalog\Services\ProductIndexer` is built to
carry that full shape.
---
@@ -22,9 +22,9 @@ carry that full shape.
## Usage
```php
use Modules\Core\Product\DTOs\ProductFilters;
use Modules\Core\Product\Services\ProductService;
use Modules\Core\Product\Enums\ProductSort;
use Modules\Core\Catalog\DTOs\ProductFilters;
use Modules\Core\Catalog\Services\ProductService;
use Modules\Core\Catalog\Enums\ProductSort;
$service = app(ProductService::class);
@@ -62,11 +62,11 @@ All `ProductFilters` fields are optional; only the ones set are added to the Mei
---
## Fields this depends on: `Modules\Core\Product\Services\ProductIndexer`
## Fields this depends on: `Modules\Core\Catalog\Services\ProductIndexer`
Lunar's own `Lunar\Search\ProductIndexer` only carries listing-grade fields (name, description,
status, brand, a single thumbnail, skus) and marks just `__soft_deleted`, `skus`, `status` as
filterable. `Modules\Core\Product\Services\ProductIndexer` extends it to add everything `ProductService`
filterable. `Modules\Core\Catalog\Services\ProductIndexer` extends it to add everything `ProductService`
needs, listing and detail alike:
| Field | Source | Notes |
@@ -149,9 +149,9 @@ variants don't.
## Sorting
`ProductSort` (`Modules\Core\Product\Enums\ProductSort`) is a fixed enum of supported sort orders —
`ProductSort` (`Modules\Core\Catalog\Enums\ProductSort`) is a fixed enum of supported sort orders —
`PriceAsc`, `PriceDesc`, `Newest` — each mapping to a Meilisearch `sort` clause against a field
`Modules\Core\Product\Services\ProductIndexer::getSortableFields()` marks sortable (`price`, plus
`Modules\Core\Catalog\Services\ProductIndexer::getSortableFields()` marks sortable (`price`, plus
`created_at`/`updated_at`/`skus`/`status` inherited from Lunar's base indexer). Adding a new
`ProductSort` case requires adding the matching field to `getSortableFields()` and re-syncing (see
below) — sortable attributes are index settings, not computed per-query, same as filterable ones.
@@ -168,7 +168,7 @@ Not automatic — an app opts in via its own `config/lunar/search.php`:
```php
'indexers' => [
Lunar\Models\Product::class => Modules\Core\Product\Services\ProductIndexer::class,
Lunar\Models\Product::class => Modules\Core\Catalog\Services\ProductIndexer::class,
// ...other model indexers unchanged
],
```