Completed
Push — master ( 67a857...350314 )
by Gino
01:33
created

PostListAbstract::onRun()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
namespace GinoPane\BlogTaxonomy\Classes;
4
5
use Cms\Classes\Page;
6
use Illuminate\Http\Response;
7
use Rainlab\Blog\Models\Post;
8
use GinoPane\BlogTaxonomy\Plugin;
9
use October\Rain\Database\Builder;
10
use Illuminate\Http\RedirectResponse;
11
use Illuminate\Support\Facades\Redirect;
12
use Illuminate\Database\Eloquent\Collection;
13
14
/**
15
 * Class PostListAbstract
16
 *
17
 * @package GinoPane\BlogTaxonomy\Classes
18
 */
19
abstract class PostListAbstract extends ComponentAbstract
0 ignored issues
show
Coding Style introduced by
PostListAbstract does not seem to conform to the naming convention (^Abstract|Factory$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
20
{
21
    use TranslateArrayTrait;
22
23
    /**
24
     * @var Collection | array
25
     */
26
    public $posts = [];
27
28
    /**
29
     * @var integer             The current page
30
     */
31
    public $currentPage;
32
33
    /**
34
     * @var integer             The number of results per page
35
     */
36
    public $resultsPerPage;
37
38
    /**
39
     * If the post list should be ordered by another attribute
40
     *
41
     * @var string
42
     */
43
    public $orderBy;
44
45
    /**
46
     * Filter out posts based on their slugs or ids
47
     *
48
     * @var array
49
     */
50
    protected $exceptPosts;
51
52
    /**
53
     * Filter out posts based on their categories slugs or ids
54
     *
55
     * @var array
56
     */
57
    protected $exceptCategories;
58
59
    /**
60
     * The attributes on which the post list can be ordered
61
     * @var array
62
     */
63
    public static $postAllowedSortingOptions = [
64
        'title asc' => Plugin::LOCALIZATION_KEY . 'order_options.title_asc',
65
        'title desc' => Plugin::LOCALIZATION_KEY . 'order_options.title_desc',
66
        'created_at asc' => Plugin::LOCALIZATION_KEY . 'order_options.created_at_asc',
67
        'created_at desc' => Plugin::LOCALIZATION_KEY . 'order_options.created_at_desc',
68
        'updated_at asc' => Plugin::LOCALIZATION_KEY . 'order_options.updated_at_asc',
69
        'updated_at desc' => Plugin::LOCALIZATION_KEY . 'order_options.updated_at_desc',
70
        'published_at asc' => Plugin::LOCALIZATION_KEY . 'order_options.published_at_asc',
71
        'published_at desc' => Plugin::LOCALIZATION_KEY . 'order_options.published_at_desc',
72
        'random' => Plugin::LOCALIZATION_KEY . 'order_options.random'
73
    ];
74
75
    /**
76
     * Component properties
77
     *
78
     * @return array
79
     */
80
    public function defineProperties(): array
81
    {
82
        $properties = [
83
            'orderBy' => [
84
                'title'       => 'rainlab.blog::lang.settings.posts_order',
85
                'description' => 'rainlab.blog::lang.settings.posts_order_description',
86
                'type'        => 'dropdown',
87
                'default'     => 'published_at asc',
88
                'showExternalParam' => false
89
            ]
90
        ];
91
92
        return array_merge(
93
            $properties,
94
            $this->getPaginationProperties(),
95
            $this->getPageLinkProperties(),
96
            $this->getExceptionProperties()
97
        );
98
    }
99
100
    /**
101
     * @see Post::$allowedSortingOptions
102
     *
103
     * @return string[]
104
     */
105
    public function getOrderByOptions(): array
106
    {
107
        $order = $this->translate(static::$postAllowedSortingOptions);
108
109
        asort($order);
110
111
        return $order;
112
    }
113
114
    /**
115
     * Query the item and posts belonging to it
116
     *
117
     * @return void|RedirectResponse
118
     */
119
    public function onRun()
120
    {
121
        if ($this->prepareContextItem() === null) {
122
            return Redirect::to($this->controller->pageUrl(Response::HTTP_NOT_FOUND));
123
        }
124
125
        $this->prepareVars();
126
127
        $this->listPosts();
128
    }
129
130
    /**
131
     * Load a list of posts
132
     */
133
    public function listPosts()
134
    {
135
        $query = $this->getPostsQuery();
136
137
        $this->handlePostExceptions($query);
138
139
        $this->handleOrder($query);
140
141
        $posts = $query->paginate($this->resultsPerPage, $this->currentPage);
142
143
        $this->setPostUrls($posts);
144
145
        $this->posts = $posts;
146
    }
147
148
    /**
149
     * @return mixed
150
     */
151
    public function getPostPageOptions()
152
    {
153
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
154
    }
155
156
    /**
157
     * @return mixed
158
     */
159
    public function getCategoryPageOptions()
160
    {
161
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
162
    }
163
164
    /**
165
     * Prepare variables
166
     */
167
    abstract protected function prepareContextItem();
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
168
169
    /**
170
     * @return mixed
171
     */
172
    abstract protected function getPostsQuery();
173
174
    /**
175
     * Prepare variables
176
     */
177
    protected function prepareVars()
178
    {
179
        // Paginator settings
180
        $this->populatePagination();
181
        // Page links
182
        $this->populateLinks();
183
        // Exceptions
184
        $this->populateExceptions();
185
186
        $this->orderBy = $this->property('orderBy');
187
    }
188
189
    /**
190
     * Properties for pagination handling
191
     *
192
     * @return array
193
     */
194
    private function getPaginationProperties(): array
195
    {
196
        return [
197
            'page' => [
198
                'group'         => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.pagination_group',
199
                'title'         => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.page_parameter_title',
200
                'description'   => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.page_parameter_description',
201
                'default'       => '{{ :page }}',
202
                'type'          => 'string',
203
            ],
204
            'resultsPerPage' => [
205
                'group'         => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.pagination_group',
206
                'title'         => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.pagination_per_page_title',
207
                'description'   => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.pagination_per_page_description',
208
                'default'       => 10,
209
                'type'          => 'string',
210
                'validationPattern' => '^(0+)?[1-9]\d*$',
211
                'validationMessage' => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.pagination_validation_message',
212
                'showExternalParam' => false,
213
            ]
214
        ];
215
    }
216
217
    /**
218
     * Properties for proper links handling
219
     *
220
     * @return array
221
     */
222
    private function getPageLinkProperties(): array
223
    {
224
        return [
225
            'postPage' => [
226
                'group'       => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.links_group',
227
                'title'       => 'rainlab.blog::lang.settings.posts_post',
228
                'description' => 'rainlab.blog::lang.settings.posts_description',
229
                'type'        => 'dropdown',
230
                'default'     => 'blog/post',
231
                'showExternalParam' => false,
232
            ],
233
            'categoryPage' => [
234
                'group'       => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.links_group',
235
                'title'       => 'rainlab.blog::lang.settings.posts_category',
236
                'description' => 'rainlab.blog::lang.settings.posts_category_description',
237
                'type'        => 'dropdown',
238
                'default'     => 'blog/category',
239
                'showExternalParam' => false,
240
            ],
241
        ];
242
    }
243
244
    /**
245
     * Properties for list exceptions handling
246
     *
247
     * @return array
248
     */
249
    private function getExceptionProperties(): array
250
    {
251
        return [
252
            'exceptPosts' => [
253
                'group'             => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.exceptions_group',
254
                'title'             => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.except_posts_title',
255
                'description'       => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.except_posts_description',
256
                'type'              => 'string',
257
                'default'           => '',
258
                'showExternalParam' => false,
259
            ],
260
            'exceptCategories' => [
261
                'group'             => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.exceptions_group',
262
                'title'             => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.except_categories_title',
263
                'description'       => Plugin::LOCALIZATION_KEY . 'components.post_list_abstract.except_categories_description',
264
                'type'              => 'string',
265
                'default'           => '',
266
                'showExternalParam' => false,
267
            ],
268
        ];
269
    }
270
271
    /**
272
     * @return void
273
     */
274
    private function populatePagination()
275
    {
276
        $this->currentPage = (int)$this->property('page', 1) ?: (int)post('page');
277
        $this->resultsPerPage = (int)$this->property('resultsPerPage')
278
            ?: $this->defineProperties()['resultsPerPage']['default'];
279
    }
280
281
    /**
282
     * @return void
283
     */
284
    private function populateLinks()
285
    {
286
        $this->postPage = $this->property('postPage');
287
        $this->categoryPage = $this->property('categoryPage');
288
    }
289
290
    /**
291
     * @return void
292
     */
293
    private function populateExceptions()
294
    {
295
        $this->exceptPosts = $this->extractArrayFromProperty('exceptPosts');
296
        $this->exceptCategories = $this->extractArrayFromProperty('exceptCategories');
297
    }
298
299
    /**
300
     * @param $query
301
     */
302
    private function handleOrder(Builder $query)
303
    {
304
        if (array_key_exists($this->orderBy, self::$postAllowedSortingOptions)) {
305
            if ($this->orderBy === 'random') {
306
                $query->inRandomOrder();
307
            } else {
308
                list($sortField, $sortDirection) = explode(' ', $this->orderBy);
309
310
                $query->orderBy($sortField, $sortDirection);
311
            }
312
        }
313
    }
314
315
    /**
316
     * @param $query
317
     */
318
    private function handlePostExceptions(Builder $query)
319
    {
320
        $this->handleExceptionsByPost($query);
321
        $this->handleExceptionByCategory($query);
322
    }
323
324
    /**
325
     * @param string $property
326
     *
327
     * @return array
328
     */
329
    private function extractArrayFromProperty(string $property): array
330
    {
331
        return array_map('trim', array_filter(explode(',', $this->property($property))));
332
    }
333
334
    /**
335
     * Separates parameters into two arrays: ids and slugs
336
     *
337
     * @param array $parameters
338
     *
339
     * @return array Ids array an slugs array
340
     */
341
    private function separateParameters(array $parameters): array
342
    {
343
        $slugs = $parameters;
344
        $ids = [];
345
346
        foreach ($slugs as $index => $potentialId) {
347
            if (is_numeric($potentialId)) {
348
                $ids[] = $potentialId;
349
                unset($slugs[$index]);
350
            }
351
        }
352
353
        return [$ids, $slugs];
354
    }
355
356
    /**
357
     * @param $query
358
     *
359
     */
360
    private function handleExceptionByCategory(Builder $query)
361
    {
362
        if (!empty($this->exceptCategories)) {
363
            list($ids, $slugs) = $this->separateParameters($this->exceptCategories);
364
365
            $query->whereDoesntHave('categories', static function ($innerQuery) use ($ids, $slugs) {
366
                if (!empty($ids)) {
367
                    $innerQuery->whereIn('id', $ids);
368
                }
369
370
                if (!empty($slugs)) {
371
                    $innerQuery->whereIn('slug', $slugs);
372
                }
373
            });
374
        }
375
    }
376
377
    /**
378
     * @param $query
379
     */
380
    private function handleExceptionsByPost(Builder $query)
381
    {
382
        if (!empty($this->exceptPosts)) {
383
            list($ids, $slugs) = $this->separateParameters($this->exceptPosts);
384
385
            if (!empty($ids)) {
386
                $query->whereNotIn('id', $ids);
387
            }
388
389
            if (!empty($slugs)) {
390
                $query->whereNotIn('slug', $slugs);
391
            }
392
        }
393
    }
394
}
395