Tag::postRelation()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 24

Duplication

Lines 24
Ratio 100 %

Importance

Changes 0
Metric Value
dl 24
loc 24
rs 9.536
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace GinoPane\BlogTaxonomy\Models;
4
5
use Str;
6
use System\Models\File;
7
use RainLab\Blog\Models\Post;
8
use GinoPane\BlogTaxonomy\Plugin;
9
use October\Rain\Database\Builder;
10
use October\Rain\Database\Traits\Sluggable;
11
use October\Rain\Database\Traits\Validation;
12
use October\Rain\Database\Relations\MorphToMany;
13
14
/**
15
 * Class Tag
16
 *
17
 * @property string name
18
 * @property string slug
19
 * @property int posts_count Post count of posts associated with the tag
20
 * @property int series_posts_count Post count of posts associated with the tag via the series
21
 *
22
 * @package GinoPane\BlogTaxonomy\Models
23
 */
24
class Tag extends ModelAbstract
25
{
26
    use Sluggable;
27
    use Validation;
28
29
    const TABLE_NAME = 'ginopane_blogtaxonomy_tags';
30
31
    const PIVOT_COLUMN = 'ginopane_blogtaxonomy_taggable';
32
33
    const PIVOT_TABLE = 'ginopane_blogtaxonomy_taggables';
34
35
    /**
36
     * @var string The database table used by the model
37
     */
38
    public $table = self::TABLE_NAME;
39
40
    /**
41
     * Specifying of implemented behaviours as strings is convenient when
42
     * the target behaviour could be missing due to disabled or not installed
43
     * plugin. You won't get an error, the plugin would simply work without model
44
     *
45
     * @var array
46
     */
47
    public $implement = ['@RainLab.Translate.Behaviors.TranslatableModel'];
48
49
    /**
50
     * Translatable properties, indexed property will be available in queries
51
     *
52
     * @var array
53
     */
54
    public $translatable = [
55
        'name',
56
        [
57
            'slug',
58
            'index' => true
59
        ]
60
    ];
61
62
    /**
63
     * @var array
64
     */
65
    protected $slugs = ['slug' => 'name'];
66
67
    /**
68
     * Relations
69
     *
70
     * @var array
71
     */
72
    public $morphedByMany = [
73
        'posts'  => [Post::class, 'name' => Tag::PIVOT_COLUMN],
74
        'series'  => [Series::class, 'name' => Tag::PIVOT_COLUMN],
75
    ];
76
77
    /**
78
     * Fillable fields
79
     *
80
     * @var array
81
     */
82
    public $fillable = [
83
        'name',
84
        'slug',
85
    ];
86
87
    public $attachMany = [
88
        'featured_images' => [
89
            File::class,
90
            'order' => 'sort_order',
91
            'delete' => true
92
        ]
93
    ];
94
95
    public $attachOne = [
96
        'cover_image' => [
97
            File::class,
98
            'delete' => true
99
        ]
100
    ];
101
102
    /**
103
     * Validation rules
104
     *
105
     * @var array
106
     */
107
    public $rules = [
108
        'name' => 'required|unique:' . self::TABLE_NAME . '|min:2',
109
        'slug' => 'required|unique:' . self::TABLE_NAME . '|min:2|regex:/^[\w\-]+$/iu'
110
    ];
111
112
    /**
113
     * The tag might be created via tag list and therefore it won't have a slug
114
     */
115
    public function beforeValidate()
116
    {
117
        // Generate a URL slug for this model
118
        if (!$this->exists && !$this->slug) {
119
            $this->slug = Str::slug($this->name);
120
        }
121
    }
122
123
    /**
124
     * Validation messages
125
     *
126
     * @var array
127
     */
128
    public $customMessages = [
129
        'name.required' => Plugin::LOCALIZATION_KEY . 'form.tags.name_required',
130
        'name.unique'   => Plugin::LOCALIZATION_KEY . 'form.tags.name_unique',
131
        'name.min'      => Plugin::LOCALIZATION_KEY . 'form.tags.name_too_short',
132
133
        'slug.required' => Plugin::LOCALIZATION_KEY . 'form.tags.slug_required',
134
        'slug.unique'   => Plugin::LOCALIZATION_KEY . 'form.tags.slug_unique',
135
        'slug.regex'    => Plugin::LOCALIZATION_KEY . 'form.tags.slug_invalid',
136
        'slug.min'      => Plugin::LOCALIZATION_KEY . 'form.tags.slug_too_short',
137
    ];
138
139
    /**
140
     * The attributes on which the post list can be ordered
141
     *
142
     * @var array
143
     */
144
    public static $sortingOptions = [
145
        'name asc' => Plugin::LOCALIZATION_KEY . 'order_options.name_asc',
146
        'name desc' => Plugin::LOCALIZATION_KEY . 'order_options.name_desc',
147
        'created_at asc' => Plugin::LOCALIZATION_KEY . 'order_options.created_at_asc',
148
        'created_at desc' => Plugin::LOCALIZATION_KEY . 'order_options.created_at_desc',
149
        'posts_count asc' => Plugin::LOCALIZATION_KEY . 'order_options.post_count_asc',
150
        'posts_count desc' => Plugin::LOCALIZATION_KEY . 'order_options.post_count_desc',
151
        'random' => Plugin::LOCALIZATION_KEY . 'order_options.random'
152
    ];
153
154
    /**
155
     * Returns post combined combined from posts with this tag and posts with series under this this tag
156
     *
157
     * @return int
158
     */
159
    public function getCombinedPostCountAttribute(): int
160
    {
161
        return $this->posts_count + $this->series_posts_count;
162
    }
163
164
    /**
165
     * @param array $params
166
     *
167
     * @return array
168
     */
169
    protected function getModelUrlParams(array $params): array
170
    {
171
        return [
172
            array_get($params, 'tag', 'tag') => $this->slug
173
        ];
174
    }
175
176
    /**
177
     * @param Builder $query
178
     * @param array   $options
179
     *
180
     * @return void
181
     */
182
    protected function withRelation(Builder $query, array $options)
183
    {
184
        $this->postRelation($query, $options);
185
186
        $this->seriesRelation($query, $options);
187
    }
188
189
    /**
190
     * @param Builder $query
191
     * @param array   $options
192
     */
193
    protected function queryPostSlug(Builder $query, array $options)
194
    {
195
        parent::queryPostSlug($query, $options);
196
197
        if (!empty($options['post']) && !empty($options['includeSeriesTags'])) {
198
            $query->orWhereHas('series', static function ($query) use ($options) {
199
                $query->whereHas(
200
                    'posts',
201
                    static function ($query) use ($options) {
202
                        ModelAbstract::whereTranslatableProperty($query, 'slug', $options['post']);
203
                    }
204
                );
205
            });
206
        }
207
    }
208
209
    /**
210
     * @param Builder $query
211
     * @param array   $options
212
     *
213
     * @return void
214
     */
215
    protected function queryDisplayEmpty(Builder $query, array $options)
216
    {
217
        if (empty($options['displayEmpty'])) {
218
            $query
219
                ->having('posts_count', '>', 0)
220
                ->orHaving('series_count', '>', 0);
221
        }
222
    }
223
224
    /**
225
     * @param Builder $query
226
     * @param array   $options
227
     *
228
     * @return void
229
     */
230 View Code Duplication
    private function postRelation(Builder $query, array $options)
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...
231
    {
232
        if (!empty($options['fetchPosts'])) {
233
            $query->with(
234
                [
235
                    'posts' => static function (MorphToMany $query) use ($options) {
236
                        $query->isPublished();
237
238
                        self::handleExceptions($query->getQuery(), $options);
239
                    }
240
                ]
241
            );
242
        }
243
244
        $query->withCount(
245
            [
246
                'posts' => static function ($query) use ($options) {
247
                    $query->isPublished();
248
249
                    self::handleExceptions($query, $options);
250
                }
251
            ]
252
        );
253
    }
254
255
    /**
256
     * @param Builder $query
257
     * @param array   $options
258
     *
259
     * @return void
260
     */
261 View Code Duplication
    private function seriesRelation(Builder $query, array $options)
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...
262
    {
263
        $query->withCount('series');
264
265
        if (!empty($options['fetchSeriesPostCount'])) {
266
            $query->with(
267
                [
268
                    'series' => static function (MorphToMany $query) use ($options) {
269
                        $query->withCount(
270
                            [
271
                                'posts' => static function ($query) use ($options) {
272
                                    $query->isPublished();
273
274
                                    self::handleExceptions($query, $options);
275
                                }
276
                            ]
277
                        );
278
                    }
279
                ]
280
            );
281
        }
282
    }
283
}
284