ldc-groupe/app/Http/Controllers/Front/PostsController.php

329 lines
12 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers\Front;
use Carbon\Carbon;
use App\Models\News;
use App\Models\NewsTag;
use App\Enums\YearsEnum;
use App\Models\Document;
use App\Enums\OrdersEnum;
use App\Models\DocumentTag;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;
use App\Http\Requests\Front\PostsRequest;
use Uwil\LaravelCommon\Controllers\Controller;
use Uwil\LaravelCommon\Helpers\CollectionHelper;
class PostsController extends Controller
{
2026-02-10 15:43:47 +01:00
/** @var string */
2026-01-13 11:14:47 +01:00
protected const DOCUMENTS = "documents";
2026-02-10 15:43:47 +01:00
/** @var string */
protected const NEWS = "news";
/**
* Extract unique years from date field in a collection.
*
2026-02-10 15:43:47 +01:00
* @param \Illuminate\Support\Collection $model
* @param string $field
* @return \Illuminate\Support\Collection
*/
2026-02-10 15:43:47 +01:00
private function getUniqueYearsFromCollection(Collection $model, string $field): \Illuminate\Support\Collection
{
return $model->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
*/
2026-02-10 15:43:47 +01:00
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
*
2026-02-10 15:43:47 +01:00
* @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()),
2026-01-13 11:14:47 +01:00
'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();
2026-02-10 15:43:47 +01:00
// Sort displayed years.
$years = $this->getMatchingValues(
$this->getUniqueYearsFromCollection($model, 'published_at'),
$enumYears,
'value'
);
2026-01-13 11:14:47 +01:00
if ($type === self::DOCUMENTS) {
$filters[] = [
'title' => ucfirst(__('front.news.order')),
'name' => 'order',
'fields' => $orders,
];
}
$filters[] = [
2026-02-10 15:43:47 +01:00
'title' => $type === self::DOCUMENTS
? ucfirst(__('front.news.document'))
: ucfirst(__('front.news.theme')),
'name' => 'tags',
'fields' => $tags,
];
$filters[] = [
2026-02-10 15:43:47 +01:00
'title' => $type === self::DOCUMENTS
? ucfirst(__('front.news.exercise'))
: ucfirst(__('front.news.year')),
'name' => 'years',
'fields' => $years,
];
return $filters;
}
/**
2026-02-10 15:43:47 +01:00
* 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
{
2026-02-10 15:43:47 +01:00
$data = $request->validated();
$perPage = 9;
$selectedFilters = [];
$category = $data['category'];
app()->setLocale($data['locale']);
$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);
2026-02-10 15:43:47 +01:00
// Custom pagination.
$newsModel = CollectionHelper::paginate($newsModel, $perPage);
return response()->json([
2026-02-10 15:43:47 +01:00
'pagination' => [
'total' => $newsModel->total(),
'nbItems' => count($newsModel->items()),
'currentPage' => $newsModel->currentPage(),
2026-02-10 15:43:47 +01:00
'lastPage' => $newsModel->lastPage(),
'hasMore' => $newsModel->currentPage() < $newsModel->lastPage(),
],
2026-02-10 15:43:47 +01:00
'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()
]);
}
/**
2026-02-10 15:43:47 +01:00
* 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
{
2026-02-10 15:43:47 +01:00
$data = $request->validated();
$perPage = 3;
$selectedFilters = [];
$category = $data['category'];
app()->setLocale($data['locale']);
$publishedDocumentsTags = DocumentTag::getTagsByCategoryInCurrentLocale($category);
$documentsModel = Document::getPublishedDocumentsByCategoryInCurrentLocale($category);
$requestedTags = collect($data['filters']['tags'] ?? '')
->filter()
->unique()
->values();
2026-02-10 15:43:47 +01:00
$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);
2026-02-10 15:43:47 +01:00
// Custom pagination.
$documentsModel = CollectionHelper::paginate($documentsModel, $perPage);
return response()->json([
2026-02-10 15:43:47 +01:00
'pagination' => [
'total' => $documentsModel->total(),
'nbItems' => count($documentsModel->items()),
'currentPage' => $documentsModel->currentPage(),
2026-02-10 15:43:47 +01:00
'lastPage' => $documentsModel->lastPage(),
'hasMore' => $documentsModel->currentPage() < $documentsModel->lastPage(),
],
2026-02-10 15:43:47 +01:00
'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()
]);
}
}