Passed
Push — master ( 6b7895...39c748 )
by Nate
03:35
created

ElementAccessor   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 620
Duplicated Lines 4.03 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 56
lcom 1
cbo 10
dl 25
loc 620
rs 6.3636
c 0
b 0
f 0

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ElementAccessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ElementAccessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @package    Spark
5
 * @author     Flipbox Factory <[email protected]>
6
 * @copyright  2010-2016 Flipbox Digital Limited
7
 * @license    https://github.com/FlipboxFactory/Craft3-Spark/blob/master/LICENSE
8
 * @link       https://github.com/FlipboxFactory/Craft3-Spark
9
 * @since      Class available since Release 1.1.0
10
 */
11
12
namespace flipbox\spark\services;
13
14
use Craft;
15
use craft\base\Element;
16
use craft\base\ElementInterface;
17
use craft\elements\db\ElementQueryInterface;
18
use flipbox\spark\exceptions\ElementNotFoundException;
19
use flipbox\spark\exceptions\InvalidElementException;
20
use flipbox\spark\helpers\ElementHelper;
21
use flipbox\spark\helpers\QueryHelper;
22
use flipbox\spark\helpers\SiteHelper;
23
use yii\base\Component as BaseComponent;
24
25
abstract class ElementAccessor extends BaseComponent
26
{
27
28
    /**
29
     * @var [ElementInterface[]]
30
     */
31
    protected $_cacheAll;
32
33
    /**
34
     * @var [ElementInterface[]]
35
     */
36
    protected $_cacheById = [];
37
38
    /**
39
     * The element instance that this class interacts with
40
     */
41
    const ELEMENT_CLASS_INSTANCE = ElementInterface::class;
42
43
    /**
44
     * The element this class interacts with
45
     */
46
    const ELEMENT_CLASS = '';
47
48
49
    /*******************************************
50
     * CREATE
51
     *******************************************/
52
53
    /**
54
     * @return string
55
     */
56
    public function getElementClass()
57
    {
58
        return static::ELEMENT_CLASS;
59
    }
60
61
    /**
62
     * @param array $config
63
     * @param string $scenario
64
     * @return Element|ElementInterface
65
     */
66
    public function create(array $config = [], $scenario = ElementHelper::DEFAULT_SCENARIO)
67
    {
68
69
        // Set the class the element should be
70
        $config['class'] = static::ELEMENT_CLASS;
71
72
        // Create new model
73
        return ElementHelper::create(
74
            $config,
75
            static::ELEMENT_CLASS_INSTANCE,
76
            $scenario
77
        );
78
79
    }
80
81
82
    /**
83
     * @param $identifier
84
     * @param int|null $siteId
85
     * @return Element|ElementInterface|null
86
     */
87
    public function find($identifier, int $siteId = null)
88
    {
89
90
        if ($identifier instanceof self::ELEMENT_CLASS_INSTANCE) {
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_VARIABLE or '$'
Loading history...
91
92
            $this->addToCache($identifier);
93
94
            return $identifier;
95
96
        } elseif (is_numeric($identifier)) {
97
98
            return $this->findById($identifier, $siteId);
99
100
        } elseif (is_array($identifier)) {
101
102
            return $this->getQuery($identifier)
103
                ->siteId($siteId)
104
                ->one();
105
106
        }
107
108
        return null;
109
110
    }
111
112
    /**
113
     * @param $id
114
     * @param int|null $siteId
115
     * @return Element|ElementInterface|null
116
     */
117
    public function findById($id, int $siteId = null)
118
    {
119
120
        // Check cache
121
        if (!$element = $this->findCacheById($id, $siteId)) {
122
123
            // Find new element
124
            if ($element = $this->freshFindById($id, $siteId)) {
125
126
                // Cache it
127
                $this->addToCache($element);
128
129
            } else {
130
131
                // Cache nothing
132
                $this->_cacheById[$id] = $element;
133
134
            }
135
136
        }
137
138
        return $element;
139
140
    }
141
142
    /*******************************************
143
     * GET
144
     *******************************************/
145
146
    /**
147
     * @param $identifier
148
     * @param int|null $siteId
149
     * @return Element|ElementInterface
150
     * @throws InvalidElementException
151
     */
152
    public function get($identifier, int $siteId = null)
153
    {
154
155
        // Find
156
        if (!$elemeny = $this->find($identifier, $siteId)) {
157
158
            $this->notFoundException();
159
160
        }
161
162
        return $elemeny;
163
164
    }
165
166
    /**
167
     * @param $id
168
     * @param int|null $siteId
169
     * @return Element|ElementInterface
170
     * @throws ElementNotFoundException
171
     */
172
    public function getById($id, int $siteId = null)
173
    {
174
175
        // Find by ID
176
        if (!$element = $this->findById($id, $siteId)) {
177
178
            $this->notFoundByIdException($id);
179
180
        }
181
182
        return $element;
183
184
    }
185
186
    /*******************************************
187
     * FRESH FIND
188
     *******************************************/
189
190
    /**
191
     * @param $id
192
     * @param int|null $siteId
193
     * @return Element|ElementInterface|null
194
     */
195
    public function freshFindById($id, int $siteId = null)
196
    {
197
        return Craft::$app->getElements()->getElementById($id, static::ELEMENT_CLASS, $siteId);
198
    }
199
200
    /**
201
     * @param $id
202
     * @param int|null $siteId
203
     * @return Element|ElementInterface
204
     * @throws ElementNotFoundException
205
     */
206
    public function freshGetById($id, int $siteId = null)
207
    {
208
209
        if (!$element = $this->freshFindById($id, $siteId)) {
210
211
            $this->notFoundByIdException($id);
212
213
        }
214
215
        return $element;
216
217
    }
218
219
220
    /*******************************************
221
     * QUERY
222
     *******************************************/
223
224
    /**
225
     * Get query
226
     *
227
     * @param $criteria
228
     * @return ElementQueryInterface
229
     */
230
    public function getQuery($criteria = [])
231
    {
232
233
        /** @var Element $elementClass */
234
        $elementClass = static::ELEMENT_CLASS;
235
236
        /** @var ElementQueryInterface $query */
237
        $query = $elementClass::find();
238
239
        // Configure it
240
        QueryHelper::configure(
241
            $query,
242
            $criteria
243
        );
244
245
        return $query;
246
247
    }
248
249
    /*******************************************
250
     * CACHE
251
     *******************************************/
252
253
    /**
254
     * @param $identifier
255
     * @param int|null $siteId
256
     * @return Element|ElementInterface|null
257
     */
258
    public function findCache($identifier, int $siteId = null)
259
    {
260
261
        if (is_numeric($identifier)) {
262
263
            return $this->findCacheById($identifier, $siteId);
264
265
        }
266
267
        return null;
268
269
    }
270
271
    /**
272
     * @param ElementInterface $element
273
     * @return $this
274
     */
275
    public function addToCache(ElementInterface $element)
276
    {
277
278
        $this->cacheById($element);
279
280
        return $this;
281
282
    }
283
284
    /**
285
     * Find an existing cache by ID
286
     *
287
     * @param $id
288
     * @param int|null $siteId
289
     * @return Element|ElementInterface|null
290
     */
291
    public function findCacheById($id, int $siteId = null)
292
    {
293
294
        // Resolve siteId
295
        $siteId = SiteHelper::resolveSiteId($siteId);
296
297
        // Check if already in addToCache
298
        if ($this->isCachedById($id, $siteId)) {
299
300
            return $this->_cacheById[$siteId][$id];
301
302
        }
303
304
        return null;
305
306
    }
307
308
    /**
309
     * Identify whether in cached by ID
310
     *
311
     * @param $id
312
     * @param int|null $siteId
313
     * @return bool
314
     */
315
    protected function isCachedById($id, int $siteId = null)
316
    {
317
        // Resolve siteId
318
        $siteId = SiteHelper::resolveSiteId($siteId);
319
320
        if (!isset($this->_cacheById[$siteId])) {
321
            $this->_cacheById[$siteId] = [];
322
        }
323
324
        return array_key_exists($id, $this->_cacheById[$siteId]);
325
    }
326
327
    /**
328
     * @param ElementInterface $element
329
     * @return $this
330
     */
331
    protected function cacheById(ElementInterface $element)
332
    {
333
334
        /** @var Element $element */
335
336
        $id = $element->id;
337
338
        $siteId = $element->siteId;
339
340
        // Check if already in cache
341
        if (!$this->isCachedById($id, $siteId)) {
342
343
            // Cache it
344
            $this->_cacheById[$siteId][$id] = $element;
345
346
        }
347
348
        return $this;
349
350
    }
351
352
    /*******************************************
353
     * EXCEPTIONS
354
     *******************************************/
355
356
    /**
357
     * @throws ElementNotFoundException
358
     */
359
    protected function notFoundException()
360
    {
361
362
        throw new ElementNotFoundException(
363
            sprintf(
364
                "Element does not exist."
365
            )
366
        );
367
368
    }
369
370
    /**
371
     * @param null $id
372
     * @throws ElementNotFoundException
373
     */
374
    protected function notFoundByIdException($id = null)
375
    {
376
377
        throw new ElementNotFoundException(
378
            sprintf(
379
                'Element does not exist with the id "%s".',
380
                (string)$id
381
            )
382
        );
383
384
    }
385
386
}
387