337 lines
12 KiB
PHP
337 lines
12 KiB
PHP
<?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 Illuminate\Database\Eloquent\Collection as DatabaseCollection;
|
|
use App\Http\Requests\Front\PostsRequest;
|
|
use Uwil\LaravelCommon\Controllers\Controller;
|
|
use Uwil\LaravelCommon\Helpers\CollectionHelper;
|
|
|
|
class PostsController extends Controller
|
|
{
|
|
/** @var string */
|
|
protected const DOCUMENTS = "documents";
|
|
|
|
/** @var string */
|
|
protected const NEWS = "news";
|
|
|
|
/**
|
|
* Extract unique years from date field in a collection.
|
|
*
|
|
* @param \Illuminate\Support\Collection $model
|
|
* @param string $field
|
|
* @return \Illuminate\Support\Collection
|
|
*/
|
|
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
|
|
*/
|
|
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.
|
|
*
|
|
* @template TTag of \App\Models\DocumentTag|\App\Models\NewsTag
|
|
* @template TModel of \App\Models\Document|\App\Models\News
|
|
*
|
|
* @param string $type
|
|
* @param string $category
|
|
* @param \Illuminate\Database\Eloquent\Collection<int, TTag> $publishedTags
|
|
* @param \Illuminate\Database\Eloquent\Collection<int, TModel> $model
|
|
* @return array
|
|
*/
|
|
private function getFilters(
|
|
string $type, string $category,
|
|
\Illuminate\Database\Eloquent\Collection $publishedTags,
|
|
\Illuminate\Database\Eloquent\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 === 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()
|
|
]);
|
|
}
|
|
}
|