Passed
Push — master ( f7aa08...006911 )
by Mihail
04:33
created

EntityCategoryList::buildContent()   C

Complexity

Conditions 10
Paths 68

Size

Total Lines 69
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 69
rs 6.0493
cc 10
eloc 43
nc 68
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
use Ffcms\Core\Helper\Url;
22
23
/**
24
 * Class EntityCategoryList. Build content and category data to display in views based on pathway.
25
 * @package Apps\Model\Front\Content
26
 */
27
class EntityCategoryList extends Model
28
{
29
    // page breaker to split short and full content
30
    const PAGE_BREAK = '<div style="page-break-after: always">';
31
32
    // properties to display: content item collection, category data, etc
33
    public $items;
34
    public $category;
35
    public $categories;
36
37
    // private items used on model building
38
    private $_path;
39
    private $_configs;
40
    private $_page = 0;
41
    private $_sort;
42
    private $_contentCount = 0;
43
44
    private $_currentCategory;
45
    private $_allCategories;
46
    private $_catIds;
47
48
    /**
49
     * EntityCategoryList constructor. Pass pathway as string and data of multi-category system
50
     * @param string $path
51
     * @param array $configs
52
     * @param int $offset
53
     * @param string $sort
54
     */
55
    public function __construct($path, array $configs, $offset = 0, $sort = 'newest')
56
    {
57
        $this->_path = $path;
58
        $this->_configs = $configs;
59
        $this->_page = (int)$offset;
60
        $this->_sort = $sort;
61
        parent::__construct();
62
    }
63
64
    /**
65
     * Build model properties
66
     * @throws ForbiddenException
67
     * @throws NotFoundException
68
     */
69
    public function before()
70
    {
71
        // find one or more categories where we must looking for content items
72
        if ((int)$this->_configs['multiCategories'] === 1) {
73
            $this->findCategories();
74
        } else {
75
            $this->findCategory();
76
        }
77
78
        // try to find content items depend of founded category(ies)
79
        $records = $this->findItems();
80
        // build output information
81
        $this->buildCategory();
82
        $this->buildContent($records);
83
    }
84
85
    /**
86
     * Find current category data
87
     * @throws NotFoundException
88
     */
89
    private function findCategory()
90
    {
91
        // get current category
92
        $query = ContentCategory::where('path', '=', $this->_path);
93
        if ($query->count() !== 1) {
94
            throw new NotFoundException(__('Category is not founded'));
95
        }
96
97
        // set properties from query
98
        $this->_allCategories = $query->get();
99
        $this->_currentCategory = $query->first();
100
        $this->_catIds[] = $this->_currentCategory['id'];
101
    }
102
103
    /**
104
     * Find multiple categories child of current
105
     * @throws NotFoundException
106
     */
107
    private function findCategories()
108
    {
109
        // get all categories for current path and child of it
110
        $query = ContentCategory::where('path', 'like', $this->_path . '%');
111
        if ($query->count() < 1) {
112
            throw new NotFoundException(__('Category is not founded'));
113
        }
114
        // get result as object
115
        $result = $query->get();
116
117
        // extract ids from result as array by key id
118
        $this->_catIds = Arr::ploke('id', $result->toArray());
119
120
        // get current category matching
121
        foreach ($result as $row) {
122
            if ($row->path === $this->_path) {
123
                $this->_currentCategory = $row;
124
            }
125
        }
126
127
        // set result to property
128
        $this->_allCategories = $result;
129
    }
130
131
    /**
132
     * Find content items on database and return rows as object
133
     * @return mixed
134
     * @throws NotFoundException
135
     */
136
    private function findItems()
137
    {
138
        if (!Obj::isArray($this->_catIds) || count($this->_catIds) < 1) {
139
            throw new NotFoundException(__('Category is not founded'));
140
        }
141
142
        // calculate selection offset
143
        $itemPerPage = (int)$this->_configs['itemPerCategory'];
144
        if ($itemPerPage < 1) {
145
            $itemPerPage = 1;
146
        }
147
        $offset = $this->_page * $itemPerPage;
148
149
        // get all items from categories
150
        $query = ContentRecord::whereIn('category_id', $this->_catIds)
151
            ->where('display', '=', 1);
152
        // save count
153
        $this->_contentCount = $query->count();
154
155
        // apply sort by
156
        switch ($this->_sort) {
157
            case 'rating':
158
                $query = $query->orderBy('rating', 'DESC');
159
                break;
160
            case 'views':
161
                $query = $query->orderBy('views', 'DESC');
162
                break;
163
            default:
164
                $query = $query->orderBy('created_at', 'DESC');
165
                break;
166
        }
167
168
        // make select based on offset
169
        return $query->skip($offset)->take($itemPerPage)->get();
170
    }
171
172
    /**
173
     * Prepare category data to display
174
     * @throws ForbiddenException
175
     */
176
    private function buildCategory()
177
    {
178
        // prepare rss url link for current category if enabled
179
        $rssUrl = false;
180
        if ((int)$this->_configs['rss'] === 1) {
181
            $rssUrl = App::$Alias->baseUrl . '/content/rss/' . $this->_currentCategory->path;
182
            $rssUrl = rtrim($rssUrl, '/');
183
        }
184
185
        // prepare sorting urls
186
        $catSortParams = [];
187
        if (App::$Request->query->get('page') !== null) {
188
            $catSortParams['page'] = (int)App::$Request->query->get('page');
189
        }
190
        $catSortUrls = [
191
            'views' => Url::to('content/list', $this->_currentCategory->path, null, Arr::merge($catSortParams, ['sort' => 'views']), false),
192
            'rating' => Url::to('content/list', $this->_currentCategory->path, null, Arr::merge($catSortParams, ['sort' => 'rating']), false),
193
            'newest' => Url::to('content/list', $this->_currentCategory->path, null, $catSortParams, false)
194
        ];
195
196
        // prepare current category data to output (unserialize locales and strip tags)
197
        $this->category = [
198
            'title' => App::$Security->strip_tags($this->_currentCategory->getLocaled('title')),
0 ignored issues
show
Bug introduced by
It seems like $this->_currentCategory->getLocaled('title') targeting Ffcms\Core\Arch\ActiveModel::getLocaled() can also be of type null; however, Ffcms\Core\Helper\Security::strip_tags() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
199
            'description' => App::$Security->strip_tags($this->_currentCategory->getLocaled('description')),
0 ignored issues
show
Bug introduced by
It seems like $this->_currentCategory-...tLocaled('description') targeting Ffcms\Core\Arch\ActiveModel::getLocaled() can also be of type null; however, Ffcms\Core\Helper\Security::strip_tags() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
200
            'configs' => Serialize::decode($this->_currentCategory->configs),
201
            'path' => $this->_currentCategory->path,
202
            'rss' => $rssUrl,
203
            'sort' => $catSortUrls
204
        ];
205
206
        // check if this category is hidden
207 View Code Duplication
        if ((int)$this->category['configs']['showCategory'] !== 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
208
            throw new ForbiddenException(__('This category is not available to view'));
209
        }
210
211
        // make sorted tree of categories to display in breadcrumbs
212
        foreach ($this->_allCategories as $cat) {
213
            $this->categories[$cat->id] = $cat;
214
        }
215
    }
216
217
    /**
218
     * Build content data to model properties
219
     * @param $records
220
     * @throws ForbiddenException
221
     * @throws NotFoundException
222
     */
223
    private function buildContent($records)
224
    {
225
        $nullItems = 0;
226
        foreach ($records as $row) {
227
            /** @var Content $row */
228
229
            // check title length on current language locale
230
            $localeTitle = App::$Security->strip_tags($row->getLocaled('title'));
231
            if (Str::likeEmpty($localeTitle)) {
232
                ++$nullItems;
233
                continue;
234
            }
235
236
            // get snippet from full text for current locale
237
            $text = Text::snippet($row->getLocaled('text'));
238
239
            $itemPath = $this->categories[$row->category_id]->path;
240
            if (!Str::likeEmpty($itemPath)) {
241
                $itemPath .= '/';
242
            }
243
            $itemPath .= $row->path;
244
245
            // prepare tags data
246
            $tags = $row->getLocaled('meta_keywords');
247
            if (!Str::likeEmpty($tags)) {
248
                $tags = explode(',', $tags);
249
            } else {
250
                $tags = null;
251
            }
252
253
            $owner = App::$User->identity($row->author_id);
254
            // make a fake if user is not exist over id
255
            if ($owner === null) {
256
                $owner = new User();
257
            }
258
259
            // check if current user can rate item
260
            $ignoredRate = App::$Session->get('content.rate.ignore');
261
            $canRate = true;
262
            if (Obj::isArray($ignoredRate) && Arr::in((string)$row->id, $ignoredRate)) {
263
                $canRate = false;
264
            }
265
            if (!App::$User->isAuth()) {
266
                $canRate = false;
267
            }
268
269
            // build result array
270
            $this->items[] = [
271
                'id' => $row->id,
272
                'title' => $localeTitle,
273
                'text' => $text,
274
                'date' => Date::convertToDatetime($row->created_at, Date::FORMAT_TO_HOUR),
275
                'author' => $owner,
276
                'poster' => $row->getPosterUri(),
277
                'thumb' => $row->getPosterThumbUri(),
278
                'thumbSize' => File::size($row->getPosterThumbUri()),
279
                'views' => (int)$row->views,
280
                'rating' => (int)$row->rating,
281
                'canRate' => $canRate,
282
                'category' => $this->categories[$row->category_id],
283
                'uri' => '/content/read/' . $itemPath,
284
                'tags' => $tags
285
            ];
286
        }
287
288
        if ($nullItems === $this->_contentCount) {
289
            throw new NotFoundException(__('Content is not founded'));
290
        }
291
    }
292
293
    /**
294
     * Get content items count
295
     * @return int
296
     */
297
    public function getContentCount()
298
    {
299
        return $this->_contentCount;
300
    }
301
}