Aller au contenu principal

Nouvelle API de pagination du core / suppression des widgets FLUID avec Typo3 V11

Créé par Rival Florian | | CoreExtensionTYPO3

À partir de la version 11 de Typo3, les widgets Fluid vont être supprimés du core : autocomplete, link, paginate et uri vont donc disparaître. Il n'est pas prévu de migration simple et automatique pour les remplacer, cet article explique comment migrer le widget paginate vers la nouvelle API de pagination du core.

 

Suppression de tous les widget du core Typo3

Le problème le plus important des widgets Fluid est qu'ils lancent des sous-requêtes à un contrôleur à partir d'une vue et génèrent leur propre contenu.

Ces sous-requêtes apportent une autre couche de complexité au CMS TYPO3 qu'il est impossible de gérer correctement comme le montre de nombreux rapports de bug.

Ces widgets violent le patron de conception "separation of concern" et apportent plus d'inconvénients que de bénéfices, ils seront donc supprimés.

https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Breaking-92529-AllFluidWidgetFunctionalityRemoved.html

Migration du widget de pagination Fluid vers l'API de pagination du core

L'un des widgets utilisé majoritairement au sein des sites Typo3 est le widget de pagination <f:widget.paginate> utilisé pour ajouter une pagination à une collection d'objets.

À partir de la version 10.2, une nouvelle API de pagination a été ajoutée au core TYPO3, voyons comment l'utiliser.


Configuration basique d'une extension Extbase

J'ai une extension qui affiche une liste d'objets dans une vue.

Voilà un aperçu du contrôleur sans aucune pagination :

 

public function listAction()
{
    $productIds = explode(',', $this->settings['items'] ?? '');
    $products = $this->productService->getProductsByIds($productIds);
    $this->view->assign('products', $products);
}

Ajout de la pagination à ce contrôleur avec l'API de pagination

La différence majeure avec le widget Fluid de pagination est que la logique de pagination est déplacée dans notre contrôleur principal.
Pour implémenter cette pagination, le core TYPO3 implémente deux objets : ArrayPaginator et SimplePagination.

Extbase fournit également un objet \TYPO3\CMS\Extbase\Pagination\QueryResultPaginator pour permettre la pagination des résultats de requêtes Extbase.

L'exemple suivant montre comment utiliser ces objets pour paginer un tableau de $products :

use TYPO3\CMS\Core\Pagination\ArrayPaginator;
use TYPO3\CMS\Core\Pagination\SimplePagination;

// ...

/**
 * @param int $currentPage
 */
public function listAction(int $currentPage = 1)
{
    $productIds = explode(',', $this->settings['items'] ?? '');
    $products = $this->productService->getProductsByIds($productIds);
    $arrayPaginator = new ArrayPaginator($products, $currentPage, 8);
    $pagination = new SimplePagination($arrayPaginator);
    $this->view->assignMultiple(
        [
            'products' => $products,
            'paginator' => $arrayPaginator,
            'pagination' => $pagination,
            'pages' => range(1, $pagination->getLastPageNumber()),
        ]
    );
}

Les deux objets 'paginator' et 'pagination' doivent être passés à la vue car nous avons besoin des deux pour afficher la pagination et les items paginés. La fonction 'range()' permet de générer les numéros de pages à afficher.

Contexte technique :

Le core de Typo3 fournit une interface pour implémenter la navigation en natif de listes comme : des tableaux ou des résultats de requête Extbase

  • une classe d'interface : PaginatorInterface qui ne connaît pas le type d'objet à paginer va définir les interfaces pour la pagination,
  • une classe abstraite AbstractPaginator implémente la logique de pagination pour un ensemble d'items,
  • une classe concrète : array ou QueryResultInterface

Dans notre exemple :

  • objet Paginator : responsable de la logique de pagination, par exemple, gérer les items à afficher en fonction de la page courante ou calculer le nombre de page requis,
  • objet Pagination : contient la logique d'affichage de la pagination, retourne le numéro de la page suivante ou la listes des items à afficher.

 

