pluck($field) ->map(fn($date) => Carbon::parse($date)->year) ->unique() ->values(); } /** * Filter input collection to only keep items whose value exists in a reference collection. * * @param \Illuminate\Support\Collection $array1 * @param \Illuminate\Support\Collection $array2 * @param string|null $field * @return array */ private function getMatchingValues(Collection $array1, Collection $array2, ?string $field = null): array { return $array2 ->filter(fn ($item) => $array1->contains($field ? $item[$field] : $item)) ->values() ->toArray(); } /** * Build the common filters configuration for front-end listing pages. * If the type is "documents" the "order" filter is included. * * @param string $type * @param string $category * @param EloquentCollection $publishedTags * @param EloquentCollection $model * @return array */ private function getFilters( string $type, string $category, EloquentCollection $publishedTags, EloquentCollection $model, ): array { $filters = []; /** @phpstan-ignore-next-line */ $tags = $publishedTags->map(fn(DocumentTag|NewsTag $tag) => [ 'title' => $tag->name, 'tag' => $tag->slug, 'category' => $category, ]) ->values(); $enumYears = collect(YearsEnum::toArray()) ->map(fn($year) => YearsEnum::make((int) $year->value)) ->map(fn($year) => [ 'title' => trans($year->label()), 'tag' => Str::replace('/', '-', $year->label()), 'value' => $type === self::NEWS ? explode('/', $year->label())[1] : $year->value, 'category' => $category, ]) ->sortDesc() ->values(); $orders = collect(OrdersEnum::toArray()) ->map(fn($order) => OrdersEnum::make((int) $order->value)) ->filter(fn($orderLocal) => $orderLocal->ordersWithLocal()) ->map(fn($orderLocal) => [ 'title' => ucfirst(trans($orderLocal->label())), 'tag' => Str::slug($orderLocal->name), 'category' => $category, ]) ->values(); // Sort displayed years. $years = $this->getMatchingValues( $this->getUniqueYearsFromCollection($model, 'published_at'), $enumYears, 'value' ); if ($type === self::DOCUMENTS) { $filters[] = [ 'title' => ucfirst(__('front.news.order')), 'name' => 'order', 'fields' => $orders, ]; } $filters[] = [ 'title' => $type === self::DOCUMENTS ? ucfirst(__('front.news.document')) : ucfirst(__('front.news.theme')), 'name' => 'tags', 'fields' => $tags, ]; $filters[] = [ 'title' => $type === self::DOCUMENTS ? ucfirst(__('front.news.exercise')) : ucfirst(__('front.news.year')), 'name' => 'years', 'fields' => $years, ]; return $filters; } /** * Returns news in JSON format filtered by category and language. * Optionally, the results can be further filtered by tags, and years. * * @param \App\Http\Requests\Front\PostsRequest $request * @return \Illuminate\Http\JsonResponse */ public function ajaxLoadNews(PostsRequest $request): \Illuminate\Http\JsonResponse { $validatedData = $request->validated(); $perPage = 9; $selectedFilters = []; /** @var string $slugCategory */ $slugCategory = $validatedData['category']; app()->setLocale($validatedData['locale']); $publishedNewsTags = NewsTag::getTagsByCategoryInCurrentLocale($slugCategory); $newsModel = News::getPublishedNewsByCategoryInCurrentLocale($slugCategory); $requestedTags = collect($validatedData['filters']['tags'] ?? '') ->filter() ->unique() ->values(); $selectedTags = $publishedNewsTags->filter(fn ($tag) => $requestedTags->contains($tag->slug ?? null)) ->map(fn ($tag) => [ 'id' => $tag->id, 'title' => $tag->name, 'tag' => $tag->slug, 'color' => $tag->color, 'category' => $slugCategory, ]) ->values(); if ($selectedTags->isNotEmpty()) { $newsModel = $newsModel->filter(function ($newsModel) use ($selectedTags) { return $newsModel->tags->contains( fn ($tag) => $selectedTags->contains('id', $tag->id) ); }); $selectedFilters['tags'] = $selectedTags; } $selectedYears = collect($validatedData['filters']['years'] ?? '') ->map(fn (string $label) => YearsEnum::makeFromLabel(Str::replace('-', '/', $label))) ->filter() ->map(fn($year) => [ 'title' => $year->label(), 'tag' => Str::replace('/', '-', $year->label()), 'value' => explode('/', $year->label())[1], 'category' => $slugCategory, ]) ->values(); if ($selectedYears->isNotEmpty()) { $newsModel = $newsModel->filter(function ($news) use ($selectedYears) { return $selectedYears->contains('value', $news->published_at->year); }); $selectedFilters['years'] = $selectedYears; } $newsModel = $newsModel->sortByDesc('published_at'); $filters = $this->getFilters('news', $slugCategory, $publishedNewsTags, $newsModel); // Custom pagination. $newsModel = CollectionHelper::paginate($newsModel, $perPage); return response()->json([ 'pagination' => [ 'total' => $newsModel->total(), 'nbItems' => count($newsModel->items()), 'currentPage' => $newsModel->currentPage(), 'lastPage' => $newsModel->lastPage(), 'hasMore' => $newsModel->currentPage() < $newsModel->lastPage(), ], 'filters' => view('components.front.posts.filters-dropdowns', ['filters' => $filters])->render(), 'selectedFilters' => view('components.front.posts.selected-filters', [ 'selectedFilters' => $selectedFilters, ])->render(), 'news' => view('components.front.posts.news', ['newsList' => $newsModel->items()])->render() ]); } /** * Returns documents in JSON format filtered by category and language. * Optionally, the results can be further filtered by order, tags, and years. * * @param \App\Http\Requests\Front\PostsRequest $request * @return \Illuminate\Http\JsonResponse */ public function ajaxLoadDocuments(PostsRequest $request): \Illuminate\Http\JsonResponse { $validatedData = $request->validated(); $perPage = 3; $selectedFilters = []; /** @var string $slugCategory */ $slugCategory = $validatedData['category']; app()->setLocale($validatedData['locale']); $documentTagModels = DocumentTag::getTagsByCategoryInCurrentLocale($slugCategory); $documentModels = Document::getPublishedDocumentsByCategoryInCurrentLocale($slugCategory); $requestedTags = collect($validatedData['filters']['tags'] ?? '') ->filter() ->unique() ->values(); $selectedTags = $documentTagModels ->filter(fn ($tag) => $requestedTags->contains($tag->slug ?? null)) ->map(fn ($tag) => [ 'id' => $tag->id, 'title' => $tag->name, 'tag' => $tag->slug, 'color' => $tag->color, 'category' => $slugCategory, ]) ->values(); if ($selectedTags->isNotEmpty()) { $documentModels = $documentModels->filter(function ($documentModel) use ($selectedTags) { return $documentModel->tags->contains( fn ($tag) => $selectedTags->contains('id', $tag->id) ); }); $selectedFilters['tags'] = $selectedTags; } $selectedYears = collect($validatedData['filters']['years'] ?? '') ->map(fn (string $label) => YearsEnum::makeFromLabel(Str::replace('-', '/', $label))) ->filter() ->map(fn($year) => [ 'title' => $year->label(), 'tag' => Str::replace('/', '-', $year->label()), 'value' => $year->value, 'category' => $slugCategory, ]) ->values(); if ($selectedYears->isNotEmpty()) { $documentModels = $documentModels->filter(function ($documentModel) use ($selectedYears) { return $selectedYears->contains('value', $documentModel->exercice); }); $selectedFilters['years'] = $selectedYears; } $selectedOrder = collect($validatedData['filters']['order'] ?? '') ->map(fn (string $label) => OrdersEnum::makeFromLabel($label)) ->filter() ->map(fn($order) => [ 'title' => trans($order->label()), 'tag' => $order->name(), 'category' => $slugCategory, ]) ->values(); if ($selectedOrder->isNotEmpty()) { $firstTag = $selectedOrder->map(fn($order) => $order['tag'])->first(); $documentModels = $firstTag === 'desc' ? $documentModels->sortByDesc('published_at') : $documentModels->sortBy('published_at'); $selectedFilters['order'] = $selectedOrder->all(); } else { $documentModels = $documentModels->sortByDesc('published_at'); } $filters = $this->getFilters('documents', $slugCategory, $documentTagModels, $documentModels); // Custom pagination. $documentModels = CollectionHelper::paginate($documentModels, $perPage); return response()->json([ 'pagination' => [ 'total' => $documentModels->total(), 'nbItems' => count($documentModels->items()), 'currentPage' => $documentModels->currentPage(), 'lastPage' => $documentModels->lastPage(), 'hasMore' => $documentModels->currentPage() < $documentModels->lastPage(), ], 'filters' => view('components.front.posts.filters-dropdowns', ['filters' => $filters])->render(), 'selectedFilters' => view('components.front.posts.selected-filters', [ 'selectedFilters' => $selectedFilters, ])->render(), 'news' => view('components.front.posts.documents', [ 'documentsList' => $documentModels->items(), ])->render() ]); } }