Completed
Push — master ( 193c3c...281dde )
by Nate
07:20
created

ElementAccessor   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 339
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 9
dl 0
loc 339
ccs 0
cts 145
cp 0
rs 9.6
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
elementClass() 0 1 ?
A elementClassInstance() 0 4 1
B create() 0 25 4
A getQuery() 0 16 1
A find() 0 16 3
A get() 0 8 2
A findById() 0 15 3
A freshFindById() 0 15 2
A freshGetById() 0 8 2
A findCache() 0 8 3
A addToCache() 0 5 1
A findCacheById() 0 10 2
A isCachedById() 0 10 2
A cacheById() 0 15 2
A applyScenario() 0 8 2
A notFoundException() 0 8 1
A notFoundByIdException() 0 9 1
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipboxfactory/craft-ember/blob/master/LICENSE
6
 * @link       https://github.com/flipboxfactory/craft-ember
7
 */
8
9
namespace flipbox\ember\services\traits;
10
11
use craft\base\Element;
12
use craft\base\ElementInterface;
13
use craft\elements\db\ElementQuery;
14
use craft\elements\db\ElementQueryInterface;
15
use craft\helpers\ArrayHelper;
16
use flipbox\ember\exceptions\ElementNotFoundException;
17
use flipbox\ember\helpers\ObjectHelper;
18
use flipbox\ember\helpers\QueryHelper;
19
use flipbox\ember\helpers\SiteHelper;
20
use yii\base\InvalidConfigException;
21
22
/**
23
 * @author Flipbox Factory <[email protected]>
24
 * @since 1.0.0
25
 */
