2026-01-08 16:41:24 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Front;
|
|
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
use Carbon\Carbon;
|
2026-01-08 16:41:24 +01:00
|
|
|
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;
|
2026-01-09 17:07:50 +01:00
|
|
|
use Illuminate\Support\Collection;
|
2026-01-08 16:41:24 +01:00
|
|
|
use App\Http\Requests\Front\PostsRequest;
|
|
|
|
|
use Uwil\LaravelCommon\Controllers\Controller;
|
2026-01-09 17:07:50 +01:00
|
|
|
use Uwil\LaravelCommon\Helpers\CollectionHelper;
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
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";
|
|
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
/**
|
|
|
|
|
* 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
|
2026-01-09 17:07:50 +01:00
|
|
|
* @return \Illuminate\Support\Collection
|
|
|
|
|
*/
|
2026-02-10 15:43:47 +01:00
|
|
|
private function getUniqueYearsFromCollection(Collection $model, string $field): \Illuminate\Support\Collection
|
2026-01-09 17:07:50 +01:00
|
|
|
{
|
|
|
|
|
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
|
2026-01-09 17:07:50 +01:00
|
|
|
{
|
|
|
|
|
return $array2
|
|
|
|
|
->filter(fn ($item) => $array1->contains($field ? $item[$field] : $item))
|
|
|
|
|
->values()
|
|
|
|
|
->toArray();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-08 16:41:24 +01:00
|
|
|
/**
|
|
|
|
|
* 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
|
2026-01-08 16:41:24 +01:00
|
|
|
* @param \Illuminate\Database\Eloquent\Collection $publishedTags
|
2026-01-09 17:07:50 +01:00
|
|
|
* @param \Illuminate\Database\Eloquent\Collection $model
|
2026-01-08 16:41:24 +01:00
|
|
|
* @return array
|
|
|
|
|
*/
|
2026-01-09 17:07:50 +01:00
|
|
|
private function getFilters(string $type, string $category, Collection $publishedTags, Collection $model): array
|
2026-01-08 16:41:24 +01:00
|
|
|
{
|
|
|
|
|
$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,
|
2026-01-08 16:41:24 +01:00
|
|
|
'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.
|
2026-01-09 17:07:50 +01:00
|
|
|
$years = $this->getMatchingValues(
|
|
|
|
|
$this->getUniqueYearsFromCollection($model, 'published_at'),
|
2026-01-08 16:41:24 +01:00
|
|
|
$enumYears,
|
|
|
|
|
'value'
|
|
|
|
|
);
|
|
|
|
|
|
2026-01-13 11:14:47 +01:00
|
|
|
if ($type === self::DOCUMENTS) {
|
2026-01-08 16:41:24 +01:00
|
|
|
$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')),
|
2026-01-08 16:41:24 +01:00
|
|
|
'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')),
|
2026-01-08 16:41:24 +01:00
|
|
|
'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.
|
2026-01-08 16:41:24 +01:00
|
|
|
*
|
|
|
|
|
* @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;
|
2026-01-08 16:41:24 +01:00
|
|
|
$selectedFilters = [];
|
|
|
|
|
|
|
|
|
|
$category = $data['category'];
|
|
|
|
|
app()->setLocale($data['locale']);
|
|
|
|
|
|
|
|
|
|
$publishedNewsTags = NewsTag::getTagsByCategoryInCurrentLocale($category);
|
|
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
$newsModel = News::getPublishedNewsByCategoryInCurrentLocale($category);
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
$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()) {
|
2026-01-09 17:07:50 +01:00
|
|
|
$newsModel = $newsModel->filter(function ($news) use ($selectedTags) {
|
|
|
|
|
return $news->tags->contains(
|
|
|
|
|
fn ($tag) => $selectedTags->contains('id', $tag->id)
|
|
|
|
|
);
|
2026-01-08 16:41:24 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$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()) {
|
2026-01-09 17:07:50 +01:00
|
|
|
$newsModel = $newsModel->filter(function ($news) use ($selectedYears) {
|
|
|
|
|
return $selectedYears->contains('value', $news->published_at->year);
|
|
|
|
|
});
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
$selectedFilters['years'] = $selectedYears;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
$newsModel = $newsModel->sortByDesc('published_at');
|
2026-01-08 16:41:24 +01:00
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
$filters = $this->getFilters('news', $category, $publishedNewsTags, $newsModel);
|
2026-01-08 16:41:24 +01:00
|
|
|
|
2026-02-10 15:43:47 +01:00
|
|
|
// Custom pagination.
|
2026-01-09 17:07:50 +01:00
|
|
|
$newsModel = CollectionHelper::paginate($newsModel, $perPage);
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
return response()->json([
|
2026-02-10 15:43:47 +01:00
|
|
|
'pagination' => [
|
|
|
|
|
'total' => $newsModel->total(),
|
|
|
|
|
'nbItems' => count($newsModel->items()),
|
2026-01-08 16:41:24 +01:00
|
|
|
'currentPage' => $newsModel->currentPage(),
|
2026-02-10 15:43:47 +01:00
|
|
|
'lastPage' => $newsModel->lastPage(),
|
|
|
|
|
'hasMore' => $newsModel->currentPage() < $newsModel->lastPage(),
|
2026-01-08 16:41:24 +01:00
|
|
|
],
|
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-01-08 16:41:24 +01:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
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.
|
2026-01-08 16:41:24 +01:00
|
|
|
*
|
|
|
|
|
* @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;
|
2026-01-08 16:41:24 +01:00
|
|
|
$selectedFilters = [];
|
|
|
|
|
|
|
|
|
|
$category = $data['category'];
|
|
|
|
|
app()->setLocale($data['locale']);
|
|
|
|
|
|
|
|
|
|
$publishedDocumentsTags = DocumentTag::getTagsByCategoryInCurrentLocale($category);
|
|
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
$documentsModel = Document::getPublishedDocumentsByCategoryInCurrentLocale($category);
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
$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))
|
2026-01-08 16:41:24 +01:00
|
|
|
->map(fn ($tag) => [
|
|
|
|
|
'id' => $tag->id,
|
|
|
|
|
'title' => $tag->name,
|
|
|
|
|
'tag' => $tag->slug,
|
|
|
|
|
'color' => $tag->color,
|
|
|
|
|
'category' => $category,
|
|
|
|
|
])
|
|
|
|
|
->values();
|
|
|
|
|
|
|
|
|
|
if ($selectedTags->isNotEmpty()) {
|
2026-01-09 17:07:50 +01:00
|
|
|
$documentsModel = $documentsModel->filter(function ($documents) use ($selectedTags) {
|
|
|
|
|
return $documents->tags->contains(
|
|
|
|
|
fn ($tag) => $selectedTags->contains('id', $tag->id)
|
|
|
|
|
);
|
2026-01-08 16:41:24 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$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()) {
|
2026-01-09 17:07:50 +01:00
|
|
|
$documentsModel = $documentsModel->filter(function ($documents) use ($selectedYears) {
|
|
|
|
|
return $selectedYears->contains('value', $documents->exercice);
|
|
|
|
|
});
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
$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()) {
|
2026-01-09 17:07:50 +01:00
|
|
|
$documentsModel = $selectedOrder->map->tag->first() === 'desc'
|
|
|
|
|
? $documentsModel->sortByDesc('published_at')
|
|
|
|
|
: $documentsModel->sortBy('published_at');
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
$selectedFilters['order'] = $selectedOrder->all();
|
2026-01-09 17:07:50 +01:00
|
|
|
} else {
|
|
|
|
|
$documentsModel = $documentsModel->sortByDesc('published_at');
|
2026-01-08 16:41:24 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-09 17:07:50 +01:00
|
|
|
$filters = $this->getFilters('documents', $category, $publishedDocumentsTags, $documentsModel);
|
2026-01-08 16:41:24 +01:00
|
|
|
|
2026-02-10 15:43:47 +01:00
|
|
|
// Custom pagination.
|
2026-01-09 17:07:50 +01:00
|
|
|
$documentsModel = CollectionHelper::paginate($documentsModel, $perPage);
|
2026-01-08 16:41:24 +01:00
|
|
|
|
|
|
|
|
return response()->json([
|
2026-02-10 15:43:47 +01:00
|
|
|
'pagination' => [
|
|
|
|
|
'total' => $documentsModel->total(),
|
|
|
|
|
'nbItems' => count($documentsModel->items()),
|
2026-01-08 16:41:24 +01:00
|
|
|
'currentPage' => $documentsModel->currentPage(),
|
2026-02-10 15:43:47 +01:00
|
|
|
'lastPage' => $documentsModel->lastPage(),
|
|
|
|
|
'hasMore' => $documentsModel->currentPage() < $documentsModel->lastPage(),
|
2026-01-08 16:41:24 +01:00
|
|
|
],
|
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()
|
2026-01-08 16:41:24 +01:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|