Skip to content

Commit

Permalink
Support pagination with search parameters
Browse files Browse the repository at this point in the history
Ensure the parameters are passed on to new generated links.
Cover things with tests.

Relates: #11574
  • Loading branch information
d-s-codappix committed Jan 16, 2025
1 parent 4d9c2dc commit b8a2a69
Show file tree
Hide file tree
Showing 24 changed files with 757 additions and 17 deletions.
45 changes: 29 additions & 16 deletions Classes/Controller/DateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Exception;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Service\ExtensionService;
use WerkraumMedia\Events\Domain\Model\Date;
Expand Down Expand Up @@ -38,13 +39,13 @@ protected function initializeAction(): void
{
parent::initializeAction();

$this->handlePostRequests();

$contentObject = $this->request->getAttribute('currentContentObject');
if ($contentObject !== null) {
$this->demandFactory->setContentObjectRenderer($contentObject);
}
$this->dataProcessing->setConfigurationManager($this->configurationManager);

$this->handlePostRequest();
}

public function listAction(
Expand Down Expand Up @@ -127,22 +128,34 @@ public function showAction(?Date $date = null): ResponseInterface
*
* @see: https://en.wikipedia.org/wiki/Post/Redirect/Get
*/
private function handlePostRequest(): void
private function handlePostRequests(): void
{
if (
$this->request->getMethod() === 'POST'
&& $this->request->hasArgument('search')
&& is_array($this->request->getArgument('search'))
) {
$namespace = $this->extensionService->getPluginNamespace(null, null);
if ($this->request->getMethod() !== 'POST') {
return;
}

$searchArguments = [];
if ($this->request->hasArgument('search')) {
$searchArguments = $this->request->getArgument('search');
}
if (is_array($searchArguments) === false) {
$searchArguments = [];
}
$searchArguments = array_filter($searchArguments);

$parameter = [];
if ($searchArguments !== []) {
$parameter['search'] = $searchArguments;
}

$namespace = $this->extensionService->getPluginNamespace(null, null);

throw new PropagateResponseException(
$this->redirectToUri($this->request->getAttribute('currentContentObject')->typoLink_URL([
'parameter' => 't3://page?uid=current',
'additionalParams' => '&' . http_build_query([
$namespace => [
'search' => array_filter($this->request->getArgument('search')),
],
]),
]));
}
'additionalParams' => '&' . http_build_query([$namespace => $parameter]),
])),
303
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

/*
* Copyright (C) 2025 Daniel Siepmann <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

namespace WerkraumMedia\Events\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Routing\PageArguments;
use TYPO3\CMS\Core\Utility\ArrayUtility;

/**
* Right now I couldn't find any way to add the search parameter to the pagination via addQueryString.
* The issue is that these are not part of TYPO3 routing. Nested lists like categories also seem impossible to configure for routing.
*
* See corresponding core issue: https://forge.typo3.org/issues/105941
*/
final class AddSearchArgumentsToRouteArgumentsMiddleware implements MiddlewareInterface
{
public function __construct(
private readonly string $routingPath = 'events/search',
) {
}

public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$previousResult = $request->getAttribute('routing', null);
if ($previousResult instanceof PageArguments) {
$request = $request->withAttribute('routing', $this->extendPageArguments($previousResult));
}

return $handler->handle($request);
}

private function extendPageArguments(PageArguments $pageArguments): PageArguments
{
$dynamicArguments = $pageArguments->getDynamicArguments();

if (ArrayUtility::isValidPath($dynamicArguments, $this->routingPath) === false) {
return $pageArguments;
}

$routeArguments = ArrayUtility::setValueByPath(
$pageArguments->getRouteArguments(),
$this->routingPath,
ArrayUtility::getValueByPath($dynamicArguments, $this->routingPath)
);
$remainingArguments = ArrayUtility::removeByPath($dynamicArguments, $this->routingPath);

return new PageArguments(
$pageArguments->getPageId(),
$pageArguments->getPageType(),
$routeArguments,
$pageArguments->getStaticArguments(),
$remainingArguments,
);
}
}
4 changes: 3 additions & 1 deletion Configuration/CeRouting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ routeEnhancers:
EventsPagination:
type: Plugin
namespace: events
routePath: '/{localizedPage}-{currentPage}'
routePath: '/{localizedPage}-{currentPage}/{controller}'
defaults:
controller: 'Date'
aspects:
localizedPage:
type: LocaleModifier
Expand Down
19 changes: 19 additions & 0 deletions Configuration/RequestMiddlewares.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

use WerkraumMedia\Events\Middleware\AddSearchArgumentsToRouteArgumentsMiddleware;

