Migrate pagination from Widget to new API

This commit is contained in:
Daniel Siepmann 2022-08-02 15:15:47 +00:00
parent 65e653f6ec
commit 8d14834db9
13 changed files with 422 additions and 121 deletions

View file

@ -0,0 +1,178 @@
<?php
declare(strict_types=1);
namespace Wrm\Events\Backports\V12\Pagination;
// Original source: https://github.com/TYPO3/typo3/blob/b60cf59fbe7875aff5ee1ba4c56155301694d6b8/typo3/sysext/core/Classes/Pagination/SlidingWindowPagination.php
// Remove once we have v12 as minimum version.
use TYPO3\CMS\Core\Pagination\PaginationInterface;
use TYPO3\CMS\Core\Pagination\PaginatorInterface;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
final class SlidingWindowPagination implements PaginationInterface
{
/**
* @var int
*/
protected $displayRangeStart = 0;
/**
* @var int
*/
protected $displayRangeEnd = 0;
/**
* @var bool
*/
protected $hasLessPages = false;
/**
* @var bool
*/
protected $hasMorePages = false;
/**
* @var int
*/
protected $maximumNumberOfLinks = 0;
/**
* @var PaginatorInterface
*/
protected $paginator;
public function __construct(PaginatorInterface $paginator, int $maximumNumberOfLinks = 0)
{
$this->paginator = $paginator;
if ($maximumNumberOfLinks > 0) {
$this->maximumNumberOfLinks = $maximumNumberOfLinks;
}
$this->calculateDisplayRange();
}
public function getPreviousPageNumber(): ?int
{
$previousPage = $this->paginator->getCurrentPageNumber() - 1;
if ($previousPage > $this->paginator->getNumberOfPages()) {
return null;
}
return $previousPage >= $this->getFirstPageNumber() ? $previousPage : null;
}
public function getNextPageNumber(): ?int
{
$nextPage = $this->paginator->getCurrentPageNumber() + 1;
return $nextPage <= $this->paginator->getNumberOfPages() ? $nextPage : null;
}
public function getFirstPageNumber(): int
{
return 1;
}
public function getLastPageNumber(): int
{
return $this->paginator->getNumberOfPages();
}
public function getStartRecordNumber(): int
{
if ($this->paginator->getCurrentPageNumber() > $this->paginator->getNumberOfPages()) {
return 0;
}
return $this->paginator->getKeyOfFirstPaginatedItem() + 1;
}
public function getEndRecordNumber(): int
{
if ($this->paginator->getCurrentPageNumber() > $this->paginator->getNumberOfPages()) {
return 0;
}
return $this->paginator->getKeyOfLastPaginatedItem() + 1;
}
public function getAllPageNumbers(): array
{
return range($this->displayRangeStart, $this->displayRangeEnd);
}
public function getDisplayRangeStart(): int
{
return $this->displayRangeStart;
}
public function getDisplayRangeEnd(): int
{
return $this->displayRangeEnd;
}
public function getHasLessPages(): bool
{
return $this->hasLessPages;
}
public function getHasMorePages(): bool
{
return $this->hasMorePages;
}
public function getMaximumNumberOfLinks(): int
{
return $this->maximumNumberOfLinks;
}
public function getPaginator(): PaginatorInterface
{
return $this->paginator;
}
protected function calculateDisplayRange(): void
{
$maximumNumberOfLinks = $this->maximumNumberOfLinks;
$numberOfPages = $this->paginator->getNumberOfPages();
if ($maximumNumberOfLinks > $numberOfPages) {
$maximumNumberOfLinks = $numberOfPages;
}
$currentPage = $this->paginator->getCurrentPageNumber();
$delta = floor($maximumNumberOfLinks / 2);
$this->displayRangeStart = (int)($currentPage - $delta);
$this->displayRangeEnd = (int)($currentPage + $delta - ($maximumNumberOfLinks % 2 === 0 ? 1 : 0));
if ($this->displayRangeStart < 1) {
$this->displayRangeEnd -= $this->displayRangeStart - 1;
}
if ($this->displayRangeEnd > $numberOfPages) {
$this->displayRangeStart -= $this->displayRangeEnd - $numberOfPages;
}
$this->displayRangeStart = (int)max($this->displayRangeStart, 1);
$this->displayRangeEnd = (int)min($this->displayRangeEnd, $numberOfPages);
$this->hasLessPages = $this->displayRangeStart > 2;
$this->hasMorePages = $this->displayRangeEnd + 1 < $this->paginator->getNumberOfPages();
}
}

