where('active', 0) ->pluck('name'); } public function filterProductsByVisibleCategories(Collection $products): Collection { $hidden = $this->hiddenCategoryNames(); if ($hidden->isEmpty()) { return $products; } return $products->filter(function ($product) use ($hidden) { $group = trim((string) ($product->group_type ?? '')); if ($group === '') { return true; } if (! Category::where('name', $group)->exists()) { return true; } return ! $hidden->contains($group); })->values(); } public function sortProductsForStore(Collection $products): Collection { return $products ->sort(function ($a, $b) { $aInStock = (int) ($a->inventory ?? 0) > 0 ? 0 : 1; $bInStock = (int) ($b->inventory ?? 0) > 0 ? 0 : 1; if ($aInStock !== $bInStock) { return $aInStock <=> $bInStock; } return ((int) ($b->inventory ?? 0)) <=> ((int) ($a->inventory ?? 0)); }) ->values(); } /** @return Collection */ public function collectTargetCategoryNames(): Collection { $fromProducts = Product::query() ->whereNotNull('group_type') ->where('group_type', '!=', '') ->distinct() ->pluck('group_type'); $defaults = collect(self::DEFAULT_CATEGORY_NAMES); $extras = $fromProducts ->map(static fn ($name) => trim((string) $name)) ->filter() ->diff($defaults) ->sort() ->values(); return $defaults ->merge($extras) ->unique() ->values(); } public function syncFromProducts(): int { $names = $this->collectTargetCategoryNames(); $created = 0; $maxOrder = (int) Category::max('sort_order'); foreach ($names as $name) { $existing = Category::where('name', $name)->first(); if ($existing) { continue; } $maxOrder++; Category::create([ 'name' => $name, 'active' => 1, 'show_in_filter' => 1, 'show_in_select' => 1, 'sort_order' => $maxOrder, ]); $created++; } return $created; } /** * تب‌های فیلتر اپ — فقط دسته‌های فعال که حداقل یک محصول فعال دارند. * show_in_filter دستی لازم نیست؛ از همان منطق نمایش محصولات مشتق می‌شود. */ public function clientFilterCategories(): Collection { $groupsWithProducts = Product::query() ->where('active', 1) ->whereNotNull('group_type') ->where('group_type', '!=', '') ->distinct() ->pluck('group_type') ->map(static fn ($name) => trim((string) $name)) ->filter() ->unique() ->values(); if ($groupsWithProducts->isEmpty()) { return collect(); } if (! Schema::hasTable('categories')) { return $this->fallbackClientCategories($groupsWithProducts); } if (! Schema::hasColumn('categories', 'sort_order')) { return $this->fallbackClientCategories($groupsWithProducts); } $categories = Category::query() ->where('active', 1) ->whereIn('name', $groupsWithProducts) ->orderBy('sort_order') ->orderBy('id') ->get(['id', 'name', 'sort_order']); if ($categories->isEmpty()) { $this->syncFromProducts(); return Category::query() ->where('active', 1) ->whereIn('name', $groupsWithProducts) ->orderBy('sort_order') ->orderBy('id') ->get(['id', 'name', 'sort_order']); } return $categories; } /** وقتی جدول categories هنوز migrate نشده — از group_type محصولات */ private function fallbackClientCategories(Collection $groupsWithProducts): Collection { $ordered = collect(self::DEFAULT_CATEGORY_NAMES) ->merge($groupsWithProducts) ->unique() ->values(); $order = 0; return $ordered ->filter(static fn (string $name) => $groupsWithProducts->contains($name)) ->values() ->map(static function (string $name) use (&$order) { $order++; return (object) [ 'id' => $order, 'name' => $name, 'sort_order' => $order, ]; }); } }