Completed
Push — master ( 044ba8...318a17 )
by Gino
01:35
created

RelatedPosts::getOrderByOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace GinoPane\BlogTaxonomy\Components;
4
5
use DB;
6
use Cms\Classes\Page;
7
use GinoPane\BlogTaxonomy\Models\Tag;
8
use October\Rain\Database\Collection;
9
use RainLab\Blog\Models\Post;
10
use GinoPane\BlogTaxonomy\Plugin;
11
12
/**
13
 * Class RelatedPosts
14
 *
15
 * @package GinoPane\BlogTaxonomy\Components
16
 */
17
class RelatedPosts extends ComponentAbstract
18
{
19
    const NAME = 'relatedPosts';
20
21
    use TranslateArrayTrait;
22
23
    /**
24
     * @var Collection | array
25
     */
26
    public $posts = [];
27
28
    /**
29
     * Reference to the page name for linking to posts
30
     *
31
     * @var string
32
     */
33
    public $postPage;
34
35
    /**
36
     * If the post list should be ordered by another attribute
37
     *
38
     * @var string
39
     */
40
    public $orderBy;
41
42
    /**
43
     * Limits the number of records to display
44
     *
45
     * @var int
46
     */
47
    public $limit;
48
49
    /**
50
     * Component Registration
51
     *
52
     * @return  array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
53
     */
54
    public function componentDetails()
55
    {
56
        return [
57
            'name'        => Plugin::LOCALIZATION_KEY . 'components.related_posts.name',
58
            'description' => Plugin::LOCALIZATION_KEY . 'components.related_posts.description'
59
        ];
60
    }
61
62
    /**
63
     * Component Properties
64
     *
65
     * @return  array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<string,string|false>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
66
     */
67
    public function defineProperties()
68
    {
69
        return [
70
            'slug' => [
71
                'title'             => Plugin::LOCALIZATION_KEY . 'components.related_posts.post_slug_title',
72
                'description'       => Plugin::LOCALIZATION_KEY . 'components.related_posts.post_slug_description',
73
                'default'           => '{{ :slug }}',
74
                'type'              => 'string'
75
            ],
76
77
            'limit' => [
78
                'title'             => Plugin::LOCALIZATION_KEY . 'components.related_posts.limit_title',
79
                'description'       => Plugin::LOCALIZATION_KEY . 'components.related_posts.limit_description',
80
                'type'              => 'string',
81
                'default'           => '0',
82
                'validationPattern' => '^[0-9]+$',
83
                'validationMessage' => Plugin::LOCALIZATION_KEY . 'components.related_posts.limit_validation_message',
84
                'showExternalParam' => false
85
            ],
86
87
            'orderBy' => [
88
                'title'       => 'rainlab.blog::lang.settings.posts_order',
89
                'description' => 'rainlab.blog::lang.settings.posts_order_description',
90
                'type'        => 'dropdown',
91
                'default'     => 'published_at asc',
92
                'showExternalParam' => false
93
            ],
94
95
            'postPage' => [
96
                'group'       => Plugin::LOCALIZATION_KEY . 'components.related_posts.links_group',
97
                'title'       => 'rainlab.blog::lang.settings.posts_post',
98
                'description' => 'rainlab.blog::lang.settings.posts_description',
99
                'type'        => 'dropdown',
100
                'default'     => 'blog/post',
101
            ],
102
        ];
103
    }
104
105
    /**
106
     * @see PostListAbstract::$postAllowedSortingOptions
107
     *
108
     * @return mixed
109
     */
110
    public function getOrderByOptions()
111
    {
112
        $order = $this->translate(
113
            array_merge(
114
                [
115
                    'relevance asc' => Plugin::LOCALIZATION_KEY . 'order_options.relevance_asc',
116
                    'relevance desc' => Plugin::LOCALIZATION_KEY . 'order_options.relevance_desc'
117
                ],
118
                PostListAbstract::$postAllowedSortingOptions
119
            )
120
        );
121
122
        asort($order);
123
124
        return $order;
125
    }
126
127
    private function prepareVars()
128
    {
129
        $this->orderBy = $this->page['orderBy'] = $this->property('orderBy');
130
131
        // Page links
132
        $this->postPage = $this->page['postPage' ] = $this->property('postPage');
133
    }
134
135
    public function getPostPageOptions()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
136
    {
137
        return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
138
    }
139
140
    /**
141
     * Load post and start building query for related posts
142
     */
143
    public function onRun()
144
    {
145
        //Prepare vars
146
        $this->prepareVars();
147
148
        $this->posts = $this->loadRelatedPosts();
149
    }
150
151
    /**
152
     * Load related posts by common tags
153
     *
154
     * @return mixed
155
     */
156
    private function loadRelatedPosts()
157
    {
158
        $post = Post::where('slug', $this->property('slug'))
159
            ->with('tags')
160
            ->first();
161
162
        if (!$post || (!$tagIds = $post->tags->lists('id'))) {
163
            return null;
164
        }
165
166
        $query = Post::isPublished()
167
            ->where('id', '<>', $post->id)
168
            ->whereHas('tags', function ($tag) use ($tagIds) {
169
                $tag->whereIn('id', $tagIds);
170
            })
171
            ->with('tags');
172
173
        $this->queryOrderBy($query, $tagIds);
174
175
        if ($take = intval($this->property('limit'))) {
176
            $query->take($take);
177
        }
178
179
        $posts = $query->get();
180
181
        $this->setPostUrls($posts);
182
183
        return $posts;
184
    }
185
186
    /**
187
     * @param $query
188
     * @param $tagIds
189
     */
190
    private function queryOrderBy($query, $tagIds)
191
    {
192
        if (in_array($this->orderBy, array_keys($this->getOrderByOptions()))) {
193
            if ($this->orderBy == 'random') {
194
                $query->inRandomOrder();
195
            } else {
196
                list($sortField, $sortDirection) = explode(' ', $this->orderBy);
197
198
                if ($sortField == 'relevance') {
199
                    $sortField = DB::raw(
200
                        sprintf(
201
                            '(
202
                                select count(*)
203
                                from `%1$s`
204
                                where `%1$s`.`post_id` = `rainlab_blog_posts`.`id`
205
                                and `%1$s`.`tag_id` in (%2$s)
206
                            )',
207
                            Tag::CROSS_REFERENCE_TABLE_NAME,
208
                            DB::getPdo()->quote(implode(', ', $tagIds))
209
                        )
210
                    );
211
                }
212
213
                $query->orderBy($sortField, $sortDirection);
214
            }
215
        }
216
    }
217
}
218