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