1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Apps\Model\Front\Content; |
4
|
|
|
|
5
|
|
|
|
6
|
|
|
use Apps\ActiveRecord\Content; |
7
|
|
|
use Apps\ActiveRecord\ContentCategory; |
8
|
|
|
use Apps\ActiveRecord\Content as ContentRecord; |
9
|
|
|
use Apps\ActiveRecord\User; |
10
|
|
|
use Ffcms\Core\App; |
11
|
|
|
use Ffcms\Core\Arch\Model; |
12
|
|
|
use Ffcms\Core\Exception\ForbiddenException; |
13
|
|
|
use Ffcms\Core\Exception\NotFoundException; |
14
|
|
|
use Ffcms\Core\Helper\Date; |
15
|
|
|
use Ffcms\Core\Helper\FileSystem\File; |
16
|
|
|
use Ffcms\Core\Helper\Serialize; |
17
|
|
|
use Ffcms\Core\Helper\Text; |
18
|
|
|
use Ffcms\Core\Helper\Type\Arr; |
19
|
|
|
use Ffcms\Core\Helper\Type\Obj; |
20
|
|
|
use Ffcms\Core\Helper\Type\Str; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Class EntityCategoryList. Build content and category data to display in views based on pathway. |
24
|
|
|
* @package Apps\Model\Front\Content |
25
|
|
|
*/ |
26
|
|
|
class EntityCategoryList extends Model |
27
|
|
|
{ |
28
|
|
|
// page breaker to split short and full content |
29
|
|
|
const PAGE_BREAK = '<div style="page-break-after: always">'; |
30
|
|
|
|
31
|
|
|
// properties to display: content item collection, category data, etc |
32
|
|
|
public $items; |
33
|
|
|
public $category; |
34
|
|
|
public $categories; |
35
|
|
|
|
36
|
|
|
// private items used on model building |
37
|
|
|
private $_path; |
38
|
|
|
private $_configs; |
39
|
|
|
private $_page = 0; |
40
|
|
|
private $_contentCount = 0; |
41
|
|
|
|
42
|
|
|
private $_currentCategory; |
43
|
|
|
private $_allCategories; |
44
|
|
|
private $_catIds; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* EntityCategoryList constructor. Pass pathway as string and data of multi-category system |
48
|
|
|
* @param string $path |
49
|
|
|
* @param array $configs |
50
|
|
|
* @param int $offset |
51
|
|
|
*/ |
52
|
|
|
public function __construct($path, array $configs, $offset = 0) |
53
|
|
|
{ |
54
|
|
|
$this->_path = $path; |
55
|
|
|
$this->_configs = $configs; |
56
|
|
|
$this->_page = (int)$offset; |
57
|
|
|
parent::__construct(); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Build model properties |
62
|
|
|
* @throws ForbiddenException |
63
|
|
|
* @throws NotFoundException |
64
|
|
|
*/ |
65
|
|
|
public function before() |
66
|
|
|
{ |
67
|
|
|
// find one or more categories where we must looking for content items |
68
|
|
|
if ((int)$this->_configs['multiCategories'] === 1) { |
69
|
|
|
$this->findCategories(); |
70
|
|
|
} else { |
71
|
|
|
$this->findCategory(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
// try to find content items depend of founded category(ies) |
75
|
|
|
$records = $this->findItems(); |
76
|
|
|
// prepare output data |
77
|
|
|
$this->buildOutput($records); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Find current category data |
82
|
|
|
* @throws NotFoundException |
83
|
|
|
*/ |
84
|
|
|
private function findCategory() |
85
|
|
|
{ |
86
|
|
|
// get current category |
87
|
|
|
$query = ContentCategory::where('path', '=', $this->_path); |
88
|
|
|
if ($query->count() !== 1) { |
89
|
|
|
throw new NotFoundException(__('Category is not founded')); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
// set properties from query |
93
|
|
|
$this->_allCategories = $query->get(); |
94
|
|
|
$this->_currentCategory = $query->first(); |
95
|
|
|
$this->_catIds[] = $this->_currentCategory['id']; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Find multiple categories child of current |
100
|
|
|
* @throws NotFoundException |
101
|
|
|
*/ |
102
|
|
|
private function findCategories() |
103
|
|
|
{ |
104
|
|
|
// get all categories for current path and child of it |
105
|
|
|
$query = ContentCategory::where('path', 'like', $this->_path . '%'); |
106
|
|
|
if ($query->count() < 1) { |
107
|
|
|
throw new NotFoundException(__('Category is not founded')); |
108
|
|
|
} |
109
|
|
|
// get result as object |
110
|
|
|
$result = $query->get(); |
111
|
|
|
|
112
|
|
|
// extract ids from result as array by key id |
113
|
|
|
$this->_catIds = Arr::ploke('id', $result->toArray()); |
114
|
|
|
|
115
|
|
|
// get current category matching |
116
|
|
|
foreach ($result as $row) { |
117
|
|
|
if ($row->path === $this->_path) { |
118
|
|
|
$this->_currentCategory = $row; |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
// set result to property |
123
|
|
|
$this->_allCategories = $result; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Find content items on database and return rows as object |
128
|
|
|
* @return mixed |
129
|
|
|
* @throws NotFoundException |
130
|
|
|
*/ |
131
|
|
|
private function findItems() |
132
|
|
|
{ |
133
|
|
|
if (!Obj::isArray($this->_catIds) || count($this->_catIds) < 1) { |
134
|
|
|
throw new NotFoundException(__('Category is not founded')); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// calculate selection offset |
138
|
|
|
$itemPerPage = (int)$this->_configs['itemPerCategory']; |
139
|
|
|
if ($itemPerPage < 1) { |
140
|
|
|
$itemPerPage = 1; |
141
|
|
|
} |
142
|
|
|
$offset = $this->_page * $itemPerPage; |
143
|
|
|
|
144
|
|
|
// get all items from categories |
145
|
|
|
$query = ContentRecord::whereIn('category_id', $this->_catIds) |
146
|
|
|
->where('display', '=', 1); |
147
|
|
|
// save count |
148
|
|
|
$this->_contentCount = $query->count(); |
149
|
|
|
|
150
|
|
|
// make select based on offset |
151
|
|
|
return $query->skip($offset)->take($itemPerPage)->orderBy('created_at', 'DESC')->get(); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Build content data to model properties |
156
|
|
|
* @param $records |
157
|
|
|
* @throws ForbiddenException |
158
|
|
|
* @throws NotFoundException |
159
|
|
|
*/ |
160
|
|
|
private function buildOutput($records) |
161
|
|
|
{ |
162
|
|
|
// prepare current category data to output (unserialize locales and strip tags) |
163
|
|
|
$this->category = [ |
164
|
|
|
'title' => App::$Security->strip_tags($this->_currentCategory->getLocaled('title')), |
165
|
|
|
'description' => App::$Security->strip_tags($this->_currentCategory->getLocaled('description')), |
166
|
|
|
'configs' => Serialize::decode($this->_currentCategory->configs), |
167
|
|
|
'path' => $this->_currentCategory->path |
168
|
|
|
]; |
169
|
|
|
|
170
|
|
|
// check if this category is hidden |
171
|
|
View Code Duplication |
if ((int)$this->category['configs']['showCategory'] !== 1) { |
|
|
|
|
172
|
|
|
throw new ForbiddenException(__('This category is not available to view')); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
// make sorted tree of categories to display in breadcrumbs |
176
|
|
|
foreach ($this->_allCategories as $cat) { |
177
|
|
|
$this->categories[$cat->id] = $cat; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
$nullItems = 0; |
181
|
|
|
foreach ($records as $row) { |
182
|
|
|
/** @var Content $row */ |
183
|
|
|
|
184
|
|
|
// check title length on current language locale |
185
|
|
|
$localeTitle = App::$Security->strip_tags($row->getLocaled('title')); |
186
|
|
|
if (Str::likeEmpty($localeTitle)) { |
187
|
|
|
++$nullItems; |
188
|
|
|
continue; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
// get snippet from full text for current locale |
192
|
|
|
$text = Text::snippet($row->getLocaled('text')); |
193
|
|
|
|
194
|
|
|
$itemPath = $this->categories[$row->category_id]->path; |
195
|
|
|
if (!Str::likeEmpty($itemPath)) { |
196
|
|
|
$itemPath .= '/'; |
197
|
|
|
} |
198
|
|
|
$itemPath .= $row->path; |
199
|
|
|
|
200
|
|
|
// prepare tags data |
201
|
|
|
$tags = $row->getLocaled('meta_keywords'); |
202
|
|
|
if (!Str::likeEmpty($tags)) { |
203
|
|
|
$tags = explode(',', $tags); |
204
|
|
|
} else { |
205
|
|
|
$tags = null; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
$owner = App::$User->identity($row->author_id); |
209
|
|
|
// make a fake if user is not exist over id |
210
|
|
|
if ($owner === null) { |
211
|
|
|
$owner = new User(); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
// check if current user can rate item |
215
|
|
|
$ignoredRate = App::$Session->get('content.rate.ignore'); |
216
|
|
|
$canRate = true; |
217
|
|
|
if (Obj::isArray($ignoredRate) && Arr::in((string)$row->id, $ignoredRate)) { |
218
|
|
|
$canRate = false; |
219
|
|
|
} |
220
|
|
|
if (!App::$User->isAuth()) { |
221
|
|
|
$canRate = false; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
// build result array |
225
|
|
|
$this->items[] = [ |
226
|
|
|
'id' => $row->id, |
227
|
|
|
'title' => $localeTitle, |
228
|
|
|
'text' => $text, |
229
|
|
|
'date' => Date::convertToDatetime($row->created_at, Date::FORMAT_TO_HOUR), |
230
|
|
|
'author' => $owner, |
231
|
|
|
'poster' => $row->getPosterUri(), |
232
|
|
|
'thumb' => $row->getPosterThumbUri(), |
233
|
|
|
'thumbSize' => File::size($row->getPosterThumbUri()), |
234
|
|
|
'views' => (int)$row->views, |
235
|
|
|
'rating' => (int)$row->rating, |
236
|
|
|
'canRate' => $canRate, |
237
|
|
|
'category' => $this->categories[$row->category_id], |
238
|
|
|
'uri' => '/content/read/' . $itemPath, |
239
|
|
|
'tags' => $tags |
240
|
|
|
]; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
if ($nullItems === $this->_contentCount) { |
244
|
|
|
throw new NotFoundException(__('Content is not founded')); |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Get content items count |
250
|
|
|
* @return int |
251
|
|
|
*/ |
252
|
|
|
public function getContentCount() |
253
|
|
|
{ |
254
|
|
|
return $this->_contentCount; |
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.