View file

@ -14,6 +14,7 @@ use Wrm\Events\Domain\Repository\DateRepository;
use Wrm\Events\Domain\Repository\RegionRepository;
use Wrm\Events\Events\Controller\DateListVariables;
use Wrm\Events\Events\Controller\DateSearchVariables;
use Wrm\Events\Pagination\Factory;
use Wrm\Events\Service\DataProcessingForModels;
/**
@ -46,6 +47,11 @@ class DateController extends AbstractController
*/
protected $eventDispatcher;
/**
* @var Factory
*/
protected $paginationFactory;
/**
* @var DataProcessingForModels
*/
@ -57,7 +63,8 @@ class DateController extends AbstractController
DateRepository $dateRepository,
CategoryRepository $categoryRepository,
DataProcessingForModels $dataProcessing,
EventDispatcher $eventDispatcher
EventDispatcher $eventDispatcher,
Factory $paginationFactory
) {
$this->demandFactory = $demandFactory;
$this->regionRepository = $regionRepository;
@ -65,6 +72,7 @@ class DateController extends AbstractController
$this->categoryRepository = $categoryRepository;
$this->dataProcessing = $dataProcessing;
$this->eventDispatcher = $eventDispatcher;
$this->paginationFactory = $paginationFactory;
}
protected function initializeAction(): void
@ -78,9 +86,12 @@ class DateController extends AbstractController
/**
* @param array $search
* @param int $currentPage
*/
public function listAction(array $search = []): void
{
public function listAction(
array $search = [],
int $currentPage = 1
): void {
$demand = $this->demandFactory->fromSettings($this->settings);
if ($search !== []) {
$demand = DateDemand::createFromRequestValues($search, $this->settings);
@ -94,10 +105,17 @@ class DateController extends AbstractController
$demand = $this->createDemandFromSearch();
}
$dates = $this->dateRepository->findByDemand($demand);
$event = $this->eventDispatcher->dispatch(new DateListVariables(
$search,
$demand,
$this->dateRepository->findByDemand($demand)
$dates,
$this->paginationFactory->create(
$currentPage,
$this->settings['itemsPerPage'] ?? 25,
$this->settings['maximumLinks'] ?? 5,
$dates
)
));
if (!$event instanceof DateListVariables) {
throw new \Exception('Did not retrieve DateSearchVariables from event dispatcher, got: ' . get_class($event), 1657542318);

View file

@ -3,6 +3,7 @@
namespace Wrm\Events\Events\Controller;
use TYPO3\CMS\Core\Pagination\PaginationInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult;
use Wrm\Events\Domain\Model\Date;
use Wrm\Events\Domain\Model\Dto\DateDemand;
@ -24,6 +25,11 @@ final class DateListVariables
*/
private $dates;
/**
* @var PaginationInterface
*/
private $pagination;
/**
* @var array
*/
@ -32,11 +38,13 @@ final class DateListVariables
public function __construct(
array $search,
DateDemand $demand,
QueryResult $dates
QueryResult $dates,
PaginationInterface $pagination
) {
$this->search = $search;
$this->demand = $demand;
$this->dates = $dates;
$this->pagination = $pagination;
}
public function getSearch(): array
@ -71,6 +79,7 @@ final class DateListVariables
'search' => $this->search,
'demand' => $this->demand,
'dates' => $this->dates,
'pagination' => $this->pagination,
], $this->variables);
}
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Wrm\Events\Pagination;
use TYPO3\CMS\Core\Pagination\PaginationInterface;
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
use Wrm\Events\Backports\V12\Pagination\SlidingWindowPagination;
class Factory
{
public function create(
int $currentPage,
int $itemsPerPage,
int $maximumLinks,
QueryResultInterface $items
): PaginationInterface {
return new SlidingWindowPagination(
new QueryResultPaginator($items, $currentPage, $itemsPerPage),
$maximumLinks
);
}
}

