['except' => ''], 'category' => ['except' => ''], 'status' => ['except' => ''], 'manufacturerId' => ['except' => ''], 'openingYearFrom' => ['except' => ''], 'openingYearTo' => ['except' => ''], 'minHeight' => ['except' => ''], 'maxHeight' => ['except' => ''], 'parkId' => ['except' => ''], 'page' => ['except' => 1], ]; /** * Component initialization */ public function mount(): void { // Initialize component state } /** * Reset pagination when search/filters change */ public function updatedSearch(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedCategory(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedStatus(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedManufacturerId(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedOpeningYearFrom(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedOpeningYearTo(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedMinHeight(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedMaxHeight(): void { $this->resetPage(); $this->invalidateCache('rides'); } public function updatedParkId(): void { $this->resetPage(); $this->invalidateCache('rides'); } /** * Clear all filters */ public function clearFilters(): void { $this->reset([ 'search', 'category', 'status', 'manufacturerId', 'openingYearFrom', 'openingYearTo', 'minHeight', 'maxHeight', 'parkId' ]); $this->resetPage(); $this->invalidateCache('rides'); } /** * Get rides with Django parity search and filtering */ public function getRidesProperty() { $cacheKey = $this->getCacheKey('rides.' . md5(serialize([ 'search' => $this->search, 'category' => $this->category, 'status' => $this->status, 'manufacturerId' => $this->manufacturerId, 'openingYearFrom' => $this->openingYearFrom, 'openingYearTo' => $this->openingYearTo, 'minHeight' => $this->minHeight, 'maxHeight' => $this->maxHeight, 'parkId' => $this->parkId, 'page' => $this->getPage(), ]))); return $this->remember($cacheKey, function () { return $this->buildQuery()->paginate(12); }, 300); // 5 minute cache } /** * Build the query with Django parity search and filters */ protected function buildQuery() { $query = Ride::query() ->with(['park', 'manufacturer', 'designer', 'photos' => function ($q) { $q->where('is_featured', true)->limit(1); }]); // Django parity multi-term search if (!empty($this->search)) { $terms = explode(' ', trim($this->search)); foreach ($terms as $term) { $query->where(function ($subQuery) use ($term) { $subQuery->where('name', 'ilike', "%{$term}%") ->orWhere('description', 'ilike', "%{$term}%") ->orWhereHas('park', fn($q) => $q->where('name', 'ilike', "%{$term}%")) ->orWhereHas('manufacturer', fn($q) => $q->where('name', 'ilike', "%{$term}%")) ->orWhereHas('designer', fn($q) => $q->where('name', 'ilike', "%{$term}%")); }); } } // Apply filters with Django parity $query = $this->applyFilters($query); return $query->orderBy('name'); } /** * Apply filters with Django parity */ protected function applyFilters($query) { return $query ->when($this->category, fn($q, $category) => $q->where('ride_type', $category)) ->when($this->status, fn($q, $status) => $q->where('status', $status)) ->when($this->manufacturerId, fn($q, $manufacturerId) => $q->where('manufacturer_id', $manufacturerId)) ->when($this->openingYearFrom, fn($q, $year) => $q->where('opening_date', '>=', "{$year}-01-01")) ->when($this->openingYearTo, fn($q, $year) => $q->where('opening_date', '<=', "{$year}-12-31")) ->when($this->minHeight, fn($q, $height) => $q->where('height_requirement', '>=', $height)) ->when($this->maxHeight, fn($q, $height) => $q->where('height_requirement', '<=', $height)) ->when($this->parkId, fn($q, $parkId) => $q->where('park_id', $parkId)); } /** * Get available filter options for dropdowns */ public function getFilterOptionsProperty() { return $this->remember('filter_options', function () { return [ 'categories' => Ride::select('ride_type') ->distinct() ->whereNotNull('ride_type') ->orderBy('ride_type') ->pluck('ride_type', 'ride_type'), 'statuses' => Ride::select('status') ->distinct() ->whereNotNull('status') ->orderBy('status') ->pluck('status', 'status'), 'manufacturers' => \App\Models\Manufacturer::orderBy('name') ->pluck('name', 'id'), 'parks' => \App\Models\Park::orderBy('name') ->pluck('name', 'id'), ]; }, 3600); // 1 hour cache for filter options } /** * Render the component */ public function render() { return view('livewire.rides-listing', [ 'rides' => $this->rides, 'filterOptions' => $this->filterOptions, ]); } /** * Get cache key for this component */ protected function getCacheKey(string $suffix = ''): string { return 'thrillwiki.' . class_basename(static::class) . '.' . $suffix; } /** * Remember data with caching */ protected function remember(string $key, $callback, int $ttl = 3600) { return Cache::remember($this->getCacheKey($key), $ttl, $callback); } /** * Invalidate component cache */ protected function invalidateCache(string $key = null): void { if ($key) { Cache::forget($this->getCacheKey($key)); } else { // Clear all cache for this component Cache::flush(); } } }