'boolean']; public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logFillable() ->logOnlyDirty() ->dontSubmitEmptyLogs(); } public function parent(): BelongsTo { return $this->belongsTo(Category::class, 'parent_id'); } public function children(): HasMany { return $this->hasMany(Category::class, 'parent_id'); } public function listings(): HasMany { return $this->hasMany(\Modules\Listing\Models\Listing::class); } public function scopeActive(Builder $query): Builder { return $query->where('is_active', true); } public function scopeOrdered(Builder $query): Builder { return $query->orderBy('sort_order')->orderBy('name'); } public static function filterOptions(): Collection { return static::query() ->active() ->ordered() ->get(['id', 'name']); } public static function themePills(int $limit = 8): Collection { return static::query() ->active() ->ordered() ->limit($limit) ->get(['id', 'name', 'slug']); } public static function rootTreeWithActiveChildren(): Collection { return static::query() ->active() ->whereNull('parent_id') ->with([ 'children' => fn ($query) => $query->active()->ordered(), ]) ->ordered() ->get(); } public static function listingDirectory(?int $selectedCategoryId): array { $categories = static::query() ->active() ->ordered() ->get(['id', 'name', 'parent_id']); $activeListingCounts = Listing::query() ->active() ->whereNotNull('category_id') ->selectRaw('category_id, count(*) as aggregate') ->groupBy('category_id') ->pluck('aggregate', 'category_id') ->map(fn ($count): int => (int) $count); return [ 'categories' => static::buildListingDirectoryTree($categories, $activeListingCounts), 'selectedCategory' => $selectedCategoryId ? $categories->firstWhere('id', $selectedCategoryId) : null, 'filterIds' => static::listingFilterIds($selectedCategoryId, $categories), ]; } public static function listingFilterIds(?int $selectedCategoryId, ?Collection $categories = null): ?array { if (! $selectedCategoryId) { return null; } if ($categories instanceof Collection) { $selectedCategory = $categories->firstWhere('id', $selectedCategoryId); if (! $selectedCategory instanceof self) { return []; } return static::descendantAndSelfIdsFromCollection($selectedCategoryId, $categories); } $selectedCategory = static::query() ->active() ->whereKey($selectedCategoryId) ->first(['id']); if (! $selectedCategory) { return []; } return $selectedCategory->descendantAndSelfIds()->all(); } public function descendantAndSelfIds(): Collection { $ids = collect([(int) $this->getKey()]); $frontier = $ids; while ($frontier->isNotEmpty()) { $children = static::query() ->whereIn('parent_id', $frontier->all()) ->pluck('id') ->map(fn ($id): int => (int) $id) ->values(); if ($children->isEmpty()) { break; } $ids = $ids ->merge($children) ->unique() ->values(); $frontier = $children; } return $ids; } public function breadcrumbTrail(): Collection { $trail = collect(); $current = $this; while ($current) { $trail->prepend($current); $current = $current->parent; } return $trail; } public function listingCustomFields(): HasMany { return $this->hasMany(\Modules\Listing\Models\ListingCustomField::class); } public function activeListings(): HasMany { return $this->hasMany(\Modules\Listing\Models\Listing::class)->where('status', 'active'); } private static function buildListingDirectoryTree(Collection $categories, Collection $activeListingCounts, ?int $parentId = null): Collection { return $categories ->filter(fn (Category $category): bool => $parentId === null ? $category->parent_id === null : (int) $category->parent_id === $parentId) ->values() ->map(function (Category $category) use ($categories, $activeListingCounts): Category { $children = static::buildListingDirectoryTree($categories, $activeListingCounts, (int) $category->getKey()); $directActiveListingsCount = (int) $activeListingCounts->get((int) $category->getKey(), 0); $activeListingTotal = $directActiveListingsCount + $children->sum( fn (Category $child): int => (int) $child->getAttribute('active_listing_total') ); $category->setRelation('children', $children); $category->setAttribute('direct_active_listings_count', $directActiveListingsCount); $category->setAttribute('active_listing_total', $activeListingTotal); return $category; }) ->values(); } private static function descendantAndSelfIdsFromCollection(int $selectedCategoryId, Collection $categories): array { $ids = collect([$selectedCategoryId]); $frontier = collect([$selectedCategoryId]); while ($frontier->isNotEmpty()) { $children = $categories ->filter(fn (Category $category): bool => $category->parent_id !== null && in_array((int) $category->parent_id, $frontier->all(), true)) ->pluck('id') ->map(fn ($id): int => (int) $id) ->values(); if ($children->isEmpty()) { break; } $ids = $ids ->merge($children) ->unique() ->values(); $frontier = $children; } return $ids->all(); } }