Overhaul pagination

This commit is contained in:
Jérémy Munsch 2023-09-06 09:53:52 +02:00
parent 61e900ee32
commit c0bce88e75
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
8 changed files with 253 additions and 52 deletions

View file

@ -0,0 +1,16 @@
<?php
namespace App\Enums\Pagination;
use App\Traits\Enums\BaseEnum;
enum ItemsPerPaginationEnum: int
{
use BaseEnum;
case five = 5;
case twelve = 12;
case twentyfive = 25;
case fifty = 50;
case onehundred = 100;
}

View file

@ -2,13 +2,18 @@
namespace App\Http\Controllers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
class Controller extends BaseController
{
@ -122,4 +127,26 @@ class Controller extends BaseController
}
return $query;
}
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function paginate(Builder $query, ItemsPerPaginationEnum $pagination = null): LengthAwarePaginator
{
$currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}";
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve->value;
$pagination = ToolboxHelper::getValidatedPaginate(
Cache::get($cacheKey, $pagination)
);
if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination);
}
$query = $query->paginate($pagination);
return $query;
}
}

View file

@ -38,7 +38,7 @@ class ProductController extends Controller
$this->sortQuery($products);
$products = $products->paginate(10);
$products = $this->paginate($products);
/** @var array $printMultiProduct Array of id */
$printMultiProduct = session('printMultiProduct') ?? [];

View file

@ -2,6 +2,7 @@
namespace App\Lib\Helpers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
@ -9,6 +10,7 @@ use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -288,4 +290,22 @@ class ToolboxHelper
{
return (array_diff($array1, $array2) == [] && array_diff($array2, $array1) == []);
}
/**
* Get validated pagination from request
*
* @param integer $default
* @return integer
*/
public static function getValidatedPaginate(int $default): int
{
try {
return \intval(Validator::make(
['pagination' => \request()->get('pagination')],
['pagination' => new Enum(ItemsPerPaginationEnum::class)]
)->validated()['pagination']);
} catch (ValidationException $e) {
return $default;
}
}
}

View file

@ -0,0 +1,83 @@
<?php
return [
'actions' => [
'list' => 'Liste',
'view' => 'Voir',
'create' => 'Créer',
'duplicate' => 'Dupliquer',
'update' => 'Éditer',
'delete' => 'Supprimer',
'save' => 'Sauvegarder',
'edit' => 'Mettre à jour',
],
'helpers' => [
'list' => 'Liste',
'list_all' => 'Lister tous les :model',
'create_model' => 'Créer :model',
'view_model' => 'Voir :model',
'update_model' => 'Éditer :model',
'duplicate_model' => 'Dupliquer :model',
'delete_model' => 'Supprimer :model',
'save_model' => 'Sauvegarder :model',
'edit_model' => 'Mettre à jour :model',
'no_results' => 'Aucun résultat pour :model',
'publish' => 'Publié',
'unpublish' => 'Dépublié',
],
'libs' => [
'toolbox' => [
'assertFieldIsUnique' => 'La :valeur du champ :attribut est déjà utilisée',
'assertNoInternalBoUrls' => ':attribute : Il est interdit de coller l\'uri de back-office dans ce champ',
'assertNoInternalUrls' => ':attribute : Il est interdit de coller une url de :appname dans ce champ.',
]
],
'messages' => [
'has_been_created' => ':model a été créé',
'cannot_be_created' => ':model ne peut être créé',
'has_been_updated' => ':model a été mis à jour',
'cannot_be_updated' => ':model ne peut être mis à jour',
'has_been_deleted' => ':model a été supprimé',
'cannot_be_deleted' => ':model ne peut être supprimé',
'order_changed' => 'L\'ordre a été changé !',
'order_not_changed' => 'L\'ordre n\'a pas été changé !',
'publish_status_saved' => 'Changement de l\'état de publication réussit !',
'right' => 'Vous n\'avez pas les droits pour accéder à cette partie !'
],
'minicrud' => [
'visibility_label' => 'Visibilité',
'visibility_helper' => 'Vérifier qu\'il est visible.',
'enum_select' => 'Sélectionnez :enum dans la liste.',
'associated_enum' => 'Associé :enum',
'dimensions_must_be' => 'Doit être :widthpx (largeur) par :heightpx (hauteur).',
'picture_title_helper' => 'Utile pour les utilisateurs, s\'affiche en passant la souris sur l\'image',
'picture_title_label' => 'Attribut du titre de l\'image',
'picture_alt_helper' => 'Doit décrire votre image pour les moteurs de recherche et les aveugles (5 à 6 mots)',
'picture_alt_label' => 'Attribut Alt de l\'image',
'text_warning' => 'Relisez votre contenu avant de le publier',
],
'pagination' => [
"previous" => "Page précédente",
"next" => "Page suivante",
"specific_page" => "Accéder à la page n°:id",
'paginate_list' => 'Nombre d\'éléments par page',
],
'other' => [
'up' => 'Modifier l\'ordre d\'apparition vers le haut',
'down' => 'Modifier l\'ordre d\'apparition vers le bas',
'no_model_found' => 'Aucun :model trouvés',
'publish' => 'Publié',
'unpublish' => 'Dépublié',
'sort_ascending' => 'Trier :name dans l\'ordre croissant',
'sort_descending' => 'Trier :name dans l\'ordre décroissant',
'sort_delete' => 'Supprimer le tri',
'sort_arrow' => 'Ici, les flèches sont utilisées pour trier et non pour filtrer',
'right' => 'Vous n\'avez pas les droits',
]
];