return [
'frontend' => [
'werkraummedia/events/add-search-arguments-to-route-arguments' => [
'target' => AddSearchArgumentsToRouteArgumentsMiddleware::class,
'after' => [
'typo3/cms-frontend/page-resolver',
],
'before' => [
'typo3/cms-frontend/page-argument-validator',
],
],
],
];
3 changes: 3 additions & 0 deletions Documentation/Changelog/5.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ Fixes
* Ensure pagination settings are provided in expected type (int).
TypoScript settings will always be strings, so we fix this with a proper type cast.

* Fix broken pagination routing in combination with active search for TYPO3 12.4 and onwards.
See: :ref:`searchPagination`.

Tasks
-----

Expand Down
25 changes: 25 additions & 0 deletions Documentation/Features/SearchPagination.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.. index:: single: Search
.. _searchPagination:

Search Pagination
=================

The extension supports pagination of search results.
Please properly configure the system. Add the following `TYPO3_CONF_VARS` configuration:

.. code-block:: php
'FE' => [
'cacheHash' => [
'excludedParameters' => [
'^events[search]',
],
],
],
Adopt the configuration to your own setup, e.g. change the `pluginNamespace` from `events` to whatever you've configured.
And ensure the involved plugins are excluded from caching (`USER_INT`).

The extension will assume `events[search]` as default namespace for search arguments.
Please make use of Services files and Dependency Injection to configure the custom
`AddSearchArgumentsToRouteArgumentsMiddleware` middleware with your own namespace.
57 changes: 57 additions & 0 deletions Tests/Functional/Frontend/Fixtures/Database/SearchSetup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

return [
'tt_content' => [
[
'pid' => '1',
'uid' => '1',
'CType' => 'cefilter_filter',
'header' => 'Search Form',
],
[
'pid' => '1',
'uid' => '2',
'CType' => 'celist_list',
'header' => 'Search Results',
],
],
'tx_events_domain_model_event' => [
[
'uid' => '1',
'pid' => '2',
'title' => 'Event one',
'teaser' => 'Some Teaser',
],
[
'uid' => '2',
'pid' => '2',
'title' => 'Event two',
'teaser' => 'Another teaser',
],
],
'tx_events_domain_model_date' => [
[
'uid' => '1',
'pid' => '2',
'event' => '1',
'start' => '1661626800',
'end' => '1661632200',
],
[
'uid' => '2',
'pid' => '2',
'event' => '1',
'start' => '1660158000',
'end' => '1660163400',
],
[
'uid' => '3',
'pid' => '2',
'event' => '2',
'start' => '1661194800',
'end' => '1661200200',
],
],
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
tt_content.cefilter_filter.20 {
view {
pluginNamespace = events
}

persistence {
storagePid = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">

<div class="row">
<div class="col-12 col-md-8 offset-md-4">
<div class="panel panel-default">
<div class="panel-body">
<f:form method="post" name="search" object="{demand}">
<div class="row mb-3">
<div class="col-md-12">
<div class="form-group">
<label for="searchword">Searchword</label>
<f:form.textfield type="text" class="form-control" id="searchword" property="searchword" value="{searchword}" />
</div>
</div>
</div>
<div class="row mb-3">
<div class="col col-md-6">
<div class="form-group">
<label for="start">From:</label>
<f:form.textfield type="date" class="form-control" id="start" property="start" />
</div>
</div>
<div class="col col-md-6">
<div class="form-group">
<label for="end">To:</label>
<f:form.textfield type="date" class="form-control" id="end" property="end" />
</div>
</div>
</div>

<f:if condition="{categories}">
<div class="row mb-3">
<f:for each="{categories}" as="category">
<div class="col-md-4 d-none d-lg-block">
<div class="form-check">
<f:form.checkbox class="form-check-input" property="userCategories" value="{category.uid}" id="check_{category.uid}"/>
<label class="form-check-label" for="check_{category.uid}">{category.title} {category.amountOfEvents}</label>
</div>
</div>
</f:for>
</div>
</f:if>

<div class="form-group mb-3">
<f:form.submit value="Search" class="btn btn-primary" />
</div>
</f:form>
</div>
</div>
</div>
</div>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "werkraummedia/ce-events-filter",
"type": "typo3-cms-extension",
"description": "Content element: EXT:events filter",
"license": "GPL-2.0-or-later",
"version": "v1.0.0",
"require": {
"typo3/cms-core": "*",
"typo3/cms-extbase": "*"
},
"extra": {
"typo3/cms": {
"extension-key": "ce_filter"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

$EM_CONF['ce_filter'] = [
'title' => 'Events filter',
'description' => 'Content Element for \'Events\'',
'category' => 'plugin',
'version' => '1.0.0',
'constraints' => [
'depends' => [
'typo3' => '*',
'events' => '*',
],
'conflicts' => [
],
'suggests' => [
],
],
];
Loading

0 comments on commit b8a2a69

Please sign in to comment.