|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the Yosymfony\Spress. |
|
5
|
|
|
* |
|
6
|
|
|
* (c) YoSymfony <http://github.com/yosymfony> |
|
7
|
|
|
* |
|
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
|
9
|
|
|
* file that was distributed with this source code. |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Yosymfony\Spress\Core\ContentManager\Generator\Pagination; |
|
13
|
|
|
|
|
14
|
|
|
use Yosymfony\Spress\Core\ContentManager\Generator\GeneratorInterface; |
|
15
|
|
|
use Yosymfony\Spress\Core\DataSource\ItemInterface; |
|
16
|
|
|
use Yosymfony\Spress\Core\DataSource\Item; |
|
17
|
|
|
use Yosymfony\Spress\Core\Support\ArrayWrapper; |
|
18
|
|
|
use Yosymfony\Spress\Core\Support\AttributesResolver; |
|
19
|
|
|
use Yosymfony\Spress\Core\ContentManager\Exception\AttributeValueException; |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* Pagination generator lets you generate multiples |
|
23
|
|
|
* pages around a set of items. |
|
24
|
|
|
* |
|
25
|
|
|
* Example of URLs generated: |
|
26
|
|
|
* / |
|
27
|
|
|
* /page2 |
|
28
|
|
|
* ... |
|
29
|
|
|
* |
|
30
|
|
|
* How to configure? (Front matter block of the template page): |
|
31
|
|
|
* |
|
32
|
|
|
*``` |
|
33
|
|
|
* --- |
|
34
|
|
|
* layout: "default" |
|
35
|
|
|
* |
|
36
|
|
|
* generator: "pagination" |
|
37
|
|
|
* max_page: 5 |
|
38
|
|
|
* provider: "site.posts" |
|
39
|
|
|
* permalink: "/page:num" |
|
40
|
|
|
* sort_by: "date" |
|
41
|
|
|
* sort_type: "descendant" |
|
42
|
|
|
* --- |
|
43
|
|
|
*``` |
|
44
|
|
|
* |
|
45
|
|
|
* @author Victor Puertas <[email protected]> |
|
46
|
|
|
*/ |
|
47
|
|
|
class PaginationGenerator implements GeneratorInterface |
|
48
|
|
|
{ |
|
49
|
|
|
/** |
|
50
|
|
|
* {@inheritdoc} |
|
51
|
|
|
* |
|
52
|
|
|
* @throws Yosymfony\Spress\Core\ContentManager\Exception\AttributeValueException if bad attribute value |
|
53
|
|
|
*/ |
|
54
|
|
|
public function generateItems(ItemInterface $templateItem, array $collections) |
|
55
|
|
|
{ |
|
56
|
|
|
$result = []; |
|
57
|
|
|
|
|
58
|
|
|
$options = $this->getAttributesResolver($templateItem); |
|
59
|
|
|
|
|
60
|
|
|
if ($options['max_page'] < 1) { |
|
61
|
|
|
throw new AttributeValueException('Items per page value must be great than 0.', 'max_page', $templateItem->getPath(ItemInterface::SNAPSHOT_PATH_RELATIVE)); |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
$providerName = $this->providerToCollection($options['provider']); |
|
65
|
|
|
$templateItemPath = $templateItem->getPath(ItemInterface::SNAPSHOT_PATH_RELATIVE); |
|
66
|
|
|
|
|
67
|
|
|
$providerItems = $this->getProviderItems($collections, $providerName, $templateItemPath); |
|
68
|
|
|
|
|
69
|
|
|
if (empty($options['sort_by']) === false) { |
|
70
|
|
|
$providerItems = $this->sortItemsByAttribute($providerItems, $options['sort_by'], $options['sort_type']); |
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
$pages = (new ArrayWrapper($providerItems))->paginate($options['max_page']); |
|
74
|
|
|
|
|
75
|
|
|
$totalPages = count($pages); |
|
76
|
|
|
$totalItems = count($providerItems); |
|
77
|
|
|
$templatePath = dirname($templateItem->getPath(Item::SNAPSHOT_PATH_RELATIVE)); |
|
78
|
|
|
|
|
79
|
|
|
if ($templatePath === '.') { |
|
80
|
|
|
$templatePath = ''; |
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
|
|
foreach ($pages as $page => $items) { |
|
84
|
|
|
$previousPage = $page > 1 ? $page - 1 : null; |
|
85
|
|
|
$previousPagePath = $this->getPageRelativePath($templatePath, $options['permalink'], $previousPage); |
|
86
|
|
|
$previousPageUrl = $this->getPagePermalink($previousPagePath); |
|
87
|
|
|
$nextPage = $page === $totalPages ? null : $page + 1; |
|
88
|
|
|
$nextPagePath = $this->getPageRelativePath($templatePath, $options['permalink'], $nextPage); |
|
89
|
|
|
$nextPageUrl = $this->getPagePermalink($nextPagePath); |
|
90
|
|
|
|
|
91
|
|
|
$pageAttr = new ArrayWrapper($templateItem->getAttributes()); |
|
92
|
|
|
$pageAttr->set('pagination.per_page', $options['max_page']); |
|
93
|
|
|
$pageAttr->set('pagination.total_items', $totalItems); |
|
94
|
|
|
$pageAttr->set('pagination.total_pages', $totalPages); |
|
95
|
|
|
$pageAttr->set('pagination.page', $page); |
|
96
|
|
|
$pageAttr->set('pagination.previous_page', $previousPage); |
|
97
|
|
|
$pageAttr->set('pagination.previous_page_path', $previousPagePath); |
|
98
|
|
|
$pageAttr->set('pagination.previous_page_url', $previousPageUrl); |
|
99
|
|
|
$pageAttr->set('pagination.next_page', $nextPage); |
|
100
|
|
|
$pageAttr->set('pagination.next_page_path', $nextPagePath); |
|
101
|
|
|
$pageAttr->set('pagination.next_page_url', $nextPageUrl); |
|
102
|
|
|
|
|
103
|
|
|
$pageAttr->remove('permalink'); |
|
104
|
|
|
|
|
105
|
|
|
$pagePath = $this->getPageRelativePath($templatePath, $options['permalink'], $page); |
|
106
|
|
|
$permalink = $this->getPagePermalink($pagePath); |
|
107
|
|
|
|
|
108
|
|
|
$item = new PaginationItem($templateItem->getContent(), $pagePath, $pageAttr->getArray()); |
|
109
|
|
|
$item->setPageItems($items); |
|
110
|
|
|
$item->setPath($pagePath, Item::SNAPSHOT_PATH_RELATIVE); |
|
111
|
|
|
$item->setPath($permalink, Item::SNAPSHOT_PATH_PERMALINK); |
|
112
|
|
|
|
|
113
|
|
|
$result[] = $item; |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
return $result; |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
protected function getPageRelativePath($basePath, $template, $page) |
|
120
|
|
|
{ |
|
121
|
|
|
$result = $basePath; |
|
122
|
|
|
|
|
123
|
|
|
if (is_null($page)) { |
|
124
|
|
|
return; |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
$pagePath = str_replace(':num', $page, $template); |
|
128
|
|
|
$basename = basename($pagePath); |
|
129
|
|
|
|
|
130
|
|
|
if (preg_match('/^(.+?)\.(.+)$/', $basename)) { |
|
131
|
|
|
$result .= '/'.($page > 1 ? $pagePath : $basename); |
|
132
|
|
|
} else { |
|
133
|
|
|
if ($page > 1) { |
|
134
|
|
|
$result .= '/'.$pagePath; |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
$result .= '/index.html'; |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
return ltrim(preg_replace('/\/\/+/', '/', $result), '/'); |
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
View Code Duplication |
protected function getPagePermalink($pageRelativePath) |
|
|
|
|
|
|
144
|
|
|
{ |
|
145
|
|
|
if (is_null($pageRelativePath)) { |
|
146
|
|
|
return; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
$result = $pageRelativePath; |
|
150
|
|
|
$basename = basename($pageRelativePath); |
|
151
|
|
|
|
|
152
|
|
|
if ($basename === 'index.html') { |
|
153
|
|
|
$result = dirname($pageRelativePath); |
|
154
|
|
|
|
|
155
|
|
|
if ($result === '.') { |
|
156
|
|
|
$result = ''; |
|
157
|
|
|
} |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
return '/'.$result; |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
/** |
|
164
|
|
|
* @return Item[] |
|
165
|
|
|
*/ |
|
166
|
|
|
protected function getProviderItems(array $collections, $providerName, $templateItemPath) |
|
167
|
|
|
{ |
|
168
|
|
|
$arr = new ArrayWrapper($collections); |
|
169
|
|
|
|
|
170
|
|
|
if ($arr->has($providerName) === false || is_array($provider = $arr->get($providerName)) === false) { |
|
171
|
|
|
throw new AttributeValueException( |
|
172
|
|
|
sprintf('Provider: "%s" for pagination not found.', $providerName), |
|
173
|
|
|
'provider', |
|
174
|
|
|
$templateItemPath |
|
175
|
|
|
); |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
$providerItemsArr = new ArrayWrapper($provider); |
|
179
|
|
|
$itemWithActiveOutput = $providerItemsArr->where(function ($key, Item $item) { |
|
180
|
|
|
$attributes = $item->getAttributes(); |
|
181
|
|
|
|
|
182
|
|
|
if (array_key_exists('output', $attributes) && $attributes['output'] === false) { |
|
183
|
|
|
return false; |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
return true; |
|
187
|
|
|
}); |
|
188
|
|
|
|
|
189
|
|
|
return $itemWithActiveOutput; |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
protected function sortItemsByAttribute(array $items, $attribute, $sortType) |
|
193
|
|
|
{ |
|
194
|
|
|
$arr = new ArrayWrapper($items); |
|
195
|
|
|
|
|
196
|
|
View Code Duplication |
$callback = function ($key, ItemInterface $item) use ($attribute) { |
|
|
|
|
|
|
197
|
|
|
$attributes = $item->getAttributes(); |
|
198
|
|
|
|
|
199
|
|
|
return isset($attributes[$attribute]) === true ? $attributes[$attribute] : null; |
|
200
|
|
|
}; |
|
201
|
|
|
|
|
202
|
|
|
return $arr->sortBy($callback, null, SORT_REGULAR, $sortType === 'descending'); |
|
203
|
|
|
} |
|
204
|
|
|
|
|
205
|
|
|
protected function providerToCollection($providerName) |
|
206
|
|
|
{ |
|
207
|
|
|
return str_replace('site.', '', $providerName); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
protected function getAttributesResolver(ItemInterface $templateItem) |
|
211
|
|
|
{ |
|
212
|
|
|
$resolver = new AttributesResolver(); |
|
213
|
|
|
$resolver->setDefault('max_page', 5, 'int') |
|
214
|
|
|
->setDefault('provider', 'site.posts', 'string') |
|
215
|
|
|
->setDefault('permalink', '/page:num') |
|
216
|
|
|
->setDefault('sort_by', '', 'string') |
|
217
|
|
|
->setDefault('sort_type', 'descending', 'string') |
|
218
|
|
|
->setValidator('sort_type', function ($value) { |
|
219
|
|
|
switch ($value) { |
|
220
|
|
|
case 'descending': |
|
221
|
|
|
case 'ascending': |
|
222
|
|
|
return true; |
|
223
|
|
|
default: |
|
224
|
|
|
return false; |
|
225
|
|
|
} |
|
226
|
|
|
}); |
|
227
|
|
|
|
|
228
|
|
|
$attributes = $templateItem->getAttributes(); |
|
229
|
|
|
|
|
230
|
|
|
return $resolver->resolve($attributes); |
|
231
|
|
|
} |
|
232
|
|
|
} |
|
233
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.