View file

@ -15,6 +15,11 @@
white-space: nowrap;
}
.btn-md {
min-width: 38px;
height: 38px;
}
// Print fiche commerciale
@media print {
#print-id-product {

View file

@ -1,5 +1,6 @@
import { config, dom, library } from "~/@fortawesome/fontawesome-svg-core";
import {
faAnglesRight,
faArrowAltCircleDown,
faArrowAltCircleUp,
faArrowDown,
@ -12,6 +13,9 @@ import {
faArrowsAltV,
faBackspace,
faCheck,
faChevronLeft,
faChevronRight,
faCircleNotch,
faCopy,
faCrop,
faDownload,
@ -20,8 +24,10 @@ import {
faFile,
faFilePdf,
faFolderOpen,
faHeading,
faImage,
faLink,
faListUl,
faMinus,
faPencil,
faPlus,
@ -79,5 +85,11 @@ library.add(faPrint);
library.add(faImage);
library.add(faDownload);
library.add(faFilePdf);
library.add(faListUl);
library.add(faHeading);
library.add(faAnglesRight);
library.add(faChevronLeft);
library.add(faChevronRight);
library.add(faCircleNotch);
dom.watch();

View file

@ -1,60 +1,98 @@
@if ($paginator->hasPages())
<nav>
<ul class="pagination d-flex justify-content-center align-items-center m-0 mt-3">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="page-item disabled" data-bs="tooltip" data-bs-placement="top"
title="{{ __('Page précédente') }}" aria-disabled="true" aria-label="@lang('Page précédente')">
<span class="page-link" aria-hidden="true">&lsaquo;</span>
<nav class="d-flex justify-content-between align-items-center my-3">
{{-- SELECT ITEMS PER PAGE --}}
<div class="dropup-center dropup d-flex justify-content-center align-items-center input-group w-fit">
<span class="btn-md input-group-text" data-bs-toggle="tooltip" data-bs-placement="top"
title="{{ __('crud.pagination.paginate_list') }}">
<i class="fa-solid fa-list-ul"></i>
</span>
<button class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" type="button" aria-expanded="false">
{{ Cache::get('pagination.' .Str::slug(request()->route()->getName())) }}
</button>
<ul class="dropdown-menu">
@foreach (\App\Enums\Pagination\ItemsPerPaginationEnum::toArray() as $itemsPerPaginationEnum)
<li>
<a class="dropdown-item text-center"
href="{{ request()->fullUrlWithQuery(['pagination' => $itemsPerPaginationEnum->value]) }}">
{{ $itemsPerPaginationEnum->value }}
</a>
</li>
@else
<li class="page-item">
<a class="page-link" data-bs="tooltip" data-bs-placement="top"
title="{{ __('Page précédente') }}" href="{{ $paginator->previousPageUrl() }}"
rel="prev" aria-label="@lang('Page précédente')">&lsaquo;</a>
@endforeach
</ul>
</div>
@if ($paginator->hasPages())
{{-- PAGINATION --}}
<div class="d-flex justify-content-end align-items-center">
<ul class="pagination m-0">
{{-- PREVIOUS PAGE --}}
<li class="page-item @if ($paginator->onFirstPage()) disabled @endif">
<a class="page-link d-flex align-items-center h-100" data-bs-toggle="tooltip" data-bs-placement="top"
title="{{ __('crud.pagination.previous') }}" aria-label="@lang('crud.pagination.previous')"
@if (!$paginator->onFirstPage()) href="{{ $paginator->previousPageUrl() }}" @endif rel="prev"
@if ($paginator->onFirstPage()) aria-hidden="true" @endif>
<i class="fa-solid fa-chevron-left fa-2xs mt-1"></i>
</a>
</li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="page-item disabled" aria-disabled="true"><span
class="page-link">{{ $element }}</span></li>
{{-- FIRST PAGE --}}
@if ($paginator->currentPage() > 3)
<li class="page-item" data-bs-toggle="tooltip" data-bs-placement="top"
title="{{ __('crud.pagination.specific_page', ['id' => 1]) }}">
<a class="page-link" href="{{ $paginator->url(1) }}">1</a>
</li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="page-item active" data-bs="tooltip" data-bs-placement="top"
title="{{ __('Page actuelle') }}" aria-current="page">
<span class="page-link">{{ $page }}</span>
{{-- HIDE PAGES TOO FAR --}}
@if ($paginator->currentPage() > 4)
<li class="page-item disabled"><span class="page-link">...</span></li>
@endif
{{-- SHOW ACTUAL PAGES + N-2/N+2 --}}
@foreach (range(1, $paginator->lastPage()) as $i)
@if ($i >= $paginator->currentPage() - 2 && $i <= $paginator->currentPage() + 2)
@if ($i == $paginator->currentPage())
<li class="page-item">
<div class="dropup-center dropup">
<button class="btn btn-primary dropdown-toggle rounded-0" data-bs-toggle="dropdown" type="button"
aria-expanded="false">
{{ $i }}
</button>
<ul class="dropdown-menu">
@for ($i = 1; $i <= $paginator->lastPage(); $i++)
<li>
<a class="dropdown-item text-center"
href="{{ request()->fullUrlWithQuery(['page' => $i]) }}">
{{ $i }}
</a>
</li>
@endfor
</ul>
</div>
</li>
@else
<li class="page-item">
<a class="page-link" data-bs="tooltip" data-bs-placement="top"
title="{{ __('Page n°:id', ['id' => $page]) }}"
href="{{ $url }}">{{ $page }}</a>
<li class="page-item" data-bs-toggle="tooltip" data-bs-placement="top"
title="{{ __('crud.pagination.specific_page', ['id' => $i]) }}">
<a class="page-link" href="{{ $paginator->url($i) }}">{{ $i }}</a>
</li>
@endif
@endforeach
@endif
@endforeach
{{-- HIDE PAGES TOO FAR --}}
@if ($paginator->currentPage() < $paginator->lastPage() - 3)
<li class="page-item disabled"><span class="page-link">...</span></li>
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li class="page-item">
<a class="page-link" data-bs="tooltip" data-bs-placement="top" title="{{ __('Page suivante') }}"
href="{{ $paginator->nextPageUrl() }}" rel="next"
aria-label="@lang('Page suivante')">&rsaquo;</a>
{{-- LAST PAGE --}}
@if ($paginator->currentPage() < $paginator->lastPage() - 2)
<li class="page-item" data-bs-toggle="tooltip" data-bs-placement="top"
title="{{ __('crud.pagination.specific_page', ['id' => $paginator->lastPage()]) }}">
<a class="page-link" href="{{ $paginator->url($paginator->lastPage()) }}">{{ $paginator->lastPage() }}</a>
</li>
@endif
{{-- NEXT PAGE --}}
<li class="page-item @if (!$paginator->hasMorePages()) disabled @endif">
<a class="page-link d-flex align-items-center h-100" data-bs-toggle="tooltip" data-bs-placement="top"
title="{{ __('crud.pagination.next') }}" aria-label="@lang('crud.pagination.next')"
@if ($paginator->hasMorePages()) href="{{ $paginator->nextPageUrl() }}" @endif rel="next"
@if (!$paginator->hasMorePages()) aria-hidden="true" @endif>
<i class="fa-solid fa-chevron-right fa-2xs mt-1"></i></a>
</li>
@else
<li class="page-item disabled" data-bs="tooltip" data-bs-placement="top"
title="{{ __('Page suivante') }}" aria-disabled="true" aria-label="@lang('Page suivante')">
<span class="page-link" aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</nav>
@endif
</ul>
</div>
@endif
</nav>