PaginationGenerator::generateItems()   B
last analyzed

Complexity

Conditions 7
Paths 21

Size

Total Lines 64

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 64
ccs 0
cts 49
cp 0
rs 7.8521
c 0
b 0
f 0
cc 7
nc 21
nop 2
crap 56

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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