input('condition', [])); $countries = array_filter((array) $request->input('country_origin', [])); $fuels = array_filter((array) $request->input('engine_type', [])); $make = trim((string) $request->input('make', '')); $bodyType = trim((string) $request->input('body_type', '')); $priceTo = $request->filled('price_to') ? (int) $request->input('price_to') : null; $priceFrom = null; // price_range из быстрого подбора: "1500000", "1500000-3000000", "5000000-" $priceRange = $request->input('price_range', ''); if ($priceRange !== '') { if (str_ends_with($priceRange, '-')) { $priceFrom = (int) rtrim($priceRange, '-') ?: null; } elseif (str_contains($priceRange, '-')) { [$from, $to] = explode('-', $priceRange, 2); $priceFrom = $from !== '' ? (int) $from : null; $priceTo = $to !== '' ? (int) $to : $priceTo; } else { $priceTo = (int) $priceRange ?: $priceTo; } } $year = $request->filled('year') ? (int) $request->input('year') : null; $sort = $request->input('sort', ''); $page = $request->integer('page', 1); // ── Строим ключ кеша ────────────────────────────────────────── $filterData = compact('conditions', 'countries', 'fuels', 'make', 'bodyType', 'priceFrom', 'priceTo', 'year', 'sort'); ksort($filterData); $cacheKey = 'catalog.'.md5(serialize($filterData)).'.p'.$page; $cached = Cache::remember($cacheKey, self::CACHE_TTL, function () use ( $conditions, $countries, $fuels, $make, $bodyType, $priceFrom, $priceTo, $year, $sort, $page ) { $query = Car::where('status', 'active'); if ($conditions) { $query->whereIn('condition', $conditions); } if ($countries) { $query->whereIn('country_origin', $countries); } if ($fuels) { $query->whereIn('engine_type', $fuels); } if ($make) { $query->where('make', $make); } if ($bodyType) { $query->where('body_type', $bodyType); } if ($priceFrom) { $query->where('price_rub', '>=', $priceFrom); } if ($priceTo) { $query->where('price_rub', '<=', $priceTo); } if ($year) { $query->where('year', $year); } match ($sort) { 'price_asc' => $query->orderBy('price_rub'), 'price_desc' => $query->orderByDesc('price_rub'), 'year_desc' => $query->orderByDesc('year'), default => $query->orderByDesc('id'), }; $paginator = $query->paginate(self::PER_PAGE, ['*'], 'page', $page); return [ 'total' => $paginator->total(), 'items' => $paginator->getCollection()->map->getAttributes()->all(), ]; }); // Восстанавливаем Car из атрибутов (без unserialize объекта) $items = collect($cached['items'])->map(fn ($a) => (new Car)->setRawAttributes($a, true)); $cars = new LengthAwarePaginator( $items, $cached['total'], self::PER_PAGE, $page, ['path' => $request->url(), 'query' => $request->query()] ); $filterOptions = $this->filterOptions(); $activeFilters = compact('conditions', 'countries', 'fuels', 'make', 'bodyType', 'priceFrom', 'priceTo', 'year', 'sort'); return view('pages.catalog', compact('cars', 'filterOptions', 'activeFilters')); } public function show(int $id): View { // Детальная страница: без кеша — запросы редкие, зато данные всегда свежие $car = Car::where('status', 'active')->findOrFail($id); $similar = Car::where('status', 'active') ->where('id', '!=', $car->id) ->where(fn ($q) => $q->where('make', $car->make)->orWhere('body_type', $car->body_type)) ->orderByDesc('id') ->limit(3) ->get(); return view('pages.car', compact('car', 'similar')); } // Опции для фильтров — кешируются отдельно private function filterOptions(): array { return Cache::remember('catalog_filter_opts', self::CACHE_TTL, function () { $base = Car::where('status', 'active'); return [ 'makes' => (clone $base)->select('make')->distinct()->orderBy('make')->pluck('make')->toArray(), 'body_types' => (clone $base)->whereNotNull('body_type')->where('body_type', '!=', '')->select('body_type')->distinct()->orderBy('body_type')->pluck('body_type')->toArray(), 'countries' => (clone $base)->whereNotNull('country_origin')->select('country_origin')->distinct()->orderBy('country_origin')->pluck('country_origin')->toArray(), 'years' => (clone $base)->select('year')->distinct()->orderByDesc('year')->pluck('year')->toArray(), 'price_min' => (int) ((clone $base)->whereNotNull('price_rub')->min('price_rub') ?? 0), 'price_max' => (int) ((clone $base)->whereNotNull('price_rub')->max('price_rub') ?? 50000000), ]; }); } }