Test Setup Failed
Push — master ( 61c2c6...af32d6 )
by Mihail
06:18
created

EntityCategoryList   B

Complexity

Total Complexity 26

Size/Duplication

Total Lines 231
Duplicated Lines 1.3 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 4
Bugs 2 Features 0
Metric Value
wmc 26
c 4
b 2
f 0
lcom 1
cbo 17
dl 3
loc 231
rs 7.8571

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A before() 0 14 2
A findCategory() 0 13 2
B findCategories() 0 23 4
B findItems() 0 22 4
D buildOutput() 3 87 12
A getContentCount() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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) {
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...
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
}