boboko / products & collections · competitive survey

What products & collections elsewhere can do that boboko can't yet

A feature-by-feature pass across Shopify, WooCommerce, PrestaShop, and general 2026 storefront UX trends — checked against what Modules\Core\Catalog actually ships in boboko-core and what 3dealer's storefront actually calls. Unlike the rest of this survey series, this concern is not a blank slate: a real Meilisearch-backed catalog layer (listing, filtering, facets, search, collections, a product-option-type system) was built this session. The gaps here are mostly about storefront wiring and discovery/merchandising UX, not backend plumbing.

Read this first: the category page's sort dropdown, price slider, in-stock checkbox, and sidebar search box are all visually present but functionally dead — none of them submit a request or call a filter. The backend methods they'd need (ProductService::facets(), priceRange(), list()'s sort param) already exist and work; nothing in CategoryController passes them through yet.
● have ◐ partial ○ missing
31 features surveyed — 8 have · 10 partial · 13 missing
01

Core listing & filtering plumbing

The Meilisearch-backed layer everything else in this survey sits on top of — this is where most of this session's real build lives.

Paginated product listing, index-backed (not DB reads)
have
ProductService::list() reads Product::search('') via Meilisearch and returns a real LengthAwarePaginator — used end-to-end by CategoryController::show() and rendered by x-product-grid.
Filter by collection (including descendant collections)
have
ProductFilters::collectionId matches ProductIndexer's collection_ids field, which unions a product's direct collections with all ancestors — so a parent-category page picks up products attached only to a leaf subcategory. Wired in CategoryController.
Filter by brand, price range, stock status
partial
ProductFilters supports brand, minPrice/maxPrice, inStockOnly, fully implemented in ProductService::buildFilter() — but category/show.blade.php's price slider and in-stock checkbox are hardcoded markup with no form submission; CategoryController never constructs a ProductFilters with any of these three.
Faceted counts for a filter sidebar (brand, stock, etc.)
partial
ProductService::facets() returns value→count via Meilisearch facetDistribution, correctly scoped to co-applied filters — but nothing storefront-side calls it. No brand/attribute facet list renders anywhere in category/show.blade.php.
Price-range slider backed by real min/max
partial
ProductService::priceRange() reads Meilisearch facetStats for a correct, filter-scoped min/max — the sidebar instead shows a static "€10 - €50" label with a non-functional apply button.
Sort (price asc/desc, newest)
partial
ProductSort enum + ProductIndexer::getSortableFields() (price, created_at) work end-to-end in ProductService::list(sort: ...) — the storefront's sort <select> is explicitly commented {{-- Dummy — not wired to real sorting yet --}} and includes a "popularity" option with no backing signal at all.
Free-text product search
partial
ProductSearchService::search() is a complete, locale-aware, fallback-safe implementation (attributesToSearchOn targeting current + default locale) — but no search route exists in 3dealer (routes/web.php only has product.show/category.show), and both the header search icon and the sidebar search box are inert buttons/inputs.
Single-product lookup by slug or id, index-only
have
ProductService::getById()/getBySlug(), both zero-database-read lookups against the slugs/id filterable fields. ProductController::show() uses getById() directly.
02

Collections & navigation

Category tree browsing, breadcrumbs, and merchandising — what turns a flat product list into a navigable store.

Category tree browsing (root / children / by group)
have
CollectionService::list() with CollectionFilters(rootOnly/parentId/groupId), backed by CollectionIndexer's nested-set parent_id/_lft fields — no database read needed to build a nav tree.
Top-nav category dropdown
have
components/header.blade.php renders a CSS-only hover dropdown from a $categories list passed into the layout, linking to category.show.
Breadcrumb navigation
partial
CollectionIndexer indexes a full root-first ancestors array ({id, name}) specifically so a breadcrumb needs zero extra queries — but category/show.blade.php and product/show.blade.php both build a flat two-level x-breadcrumb (Home → this category/product) by hand, never reading ancestors. A product under a three-deep category shows no intermediate levels.
Category landing page merchandising (banner, pinned/featured products)
missing
category/show.blade.php renders only the collection name/description above a plain product grid — no banner image field, no "featured in this category" pinning above organic results. CollectionIndexer's thumbnail field exists but isn't read on the category page at all (only used, if anywhere, for nav-level imagery).
Sub-category faceting (filter by attribute within a category)
missing
No attribute-value facet (size, material, etc.) is indexed as filterable on ProductIndexer beyond brand and in_stock — a category page can't offer "filter dresses by size" the way Shopify/WooCommerce faceted nav does; would need new filterable fields on custom product attributes plus sidebar UI.
Product count shown per category
partial
CollectionIndexer computes product_count (including descendant collections) at index time by querying the product index directly — correct and cheap, but nothing in category/show.blade.php or the nav dropdown displays it.
03

Product detail page

What a shopper sees once they land on a single product — media, variants, reviews, cross-sell.

Multi-image gallery with lightbox
have
product/show.blade.php's product-gallery Stimulus controller — thumbnail rail, main image, full popover lightbox with prev/next/counter — fed from ProductIndexer's full media array (not just a single thumbnail).
Variant selection via color swatches
have
End-to-end: ColorOptionType lets an admin attach a hex code to an option value → ProductIndexer::mapVariant() embeds meta.hex per variant → x-ui.color-swatch renders real swatch buttons wired to a product-form Stimulus controller that swaps price/image on selection.
Swatches for non-color attributes (pattern, texture, material)
partial
The ProductOptionTypeInterface system is explicitly built to be extensible — a PatternOptionType or MaterialOptionType is a new class plus a Filament form, no core change needed — but only ColorOptionType is registered, and x-ui.color-swatch itself hardcodes a background-color swatch, not a generic swatch renderer.
Customer reviews with ratings, photos, staff replies
have
ProductReview model, fully indexed (items/count/average_rating, PII-safe), live-reindexed on review create/update/delete via ReviewServiceProvider, and rendered in product/show.blade.php's Reviews tab with x-review-card/x-review-form.
Structured data / schema.org Product markup
missing
No application/ld+json or itemscope markup anywhere in 3dealer's views. Rich results (price/rating/availability in Google Shopping) are a significant organic-CTR lever per 2026 SEO guidance — the product page already has every field (price, rating, stock) a Product schema block would need, just not emitted.
Related products / "customers also bought" / cross-sell
missing
Raw Lunar already models this (Lunar\Base\Enums\ProductAssociation::CROSS_SELL/UP_SELL/ALTERNATE, $product->associate()/associations() — see docs/lunar.md "Products and Variants") but nothing in Modules\Core\Catalog surfaces it, and the "Σχετικά προϊόντα" block at the bottom of product/show.blade.php is four fully hardcoded fake products with href => '#'.
Recently-viewed products
missing
No session/cookie tracking of viewed products anywhere in 3dealer or core — a standard discovery module on both Shopify and WooCommerce storefronts per current UX research.
Product badges (new / sale / bestseller)
missing
No badge concept on ProductIndexer's document and no badge markup on x-ui.product-card — would need either a computed signal (e.g. "new" from created_at, "sale" from compare_price already indexed per-variant) or an admin-set tag, neither wired to a visual badge today.
Size chart / fit guide
missing
No size-chart content field on Product/ProductType and no UI for it on the product page. Not especially relevant to 3dealer's current catalog (3D-printed goods), but a real gap for any apparel-leaning store built on this core.
Stock notification ("notify me when back in stock")
missing
in_stock is indexed and known per-product (ProductIndexer::toSearchableArray()), but there's no subscription model, email trigger, or UI for a shopper to ask to be notified — the signal exists, nothing acts on it.
Product Q&A section
missing
No question/answer model anywhere in core — only the separate review system (ProductReview) exists, which is a distinct concept (post-purchase rating, not pre-purchase Q&A).
04

Emerging discovery & merchandising UX

2026 trend-adjacent features, mostly backed on other platforms by paid apps/plugins rather than core — useful for calibrating how unusual these gaps are.

Quick-view modal (preview from listing grid, no page load)
missing
x-ui.product-card links straight to product.show with a hover-revealed "add to cart" button only — no modal/preview interaction. Current UX research flags quick-view modals as a common INP (responsiveness) failure point, so the absence isn't purely a gap to close blindly.
Infinite scroll as an alternative to pagination
missing
category/show.blade.php uses classic x-ui.pagination against the real paginator from ProductService::list() — works correctly, just page-based rather than scroll-based. Research is genuinely mixed on whether infinite scroll is even preferable for conversion/SEO, so this is a parity note, not a clear gap.
Product comparison tool (side-by-side spec table)
missing
Not in boboko-core, and notably not native on Shopify or WooCommerce either — both rely on third-party apps (Bear Specs & Compare, Equate, WooCommerce's own paid "Advanced Product Comparison" extension). A real gap, but not one competitors solve in-platform for free.
Product bundles / kits
missing
No bundle/kit concept (a purchasable grouping of several variants as one line item) anywhere in Lunar\Models\Product/ProductVariant or Modules\Core\Catalog.
360°/video product media, AR try-on
partial
ProductIndexer's media array is just Spatie media-library images (url/thumb) — no video or 360° asset type modeled, and no AR integration. The gallery component (product-gallery Stimulus controller) is generic enough to extend to a video slide without a rewrite, but nothing does today.
Variant-specific SEO URLs (distinct slug per color/size)
partial
Lunar's HasUrls/Url model supports per-locale slugs per product (indexed in ProductIndexer's slugs field), but there's no per-variant URL — selecting a color swatch changes displayed price/image via product-form client-side state, not the URL, so a specific variant can't be linked or indexed separately.