View file

@ -22,6 +22,12 @@ Features
inline properties within the event.
This allows to filter by location.
* Provide new stable Pagination within Controller and used Templates.
This makes the Extension compatible with v10, v11 and v12 regarding pagination.
The template now also follows https://www.a11ymatters.com/pattern/pagination/ for
accessibility.
Feel free to copy the template to projects.
Fixes
-----

View file

@ -37,6 +37,7 @@ Table of Contents
Commands
Changelog
Maintenance
Clean category relations
========================

View file

@ -0,0 +1,16 @@
.. _maintenance:
Maintenance
===========
List of changes that need to be done for maintenance reasons.
E.g. changes once we drop a certain TYPO3 version.
We might have new code backported for compatibility in older TYPO3 versions.
Those changes are documented so we know what to do once we drop an older version.
.. toctree::
:glob:
:reversed:
Maintenance/TYPO3/*

View file

@ -0,0 +1,12 @@
TYPO3 V11
=========
Changes that should happen once we drop TYPO3 v11.
Remove ``SlidingWindowPagination`` backport.
--------------------------------------------
We backported https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.0/Feature-94625-IntroduceSlidingWindowPagination.html.
That allowed us to use the new Code compatible with v12 onwards.
We should remove the file ``Classes/Backports/V12/Pagination/SlidingWindowPagination.php`` and switch to TYPO3 native class within ``Wrm\Events\Pagination\Factory``.

View file

@ -1,8 +1,9 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<f:widget.paginate objects="{dates}" as="paginatedDates" configuration="{itemsPerPage: 25, insertAbove: 0, insertBelow: 1, maximumNumberOfLinks: 5, addQueryStringMethod: 'GET'}">
<f:for each="{paginatedDates}" as="date" iteration="index">
<f:for each="{pagination.paginator.paginatedItems}" as="date" iteration="index">
<div class="row mt-3 mb-3 pb-3">
<div class="col-2">
<b><f:format.date format="H:i">{date.start}</f:format.date></b><br>
@ -49,6 +50,9 @@
</f:else>
</f:if>
</f:for>
</f:widget.paginate>
{f:render(partial: 'Pagination', arguments: {
pagination: pagination
})}
</html>

View file

@ -0,0 +1,106 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
data-namespace-typo3-fluid="true">
<nav role="navigation" aria-label="Pagination Navigation">
<ul class="pagination">
<f:if condition="{pagination.previousPageNumber}">
<li class="page-item">
<f:if condition="{pagination.previousPageNumber} > 1">
<f:then>
<a class="page-link"
href="{f:uri.action(arguments: {currentPage: pagination.previousPageNumber})}"
>
<span aria-hidden="true">&laquo;</span>
</a>
</f:then>
<f:else>
<a class="page-link" href="{f:uri.action()}">
<span aria-hidden="true">&laquo;</span>
</a>
</f:else>
</f:if>
</li>
</f:if>
<f:if condition="{pagination.displayRangeStart} > 1">
<li class="page-item">
<a class="page-link"
href="{f:uri.action()}"
aria-label="Goto Page 1"
>
1
</a>
</li>
</f:if>
<f:if condition="{pagination.hasLessPages}">
<li class="page-item">
<span class="page-link"></span>
</li>
</f:if>
<f:for each="{pagination.allPageNumbers}" as="page">
<f:if condition="{page} == {pagination.paginator.currentPageNumber}">
<f:then>
<li class="page-item active">
<span class="page-link"
aria-label="Current Page {page}"
aria-current="true"
>{page}</span>
</li>
</f:then>
<f:else>
<li class="page-item">
<f:if condition="{page} > 1">
<f:then>
<a class="page-link"
href="{f:uri.action(arguments: {currentPage: page})}"
aria-label="Goto Page {page}"
>
{page}
</a>
</f:then>
<f:else>
<a class="page-link"
href="{f:uri.action()}"
aria-label="Goto Page 1"
>
1
</a>
</f:else>
</f:if>
</li>
</f:else>
</f:if>
</f:for>
<f:if condition="{pagination.hasMorePages}">
<li class="page-item">
<span class="page-link"></span>
</li>
</f:if>
<f:if condition="{pagination.displayRangeEnd} < {pagination.lastPageNumber}">
<li class="page-item">
<a class="page-link"
href="{f:uri.action(arguments: {currentPage: pagination.lastPageNumber})}"
aria-label="Goto Page {pagination.lastPageNumber}"
>
{pagination.lastPageNumber}
</a>
</li>
</f:if>
<f:if condition="{pagination.nextPageNumber}">
<li class="page-item">
<a class="page-link"
href="{f:uri.action(arguments: {currentPage: pagination.nextPageNumber})}"
>
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</f:if>
</ul>
</nav>
</html>

View file

@ -1,16 +1,19 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<f:layout name="Default" />
<f:section name="content">
<f:switch expression="{settings.template}">
<f:case value="table">
<f:render partial="Date/ListTable" arguments="{dates:dates}" />
</f:case>
<f:case value="grid">
<f:render partial="Date/ListDefault" arguments="{dates:dates}" />
</f:case>
<f:defaultCase>
<f:render partial="Date/ListDefault" arguments="{dates:dates}" />
</f:defaultCase>
</f:switch>
<f:if condition="{settings.template} === 'table'">
<f:then>
{f:render(partial: 'Date/ListTable', arguments: _all)}
</f:then>
<f:else>
{f:render(partial: 'Date/ListDefault', arguments: _all)}
</f:else>
</f:if>
</f:section>
</html>

View file

@ -1,91 +0,0 @@
<f:if condition="{configuration.insertAbove}">
<f:render section="paginator" arguments="{pagination: pagination, configuration: configuration}" />
</f:if>
<f:renderChildren arguments="{contentArguments}" />
<f:if condition="{configuration.insertBelow}">
<f:render section="paginator" arguments="{pagination: pagination, configuration: configuration}" />
</f:if>
<f:section name="paginator">
<nav>
<ul class="pagination">
<f:if condition="{pagination.previousPage}">
<li class="page-item">
<f:if condition="{pagination.previousPage} > 1">
<f:then>
<f:widget.link class="page-link" rel="prev"
arguments="{currentPage: pagination.previousPage}"
addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}"><span aria-hidden="true">&laquo;</span>
</f:widget.link>
</f:then>
<f:else>
<f:widget.link class="page-link" rel="prev"
addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}"><span aria-hidden="true">&laquo;</span>
</f:widget.link>
</f:else>
</f:if>
</li>
</f:if>
<f:if condition="{pagination.displayRangeStart} > 1">
<li class="page-item">
<f:widget.link class="page-link" addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}">1
</f:widget.link>
</li>
</f:if>
<f:if condition="{pagination.hasLessPages}">
<li class="page-item"><span class="page-link"></span></li>
</f:if>
<f:for each="{pagination.pages}" as="page">
<f:if condition="{page.isCurrent}">
<f:then>
<li class="page-item active">
<span class="page-link">{page.number}</span>
</li>
</f:then>
<f:else>
<li class="page-item">
<f:if condition="{page.number} > 1">
<f:then>
<f:widget.link class="page-link" arguments="{currentPage: page.number}"
addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}">{page.number}
</f:widget.link>
</f:then>
<f:else>
<f:widget.link class="page-link"
addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}">{page.number}
</f:widget.link>
</f:else>
</f:if>
</li>
</f:else>
</f:if>
</f:for>
<f:if condition="{pagination.hasMorePages}">
<li class="page-item"><span class="page-link"></span></li>
</f:if>
<f:if condition="{pagination.displayRangeEnd} < {pagination.numberOfPages}">
<li class="page-item">
<f:widget.link class="page-link" arguments="{currentPage: pagination.numberOfPages}"
addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}">{pagination.numberOfPages}
</f:widget.link>
</li>
</f:if>
<f:if condition="{pagination.nextPage}">
<li class="page-item">
<f:widget.link class="page-link" rel="next" arguments="{currentPage: pagination.nextPage}"
addQueryStringMethod="{configuration.addQueryStringMethod}"
section="{configuration.section}"><span aria-hidden="true">&raquo;</span>
</f:widget.link>
</li>
</f:if>
</ul>
</nav>
</f:section>

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Wrm\Tests\Unit\Events\Controller;
use PHPUnit\Framework\TestCase;
use TYPO3\CMS\Core\Pagination\PaginationInterface;
use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult;
use Wrm\Events\Domain\Model\Dto\DateDemand;
use Wrm\Events\Events\Controller\DateListVariables;
@ -25,7 +26,8 @@ class DateListVariablesTest extends TestCase
$subject = new DateListVariables(
[],
new DateDemand(),
$this->prophesize(QueryResult::class)->reveal()
$this->prophesize(QueryResult::class)->reveal(),
$this->prophesize(PaginationInterface::class)->reveal()
);
self::assertInstanceOf(
@ -44,7 +46,8 @@ class DateListVariablesTest extends TestCase
'executed' => '1',
],
new DateDemand(),
$this->prophesize(QueryResult::class)->reveal()
$this->prophesize(QueryResult::class)->reveal(),
$this->prophesize(PaginationInterface::class)->reveal()
);
self::assertSame(
@ -65,7 +68,8 @@ class DateListVariablesTest extends TestCase
[
],
$demand,
$this->prophesize(QueryResult::class)->reveal()
$this->prophesize(QueryResult::class)->reveal(),
$this->prophesize(PaginationInterface::class)->reveal()
);
self::assertSame(
@ -84,7 +88,8 @@ class DateListVariablesTest extends TestCase
[
],
new DateDemand(),
$dates
$dates,
$this->prophesize(PaginationInterface::class)->reveal()
);
self::assertSame(
@ -100,12 +105,14 @@ class DateListVariablesTest extends TestCase
{
$demand = new DateDemand();
$dates = $this->prophesize(QueryResult::class)->reveal();
$pagination = $this->prophesize(PaginationInterface::class)->reveal();
$subject = new DateListVariables(
[
'executed' => '1',
],
$demand,
$dates
$dates,
$pagination
);
self::assertSame(
@ -115,6 +122,7 @@ class DateListVariablesTest extends TestCase
],
'demand' => $demand,
'dates' => $dates,
'pagination' => $pagination,
],
$subject->getVariablesForView()
);
@ -127,12 +135,14 @@ class DateListVariablesTest extends TestCase
{
$demand = new DateDemand();
$dates = $this->prophesize(QueryResult::class)->reveal();
$pagination = $this->prophesize(PaginationInterface::class)->reveal();
$subject = new DateListVariables(
[
'executed' => '1',
],
$demand,
$dates
$dates,
$pagination
);
$subject->addVariable('variable 1', 'Value 1');
@ -145,6 +155,7 @@ class DateListVariablesTest extends TestCase
],
'demand' => $demand,
'dates' => $dates,
'pagination' => $pagination,
'variable 1' => 'Value 1',
'variable 2' => 'Value 2',
],
@ -159,12 +170,14 @@ class DateListVariablesTest extends TestCase
{
$demand = new DateDemand();
$dates = $this->prophesize(QueryResult::class)->reveal();
$pagination = $this->prophesize(PaginationInterface::class)->reveal();
$subject = new DateListVariables(
[
'executed' => '1',
],
$demand,
$dates
$dates,
$pagination
);
$subject->addVariable('variable 1', 'Value 1');
@ -178,6 +191,7 @@ class DateListVariablesTest extends TestCase
],
'demand' => $demand,
'dates' => 'Value 2',
'pagination' => $pagination,
'variable 1' => 'Value 2',
],
$subject->getVariablesForView()