Feat: Adding tags to product filters

This commit is contained in:
2026-09-03 13:34:04 +03:00
parent c439299144
commit 215f43f3ef
5 changed files with 48 additions and 12 deletions
+1
View File
@@ -90,6 +90,7 @@ class ProductIndexer extends BaseProductIndexer
'in_stock',
'recommendations.id',
'skus',
'tags',
];
}
+26 -4
View File
@@ -78,8 +78,27 @@ class ProductService
);
$priceBounds = $this->priceSliderBounds($filters, $filters?->minPrice, $filters?->maxPrice);
$availableTags = $this->availableTags($filters);
return new ProductListingResult($products, $priceBounds);
return new ProductListingResult($products, $priceBounds, $availableTags);
}
/**
* Every distinct `tags` value present on a product matching $filters,
* excluding $filters->tag itself — same "scoped but not self-collapsing"
* reasoning as priceRange() excluding `price` — so selecting a tag
* doesn't shrink the sidebar down to just that one tag. Sorted
* alphabetically; Meilisearch's facetDistribution has no defined order
* of its own.
*
* @return array<int, string>
*/
private function availableTags(?ProductFilters $filters): array
{
$filter = $this->filterBuilder->build($filters, exclude: ['tag']);
$tags = $this->rawFacets('tags', $filter)['facetDistribution']['tags'] ?? [];
return collect($tags)->keys()->sort()->values()->all();
}
/**
@@ -93,9 +112,12 @@ class ProductService
* apply that field's own filter separately in the UI/query layer.
*
* `$field` must be one of ProductIndexer's filterable fields; only discrete-value
* fields make sense here (`brand`, `in_stock`) — a numeric field like `price`
* would return one "facet" per exact price, not a usable range bucket. Use
* `priceRange()` for `price` instead.
* fields make sense here (`brand`, `tags`, `in_stock`) — a numeric field like
* `price` would return one "facet" per exact price, not a usable range bucket.
* Use `priceRange()` for `price` instead. `facets('tags', $filters)` is how a
* category page gets "which tags actually appear on products in this category" —
* pass a $filters that omits `tag` (see `build()`'s $exclude) so the tag list
* itself doesn't collapse to whichever tag is already selected.
*
* @return array<string, int> facet value => matching product count
*/