with(['category:id,name,slug', 'brand:id,name,slug']) ->active() ->whereNotNull('published_at') ->orderByDesc('published_at') ->orderByDesc('id'); $this->applySearchFilter($query, (string) ($request->validated('q') ?? '')); $this->applyCategoryFilter($query, $request->validated('category')); $paginator = $query->paginate(24)->appends($request->query()); return ProductResource::collection($paginator); } private function applySearchFilter(Builder $query, string $search): void { $search = trim($search); if ($search !== '') { $query->where(function (Builder $builder) use ($search): void { $builder ->where('name', 'like', "%{$search}%") ->orWhere('description', 'like', "%{$search}%") ->orWhere('sku', 'like', "%{$search}%"); }); } } private function applyCategoryFilter(Builder $query, mixed $categorySlug): void { if (is_string($categorySlug) && $categorySlug !== '') { $category = Category::query() ->where('slug', $categorySlug) ->first(); if (! $category instanceof Category) { return; } $query->whereIn('category_id', $this->collectDescendantCategoryIds($category->id)); } } /** * @return list */ private function collectDescendantCategoryIds(int $rootCategoryId): array { /** @var Collection $categories */ $categories = Category::query() ->select(['id', 'parent_id']) ->get(); $byParent = []; foreach ($categories as $category) { if ($category->parent_id === null) { continue; } $byParent[$category->parent_id][] = (int) $category->id; } $stack = [$rootCategoryId]; $seen = []; while ($stack !== []) { $categoryId = array_pop($stack); if ($categoryId === null || isset($seen[$categoryId])) { continue; } $seen[$categoryId] = true; foreach ($byParent[$categoryId] ?? [] as $childId) { $stack[] = $childId; } } return array_map('intval', array_keys($seen)); } }