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 */ 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 \Illuminate\Database\Eloquent\Collection $publishedTags * @param \Illuminate\Database\Eloquent\Collection $model * * @return array */ private function getFilters(string $type, string $category, Collection $publishedTags, Collection $model): array { $filters = []; $tags = collect($publishedTags) ->map(fn($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 === "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 === "documents") { $filters[] = [ 'title' => ucfirst(__('front.news.order')), 'name' => 'order', 'fields' => $orders, ]; } $filters[] = [ 'title' => $type === "documents" ? ucfirst(__('front.news.document')) : ucfirst(__('front.news.theme')), 'name' => 'tags', 'fields' => $tags, ]; $filters[] = [ 'title' => $type === "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 { $data = $request->validated(); $perPage = 9; $selectedFilters = []; $category = $data['category']; app()->setLocale($data['locale']); $locale = app()->getLocale(); $publishedNewsTags = NewsTag::getTagsByCategoryInCurrentLocale($category); $newsModel = News::getPublishedNewsByCategoryInCurrentLocale($category); $requestedTags = collect($data['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' => $category, ]) ->values(); if ($selectedTags->isNotEmpty()) { $newsModel = $newsModel->filter(function ($news) use ($selectedTags) { return $news->tags->contains( fn ($tag) => $selectedTags->contains('id', $tag->id) ); }); $selectedFilters['tags'] = $selectedTags; } $selectedYears = collect($data['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' => $category, ]) ->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', $category, $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 { $data = $request->validated(); $perPage = 3; $selectedFilters = []; $category = $data['category']; app()->setLocale($data['locale']); $locale = app()->getLocale(); $publishedDocumentsTags = DocumentTag::getTagsByCategoryInCurrentLocale($category); $documentsModel = Document::getPublishedDocumentsByCategoryInCurrentLocale($category); $requestedTags = collect($data['filters']['tags'] ?? '') ->filter() ->unique() ->values(); $selectedTags = $publishedDocumentsTags->filter(fn ($tag) => $requestedTags->contains($tag->slug ?? null)) ->map(fn ($tag) => [ 'id' => $tag->id, 'title' => $tag->name, 'tag' => $tag->slug, 'color' => $tag->color, 'category' => $category, ]) ->values(); if ($selectedTags->isNotEmpty()) { $documentsModel = $documentsModel->filter(function ($documents) use ($selectedTags) { return $documents->tags->contains( fn ($tag) => $selectedTags->contains('id', $tag->id) ); }); $selectedFilters['tags'] = $selectedTags; } $selectedYears = collect($data['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' => $category, ]) ->values(); if ($selectedYears->isNotEmpty()) { $documentsModel = $documentsModel->filter(function ($documents) use ($selectedYears) { return $selectedYears->contains('value', $documents->exercice); }); $selectedFilters['years'] = $selectedYears; } $selectedOrder = collect($data['filters']['order'] ?? '') ->map(fn (string $label) => OrdersEnum::makeFromLabel($label)) ->filter() ->map(fn($order) => [ 'title' => trans($order->label()), 'tag' => $order->name(), 'category' => $category, ]) ->values(); if ($selectedOrder->isNotEmpty()) { $documentsModel = $selectedOrder->map->tag->first() === 'desc' ? $documentsModel->sortByDesc('published_at') : $documentsModel->sortBy('published_at'); $selectedFilters['order'] = $selectedOrder->all(); } else { $documentsModel = $documentsModel->sortByDesc('published_at'); } $filters = $this->getFilters('documents', $category, $publishedDocumentsTags, $documentsModel); /** Custom pagination */ $documentsModel = CollectionHelper::paginate($documentsModel, $perPage); return response()->json([ 'pagination' => [ 'total' => $documentsModel->total(), 'nbItems' => count($documentsModel->items()), 'currentPage' => $documentsModel->currentPage(), 'lastPage' => $documentsModel->lastPage(), 'hasMore' => $documentsModel->currentPage() < $documentsModel->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' => $documentsModel->items()])->render() ]); } }