Implémentation de la pagination dans TYPO3 Fluid

Nous allons utiliser la variable paginator pour afficher nos produits paginés.

<f:for as="product" each="{paginator.paginatedItems}" iteration="iterator">
    <f:render partial="Product/BoxStandard.html" arguments="{product: product}" />
</f:for>

Le paginateur devrait être dans un partial afin de pouvoir être réutilisable.

<f:render partial="Utility/Paginator.html" arguments="{pagination: pagination, pages: pages, paginator: paginator}" />

Et dans ce partial, voilà ce qui est implémenté :

  • affichage des liens "première page" et "dernière page" (désactivés si inutiles)
  • affichage des liens "page précédente" et "page suivante" (désactivés si inutiles)
  • mise en évidence de la page active
<ul class="pagination pagination-block">
  <f:if condition="{pagination.previousPageNumber} && {pagination.previousPageNumber} >= {pagination.firstPageNumber}">
    <f:then>
      <li class="waves-effect">
        <a href="{f:uri.action(action:actionName, arguments:{currentPage: 1})}" title="{f:translate(key:'pagination.first')}">
          <i class="material-icons">first_page</i>
        </a>
      </li>
      <li class="waves-effect">
        <a href="{f:uri.action(action:actionName, arguments:{currentPage: pagination.previousPageNumber})}" title="{f:translate(key:'pagination.previous')}">
          <i class="material-icons">chevron_left</i>
        </a>
      </li>
    </f:then>
    <f:else>
      <li class="disabled"><a href="#"><i class="material-icons">first_page</i></a></li>
      <li class="disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>
    </f:else>
  </f:if>
  <f:for each="{pages}" as="page">
    <li class="{f:if(condition: '{page} == {paginator.currentPageNumber}', then:'active', else:'waves-effect')}">
      <a href="{f:uri.action(action:actionName, arguments:{currentPage: page})}">{page}</a>
    </li>
  </f:for>

  <f:if condition="{pagination.nextPageNumber} && {pagination.nextPageNumber} <= {pagination.lastPageNumber}">
    <f:then>
      <li class="waves-effect">
        <a href="{f:uri.action(action:actionName, arguments:{currentPage: pagination.nextPageNumber})}" title="{f:translate(key:'pagination.next')}">
          <i class="material-icons">chevron_right</i>
        </a>
      </li>
      <li class="waves-effect">
        <a href="{f:uri.action(action:actionName, arguments:{currentPage: pagination.lastPageNumber})}" title="{f:translate(key:'pagination.last')}">
          <i class="material-icons">last_page</i>
        </a>
      </li>
    </f:then>
    <f:else>
      <li class="disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>
      <li class="disabled"><a href="#"><i class="material-icons">last_page</i></a></li>
    </f:else>
  </f:if>
</ul>

L'extension Numbered Pagination de Georg Ringer

Afin d'améliorer la pagination proposée par le core Typo3, Georg Ringer a réalisé une extension Numbered Pagination qui permet de gérer correctement la pagination pour un très grand nombre d'items.

Exemple : Imaginez que vous ayez 1000 enregistrements et 20 items par page, ce qui génère 50 liens, avec cette extension, vous aurez un rendu de ce type : < 1 2 ... 21 22 23 24 ... 100 >

$itemsPerPage = 10;
$maximumLinks = 15;
$currentPage = $this->request->hasArgument('currentPage') ? (int)$this->request->getArgument('currentPage') : 1;
$paginator = new \TYPO3\CMS\Extbase\Pagination\QueryResultPaginator($allItems, $currentPage, $itemsPerPage);
$pagination = new \GeorgRinger\NumberedPagination\NumberedPagination($paginator, $maximumLinks);
$this->view->assign('pagination', [
    'paginator' => $paginator,
    'pagination' => $pagination,
]);
Retour