26
trait ElementAccessor
27
{
28
    /**
29
     * @var [ElementInterface[]]
30
     */
31
    protected $cacheById = [];
32
33
    /*******************************************
34
     * OBJECT CLASSES
35
     *******************************************/
36
37
    /**
38
     * @return string
39
     */
40
    abstract public static function elementClass(): string;
41
42
    /**
43
     * @return string
44
     */
45
    public static function elementClassInstance(): string
46
    {
47
        return ElementInterface::class;
48
    }
49
50
    /*******************************************
51
     * CREATE
52
     *******************************************/
53
54
    /**
55
     * @param array $config
56
     * @param string|null $toScenario
57
     * @throws InvalidConfigException
58
     * @return ElementInterface
59
     */
60
    public function create($config = [], string $toScenario = null): ElementInterface
61
    {
62
        // Treat records as known data and set via config
63
        if ($config instanceof ElementInterface) {
64
            return $this->applyScenario($config, $toScenario);
65
        }
66
67
        // Force Array
68
        if (!is_array($config)) {
69
            $config = ArrayHelper::toArray($config, [], false);
70
        }
71
72
        // Auto-set the class
73
        if ($class = static::elementClass()) {
74
            $config['class'] = $class;
75
        }
76
77
        /** @var ElementInterface $element */
78
        $element = ObjectHelper::create(
79
            $config,
80
            static::elementClassInstance()
81
        );
82
83
        return $this->applyScenario($element, $toScenario);
84
    }
85
86
    /*******************************************
87
     * QUERY
88
     *******************************************/
89
90
    /**
91
     * Get query
92
     *
93
     * @param $criteria
94
     * @return ElementQueryInterface
95
     */
96
    public function getQuery($criteria = [])
97
    {
98
        /** @var ElementInterface $elementClass */
99
        $elementClass = static::elementClass();
100
101
        /** @var ElementQueryInterface $query */
102
        $query = $elementClass::find();
103
104
        // Configure it
105
        QueryHelper::configure(
106
            $query,
107
            $criteria
108
        );
109
110
        return $query;
111
    }
112
113
    /*******************************************
114
     * FIND/GET
115
     *******************************************/
116
117
    /**
118
     * @param $identifier
119
     * @param int $siteId
120
     * @param string $toScenario
121
     * @return ElementInterface|null
122
     */
123
    public function find($identifier, int $siteId = null, string $toScenario = null)
124
    {
125
        if ($identifier instanceof ElementInterface) {
126
            $this->addToCache($identifier);
127
            return $this->applyScenario($identifier, $toScenario);
128
        } elseif (is_numeric($identifier)) {
129
            return $this->findById($identifier, $siteId);
130
        } elseif (is_array($identifier)) {
131
            $element = $this->getQuery($identifier)
132
                ->siteId($siteId)
133
                ->one();
134
            return $this->applyScenario($element, $toScenario);
0 ignored issues
show
Bug introduced by
It seems like $element defined by $this->getQuery($identif...>siteId($siteId)->one() on line 131 can also be of type array or null; however, flipbox\ember\services\t...cessor::applyScenario() does only seem to accept object<craft\base\ElementInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
135
        }
136
137
        return null;
138
    }
139
140
    /**
141
     * @param $identifier
142
     * @param int $siteId
143
     * @param string $toScenario
144
     * @return ElementInterface
145
     * @throws ElementNotFoundException
146
     */
147
    public function get($identifier, int $siteId = null, string $toScenario = null): ElementInterface
148
    {
149
        if (!$object = $this->find($identifier, $siteId, $toScenario)) {
150
            $this->notFoundException();
151
        }
152
153
        return $object;
154
    }
155
156
157
    /*******************************************
158
     * FIND/GET BY ID
159
     *******************************************/
160
161
    /**
162
     * @param int $id
163
     * @param int|null $siteId
164
     * @param string $toScenario
165
     * @return ElementInterface|null
166
     */
167
    public function findById(int $id, int $siteId = null, string $toScenario = null)
168
    {
169
        $siteId = SiteHelper::resolveSiteId($siteId);
170
171
        if (!$element = $this->findCacheById($id, $siteId)) {
172
            if (!$element = $this->freshFindById($id, $siteId)) {
173
                $this->cacheById[$siteId][$id] = null;
174
                return null;
175
            }
176
177
            $this->addToCache($element);
178
        }
179
180
        return $this->applyScenario($element, $toScenario);
181
    }
182
183
184
    /*******************************************
185
     * FRESH FIND
186
     *******************************************/
187
188
    /**
189
     * @param int $id
190
     * @param int|null $siteId
191
     * @param string $toScenario
192
     * @return ElementInterface|null
193
     */
194
    public function freshFindById(int $id, int $siteId = null, string $toScenario = null)
195
    {
196
        /** @var ElementQuery $query */
197
        $query = $this->getQuery();
198
        $query->id = $id;
199
        $query->siteId = $siteId;
200
        $query->status = null;
201
        $query->enabledForSite = false;
202
203
        if (!$element = $query->one()) {
204
            return null;
205
        }
206
207
        return $this->applyScenario($element, $toScenario);
208
    }
209
210
    /**
211
     * @param $id
212
     * @param int|null $siteId
213
     * @param string $toScenario
214
     * @return ElementInterface
215
     * @throws ElementNotFoundException
216
     */
217
    public function freshGetById($id, int $siteId = null, string $toScenario = null)
218
    {
219
        if (!$element = $this->freshFindById($id, $siteId, $toScenario)) {
220
            $this->notFoundByIdException($id);
221
        }
222
223
        return $element;
224
    }
225
226
    /*******************************************
227
     * CACHE
228
     *******************************************/
229
230
    /**
231
     * @param $identifier
232
     * @param int|null $siteId
233
     * @return ElementInterface|null
234
     */
235
    public function findCache($identifier, int $siteId = null)
236
    {
237
        if (is_numeric($identifier)) {
238
            return $this->findCacheById($identifier, $siteId);
239
        }
240
241
        return null;
242
    }
243
244
    /**
245
     * @param ElementInterface $element
246
     * @return static
247
     */
248
    public function addToCache(ElementInterface $element)
249
    {
250
        $this->cacheById($element);
251
        return $this;
252
    }
253
254
255
    /*******************************************
256
     * CACHE BY ID
257
     *******************************************/
258
259
    /**
260
     * Find an existing cache by ID
261
     *
262
     * @param int $id
263
     * @param int|null $siteId
264
     * @return ElementInterface|null
265
     */
266
    public function findCacheById(int $id, int $siteId = null)
267
    {
268
        $siteId = SiteHelper::resolveSiteId($siteId);
269
270
        if ($this->isCachedById($id, $siteId)) {
271
            return $this->cacheById[$siteId][$id];
272
        }
273
274
        return null;
275
    }
276
277
    /**
278
     * Identify whether in cached by ID
279
     *
280
     * @param int $id
281
     * @param int|null $siteId
282
     * @return bool
283
     */
284
    protected function isCachedById(int $id, int $siteId = null): bool
285
    {
286
        $siteId = SiteHelper::resolveSiteId($siteId);
287
288
        if (!array_key_exists($siteId, $this->cacheById)) {
289
            $this->cacheById[$siteId] = [];
290
        }
291
292
        return array_key_exists($id, $this->cacheById[$siteId]);
293
    }
294
295
    /**
296
     * @param ElementInterface $element
297
     * @return $this
298
     */
299
    protected function cacheById(ElementInterface $element)
300
    {
301
        /** @var Element $element */
302
        $id = $element->getId();
303
        $siteId = $element->siteId;
304
305
        // todo - ensure siteId is set?
306
307
        // Check if already in cache
308
        if (!$this->isCachedById($id, $siteId)) {
309
            $this->cacheById[$siteId][$id] = $element;
310
        }
311
312
        return $this;
313
    }
314
315
316
    /*******************************************
317
     * UTILITIES
318
     *******************************************/
319
320
    /**
321
     * @param ElementInterface|Element $element
322
     * @param string|null $toScenario
323
     * @return ElementInterface
324
     */
325
    protected function applyScenario(ElementInterface $element, string $toScenario = null): ElementInterface
326
    {
327
        if (null !== $toScenario) {
328
            $element->setScenario($toScenario);
329
        }
330
331
        return $element;
332
    }
333
334
335
    /*******************************************
336
     * EXCEPTIONS
337
     *******************************************/
338
339
    /**
340
     * @throws ElementNotFoundException
341
     */
342
    protected function notFoundException()
343
    {
344
        throw new ElementNotFoundException(
345
            sprintf(
346
                "Element does not exist."
347
            )
348
        );
349
    }
350
351
    /**
352
     * @param int|null $id
353
     * @throws ElementNotFoundException
354
     */
355
    protected function notFoundByIdException(int $id = null)
356
    {
357
        throw new ElementNotFoundException(
358
            sprintf(
359
                'Element does not exist with the id "%s".',
360
                (string)$id
361
            )
362
        );
363
    }
364
}
365