Skip to content

Commit a8b753e

Browse files
author
Marvin Kuhn
committed
added configurable pagination
1 parent 18c9128 commit a8b753e

File tree

10 files changed

+252
-56
lines changed

10 files changed

+252
-56
lines changed

Classes/Fusion/PaginationArrayImplementation.php

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,73 +4,151 @@
44

55
use Neos\Flow\Annotations as Flow;
66
use Neos\Fusion\FusionObjects\AbstractFusionObject;
7+
use Neos\Fusion\Core\Runtime;
78

89
class PaginationArrayImplementation extends AbstractFusionObject
910
{
1011
protected $currentPage;
1112
protected $itemsPerPage;
1213
protected $maximumNumberOfLinks;
1314
protected $totalCount;
15+
protected $numberOfPages;
16+
protected $firstPage;
17+
protected $lastPage;
1418
/**
1519
* @inheritDoc
1620
*/
1721
public function __construct(Runtime $runtime, $path, $fusionObjectName)
1822
{
1923
parent::__construct($runtime, $path, $fusionObjectName);
24+
2025
$this->currentPage = \intval($this->fusionValue('currentPage'));
2126
$this->itemsPerPage = \intval($this->fusionValue('itemsPerPage'));
22-
$this->maximumNumberOfLinks = \intval($this->fusionValue('maximumNumberOfLinks')) - 2;
27+
$this->maximumNumberOfLinks = \intval($this->fusionValue('maximumNumberOfLinks'));
2328
$this->totalCount = \intval($this->fusionValue('totalCount'));
29+
$this->paginationConfig = $this->fusionValue('paginationConfig');
30+
$this->numberOfPages = $this->getNumberOfPages();
31+
32+
$this->calculateFirstAndLastPage();
33+
}
34+
/**
35+
* calculates the first and last page to show
36+
*
37+
* @return void
38+
*/
39+
protected function calculateFirstAndLastPage()
40+
{
41+
$delta = \floor($this->maximumNumberOfLinks / 2);
42+
$firstPage = $this->currentPage - $delta;
43+
$lastPage = $this->currentPage + $delta + ($this->maximumNumberOfLinks % 2 === 0 ? 1 : 0);
44+
45+
if ($firstPage < 1) {
46+
$lastPage -= $firstPage - 1;
47+
}
48+
if ($lastPage > $this->numberOfPages) {
49+
$firstPage -= ($lastPage - $this->numberOfPages);
50+
}
51+
52+
$this->firstPage = \max($firstPage, 1);
53+
$this->lastPage = \min($lastPage, $this->numberOfPages);
2454
}
2555
/**
2656
* calculates the number of pages
57+
*
2758
* @return integer
2859
*/
2960
protected function getNumberOfPages()
3061
{
3162
$numberOfPages = \ceil($this->totalCount / $this->itemsPerPage);
63+
3264
if ($this->maximumNumberOfLinks > $numberOfPages) {
3365
return $numberOfPages;
3466
}
3567
return $numberOfPages;
3668
}
3769
/**
3870
* get an array of pages to display
71+
*
3972
* @return array
4073
*/
41-
protected function getPageRangeArray($numberOfPages)
74+
protected function getPageArray()
4275
{
43-
$delta = \floor($this->maximumNumberOfLinks / 2);
44-
$rangeStart = $this->currentPage - $delta;
45-
$rangeEnd = $this->currentPage + $delta + ($this->maximumNumberOfLinks % 2 === 0 ? 1 : 0);
46-
if ($rangeStart < 1) {
47-
$rangeEnd -= $rangeStart - 1;
76+
$range = \range($this->firstPage, $this->lastPage);
77+
$pageArray = [];
78+
79+
foreach ($range as $page) {
80+
$pageArray[] = [
81+
'page' => $page,
82+
'label' => $page,
83+
'type' => 'page'
84+
];
85+
}
86+
87+
return $pageArray;
88+
}
89+
/**
90+
* add a item to the start of the page array
91+
*
92+
* @param array $pageArray
93+
* @param integer $page
94+
* @param string $type
95+
* @return array
96+
*/
97+
protected function addItemToTheStartOfPageArray($pageArray, $page, $type)
98+
{
99+
if ($this->firstPage > 1) {
100+
array_unshift($pageArray, [
101+
'page' => $page,
102+
'label' => $this->paginationConfig['labels'][$type],
103+
'type' => $type
104+
]);
48105
}
49-
if ($rangeEnd > $numberOfPages) {
50-
$rangeStart -= ($rangeEnd - $numberOfPages);
106+
107+
return $pageArray;
108+
}
109+
/**
110+
* add a item to the end of the page array
111+
*
112+
* @param array $pageArray
113+
* @param integer $page
114+
* @param string $type
115+
* @return array
116+
*/
117+
protected function addItemToTheEndOfPageArray($pageArray, $page, $type)
118+
{
119+
if ($this->lastPage < $this->numberOfPages) {
120+
$pageArray[] = [
121+
'page' => $page,
122+
'label' => $this->paginationConfig['labels'][$type],
123+
'type' => $type
124+
];
51125
}
52-
$rangeStart = \max($rangeStart, 1);
53-
$rangeEnd = \min($rangeEnd, $numberOfPages);
54-
return \range($rangeStart, $rangeEnd);
126+
127+
return $pageArray;
55128
}
56129
/**
57-
* @return Array
130+
* @return array
58131
*/
59132
public function evaluate()
60133
{
61-
if ($this->totalCount > 0 !== true) {
134+
if ($this->totalCount > 0 !== true || $this->numberOfPages === 1) {
62135
return [];
63136
}
64-
$numberOfPages = $this->getNumberOfPages();
65-
$pageArray = $this->getPageRangeArray($numberOfPages);
66-
if ($pageArray[0] > 2) {
67-
array_unshift($pageArray, "...");
68-
array_unshift($pageArray, 1);
137+
138+
$pageArray = $this->getPageArray();
139+
140+
if ($this->paginationConfig['showSeperators']) {
141+
$pageArray = $this->addItemToTheStartOfPageArray($pageArray, false, 'seperator');
142+
$pageArray = $this->addItemToTheEndOfPageArray($pageArray, false, 'seperator');
143+
}
144+
if ($this->paginationConfig['showNextAndPrevious']) {
145+
$pageArray = $this->addItemToTheStartOfPageArray($pageArray, $this->currentPage - 1, 'previous');
146+
$pageArray = $this->addItemToTheEndOfPageArray($pageArray, $this->currentPage + 1, 'next');
69147
}
70-
if (end($pagesArray) + 1 < $numberOfPages) {
71-
$links[] = "...";
72-
$links[] = $numberOfPages;
148+
if ($this->paginationConfig['showFirstAndLast']) {
149+
$pageArray = $this->addItemToTheStartOfPageArray($pageArray, 1, 'first');
150+
$pageArray = $this->addItemToTheEndOfPageArray($pageArray, $this->numberOfPages, 'last');
73151
}
74-
return $links;
152+
return $pageArray;
75153
}
76154
}

Configuration/Routes.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-
22
name: 'Paginate for Breadlesscode.Listable'
3-
uriPattern: '{node}<pageSeparator>{currentPage}<defaultUriSuffix>'
3+
uriPattern: '{node}<defaultUriSuffix>?<parameterName>={currentPage}'
44
defaults:
55
'@package': 'Neos.Neos'
66
'@controller': 'Frontend\Node'

Configuration/Settings.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,21 @@ Neos:
55
'Breadlesscode.Listable':
66
position: 'before Neos.Neos'
77
variables:
8-
pageSeparator: '~p'
8+
parameterName: 'page'
99
defaultUriSuffix: '.html'
1010
Neos:
1111
fusion:
1212
autoInclude:
1313
Breadlesscode.Listable: true
14+
Breadlesscode:
15+
Listable:
16+
pagination:
17+
showSeperators: true
18+
showNextAndPrevious: true
19+
showFirstAndLast: true
20+
labels:
21+
seperator: '&hellip;'
22+
previous: '&lang;'
23+
next: '&rang;'
24+
first: '&laquo;'
25+
last: '&raquo;'
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
prototype(Breadlesscode.Listable:Collection) < prototype(Neos.Fusion:Component) {
2+
collection = ${ [] }
3+
currentPage = ${ 1 }
4+
itemsPerPage= ${ 15 }
5+
paginate = ${ false }
6+
7+
@context.limit = ${ this.currentPage * this.itemsPerPage }
8+
@context.offset = ${ (this.currentPage - 1) * this.itemsPerPage }
9+
10+
renderer = Neos.Fusion:Case {
11+
# elastic search pagination implementation
12+
elasticSearch {
13+
condition = ${ Type.instance(props.collection, 'Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryBuilder') }
14+
renderer = ${ props.collection }
15+
renderer.@process {
16+
limitAndOffset = ${ value.limit(limit).from(offset) }
17+
[email protected] = ${ props.paginate }
18+
execute = ${ value.execute() }
19+
}
20+
}
21+
# default flow query implementaion
22+
default {
23+
condition = ${ true }
24+
renderer = ${ Type.instance(props.collection, 'Neos\Eel\FlowQuery\FlowQuery') ? props.collection : q(props.collection) }
25+
renderer.@process {
26+
slice = ${ value.slice(offset, limit) }
27+
[email protected] = ${ props.paginate }
28+
execute = ${ value.get() }
29+
}
30+
}
31+
}
32+
}

Resources/Private/Fusion/Component/List.fusion

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,39 @@
11
prototype(Breadlesscode.Listable:List) < prototype(Neos.Fusion:Component) {
2-
paginated = ${ false }
32
collection = ${ [] }
43
itemsPerPage = ${ 10 }
54
itemRenderer = 'Breadlesscode.Listable:ListItem'
5+
itemName = ${ 'item' }
6+
paginated = ${ true }
7+
paginationConfig = Breadlesscode.Listable:PaginationConfig
68

7-
_currentPage = ${request.arguments.currentPage || 1}
9+
@context.currentPage = ${request.arguments.currentPage || 1}
810

911
renderer = Neos.Fusion:Array {
10-
@context.data = Neos.Fusion:RawArray {
11-
collection = Neos.Fusion:Case {
12-
@context.limit = ${props._currentPage * props.itemsPerPage}
13-
@context.offset = ${(props._currentPage - 1) * props.itemsPerPage}
14-
elasticSearch {
15-
condition = ${Type.instance(props.collection, 'Flowpack\ElasticSearch\ContentRepositoryAdaptor\Eel\ElasticSearchQueryBuilder')}
16-
renderer = ${props.collection}
17-
[email protected] = ${value.limit(limit).from(offset)}
18-
[email protected][email protected] = ${ props.paginated }
19-
[email protected] = ${value.execute()}
20-
}
21-
default {
22-
condition = ${true}
23-
renderer = ${Type.instance(props.collection, 'Neos\Eel\FlowQuery\FlowQuery') ? props.collection : q(props.collection)}
24-
[email protected] = ${value.slice(offset, limit)}
25-
[email protected][email protected] = ${ props.paginated }
26-
[email protected] = ${value.get()}
27-
}
12+
@context.collection = Neos.Fusion:RawArray {
13+
items = Breadlesscode.Listable:Collection {
14+
collection = ${ props.collection }
15+
currentPage = ${ currentPage }
16+
itemsPerPage= ${ props.itemsPerPage }
17+
paginate = ${ props.paginated }
2818
}
29-
totalCount = ${Type.getType(props.collection) == 'array' ? q(props.collection).count() : props.collection.count()}
19+
totalCount = ${ Type.getType(props.collection) == 'array' ? q(props.collection).count() : props.collection.count() }
3020
}
3121
list = Neos.Fusion:Collection {
32-
collection = ${ data.collection }
33-
itemName = 'item'
34-
itemRenderer = Neos.Fusion:Case {
35-
prop {
36-
condition = ${true}
37-
type = ${ props.itemRenderer }
38-
}
22+
collection = ${ collection.items }
23+
itemName = ${ props.itemName }s
24+
itemRenderer = Neos.Fusion:Renderer {
25+
type = ${ props.itemRenderer }
3926
}
4027
}
41-
pagination >
28+
pagination = Breadlesscode.Listable:Pagination {
29+
currentPage = ${ currentPage }
30+
maximumNumberOfLinks = ${ 3 }
31+
totalCount = ${ collection.totalCount }
32+
itemsPerPage = ${ props.itemsPerPage }
33+
paginationConfig = ${ props.paginationConfig }
34+
}
4235
}
43-
44-
36+
4537
@cache {
4638
mode = 'dynamic'
4739
entryIdentifier {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
prototype(Breadlesscode.Listable:Pagination) < prototype(Neos.Fusion:Component) {
2+
currentPage = ''
3+
maximumNumberOfLinks = ''
4+
totalCount = ''
5+
itemsPerPage = ''
6+
paginationConfig = ${ [] }
7+
8+
@process.wrapPagination = ${ '<ul class="pagination">' + value + '</ul>' }
9+
10+
renderer = Neos.Fusion:Collection {
11+
collection = Breadlesscode.Listable:PaginationArray {
12+
currentPage = ${ props.currentPage }
13+
maximumNumberOfLinks = ${ props.maximumNumberOfLinks }
14+
totalCount = ${ props.totalCount }
15+
itemsPerPage = ${ props.itemsPerPage }
16+
paginationConfig = ${ props.paginationConfig }
17+
}
18+
19+
itemRenderer = Breadlesscode.Listable:PaginationItem {
20+
uri = Neos.Neos:NodeUri {
21+
node = ${ documentNode }
22+
additionalParams = Neos.Fusion:RawArray {
23+
currentPage = ${ item.page }
24+
}
25+
@if.noSperator = ${ item.page }
26+
}
27+
label = ${ item.label }
28+
typeClass = ${ item.type }
29+
}
30+
}
31+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
prototype(Breadlesscode.Listable:PaginationArray) {
2+
@class = 'Breadlesscode\\Listable\\Fusion\\PaginationArrayImplementation'
3+
4+
currentPage = ''
5+
maximumNumberOfLinks = ''
6+
totalCount = ''
7+
itemsPerPage = ''
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
prototype(Breadlesscode.Listable:PaginationConfig) < prototype(Neos.Fusion:RawArray) {
2+
showSeperators = ${ Configuration.setting('Breadlesscode.Listable.pagination.showSeperators') }
3+
showNextAndPrevious = ${ Configuration.setting('Breadlesscode.Listable.pagination.showNextAndPrevious') }
4+
showFirstAndLast = ${ Configuration.setting('Breadlesscode.Listable.pagination.showFirstAndLast') }
5+
6+
labels = Neos.Fusion:RawArray {
7+
seperator = ${ Configuration.setting('Breadlesscode.Listable.pagination.labels.seperator') }
8+
previous = ${ Configuration.setting('Breadlesscode.Listable.pagination.labels.previous') }
9+
next = ${ Configuration.setting('Breadlesscode.Listable.pagination.labels.next') }
10+
first = ${ Configuration.setting('Breadlesscode.Listable.pagination.labels.first') }
11+
last = ${ Configuration.setting('Breadlesscode.Listable.pagination.labels.last') }
12+
}
13+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
prototype(Breadlesscode.Listable:PaginationItem) < prototype(Neos.Fusion:Component) {
2+
label = ${ false }
3+
uri = ${ false }
4+
typeClass = ${ '' }
5+
linkClass = 'page-link'
6+
7+
renderer = Neos.Fusion:Tag {
8+
tagName = ${ props.uri ? 'a' : 'span' }
9+
content = ${ props.label }
10+
attributes {
11+
href = ${ props.uri }
12+
[email protected] = ${ props.uri ? true : false }
13+
class = ${ props.linkClass }
14+
}
15+
16+
@process.itemWrap = Neos.Fusion:Tag {
17+
tagName = 'li'
18+
content = ${ value }
19+
attributes {
20+
class = ${ 'page-item ' + (props.uri ? '' : 'disabled ') + props.typeClass }
21+
}
22+
}
23+
}
24+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
include: Component/*.fusion
2+
3+
#
4+
# Have fun by using this package
5+
# If you see an issue please report it here:
6+
# https://github.com/breadlesscode/neos-listable/issues
7+
#

0 commit comments

Comments
 (0)