Completed
Push — 5.1 ( c7eca6...1f8916 )
by Jarek
05:49
created

Metable   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 491
Duplicated Lines 4.07 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 14
Bugs 2 Features 1
Metric Value
wmc 61
c 14
b 2
f 1
lcom 1
cbo 8
dl 20
loc 491
rs 6.018

29 Methods

Rating   Name   Duplication   Size   Complexity  
A isMetaQueryable() 0 4 1
A metaSingleResult() 0 4 1
A generateMetaAlias() 0 4 1
A getMetaWhereConstraint() 0 18 4
A hasMeta() 0 4 1
A getMeta() 0 4 1
A setMeta() 0 4 1
A metaAttributes() 0 4 1
A getMetaAttributesAttribute() 0 4 1
A getMetaAttributesArray() 0 4 1
A getAllowedMeta() 0 4 2
A bootMetable() 17 17 2
A metaQuery() 3 8 2
B metaSelect() 0 24 6
A metaJoinQuery() 0 12 3
A orderByMeta() 0 6 1
A listsMeta() 0 12 2
A metaSelectListsKey() 0 12 3
A joinMeta() 0 16 1
A metaHasQuery() 0 14 2
A getMetaBoolean() 0 8 1
A getMetaOperator() 0 8 3
B unbindNumerics() 0 14 8
A getMetaBetweenConstraint() 0 11 1
A saveMeta() 0 10 3
A allowsMeta() 0 6 2
A getMetaAttributes() 0 6 1
A loadMetaAttributes() 0 12 3
A reloadMetaAttributes() 0 6 2

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 Metable 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 Metable, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Sofa\Eloquence;
4
5
use Sofa\Eloquence\Metable\Hooks;
6
use Sofa\Eloquence\Metable\Attribute;
7
use Sofa\Eloquence\Metable\AttributeBag;
8
use Sofa\Hookable\Contracts\ArgumentBag;
9
10
/**
11
 * @property array $allowedMeta
12
 */
13
trait Metable
14
{
15
    /**
16
     * Query methods customizable by this trait.
17
     *
18
     * @var array
19
     */
20
    protected $metaQueryable = [
21
        'where', 'whereBetween', 'whereIn', 'whereNull',
22
        'whereDate', 'whereYear', 'whereMonth', 'whereDay',
23
        'orderBy', 'pluck', 'value', 'aggregate', 'lists'
24
    ];
25
26
    /**
27
     * Register hooks for the trait.
28
     *
29
     * @codeCoverageIgnore
30
     *
31
     * @return void
32
     */
33 View Code Duplication
    public static function bootMetable()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
34
    {
35
        $hooks = new Hooks;
36
37
        foreach ([
38
                'setAttribute',
39
                'getAttribute',
40
                'toArray',
41
                'replicate',
42
                'save',
43
                '__isset',
44
                '__unset',
45
                'queryHook',
46
            ] as $method) {
47
            static::hook($method, $hooks->{$method}());
48
        }
49
    }
50
51
    /**
52
     * Determine wheter method called on the query is customizable by this trait.
53
     *
54
     * @param  string  $method
55
     * @return boolean
56
     */
57
    protected function isMetaQueryable($method)
58
    {
59
        return in_array($method, $this->metaQueryable);
60
    }
61
62
    /**
63
     * Custom query handler for querying meta attributes.
64
     *
65
     * @param  \Sofa\Eloquence\Builder $query
66
     * @param  string $method
67
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
68
     * @return mixed
69
     */
70
    protected function metaQuery(Builder $query, $method, ArgumentBag $args)
71
    {
72 View Code Duplication
        if (in_array($method, ['pluck', 'value', 'aggregate', 'orderBy', 'lists'])) {
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...
73
            return $this->metaJoinQuery($query, $method, $args);
74
        }
75
76
        return $this->metaHasQuery($query, $method, $args);
77
    }
78
79
    /**
80
     * Adjust meta columns for select statement.
81
     *
82
     * @param  \Sofa\Eloquence\Builder $query
83
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
84
     * @return void
85
     */
86
    protected function metaSelect(Builder $query, ArgumentBag $args)
87
    {
88
        $columns = $args->get('columns');
89
90
        foreach ($columns as $key => $column) {
91
            list($column, $alias) = $this->extractColumnAlias($column);
0 ignored issues
show
Bug introduced by
It seems like extractColumnAlias() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
92
93
            if ($this->hasColumn($column)) {
0 ignored issues
show
Bug introduced by
It seems like hasColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
94
                $select = "{$this->getTable()}.{$column}";
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
95
96
                if ($column !== $alias) {
97
                    $select .= " as {$alias}";
98
                }
99
100
                $columns[$key] = $select;
101
            } elseif (is_string($column) && strpos($column, '.') === false) {
102
                $table = $this->joinMeta($query, $column);
103
104
                $columns[$key] = "{$table}.meta_value as {$alias}";
105
            }
106
        }
107
108
        $args->set('columns', $columns);
109
    }
110
111
    /**
112
     * Join meta attributes table in order to call provided method.
113
     *
114
     * @param  \Sofa\Eloquence\Builder $query
115
     * @param  string $method
116
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
117
     * @return mixed
118
     */
119
    protected function metaJoinQuery(Builder $query, $method, ArgumentBag $args)
120
    {
121
        $alias = $this->joinMeta($query, $args->get('column'));
122
123
        // For aggregates we need the actual function name
124
        // so it can be called directly on the builder.
125
        $method = $args->get('function') ?: $method;
126
127
        return (in_array($method, ['orderBy', 'lists']))
128
            ? $this->{"{$method}Meta"}($query, $args, $alias)
129
            : $this->metaSingleResult($query, $method, $alias);
130
    }
131
132
    /**
133
     * Order query by meta attribute.
134
     *
135
     * @param  \Sofa\Eloquence\Builder $query
136
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
137
     * @param  string $alias
138
     * @return \Sofa\Eloquence\Builder
139
     */
140
    protected function orderByMeta(Builder $query, $args, $alias)
141
    {
142
        $query->with('metaAttributes')->getQuery()->orderBy("{$alias}.meta_value", $args->get('direction'));
143
144
        return $query;
145
    }
146
147
    /**
148
     * Get an array with the values of given meta attribute.
149
     *
150
     * @param  \Sofa\Eloquence\Builder $query
151
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
152
     * @param  string $alias
153
     * @return array
154
     */
155
    protected function listsMeta(Builder $query, ArgumentBag $args, $alias)
156
    {
157
        list($column, $key) = [$args->get('column'), $args->get('key')];
158
159
        $query->select("{$alias}.meta_value as {$column}");
160
161
        if (!is_null($key)) {
162
            $this->metaSelectListsKey($query, $key);
163
        }
164
165
        return $query->callParent('lists', $args->all());
166
    }
167
168
    /**
169
     * Add select clause for key of the list array.
170
     *
171
     * @param  \Sofa\Eloquence\Builder $query
172
     * @param  string $key
173
     * @return \Sofa\Eloquence\Builder
174
     */
175
    protected function metaSelectListsKey(Builder $query, $key)
176
    {
177
        if (strpos($key, '.') !== false) {
178
            return $query->addSelect($key);
0 ignored issues
show
Bug introduced by
The method addSelect() does not exist on Sofa\Eloquence\Builder. Did you maybe mean select()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
179
        } elseif ($this->hasColumn($key)) {
0 ignored issues
show
Bug introduced by
It seems like hasColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
180
            return $query->addSelect($this->getTable() . '.' . $key);
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
Bug introduced by
The method addSelect() does not exist on Sofa\Eloquence\Builder. Did you maybe mean select()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
181
        }
182
183
        $alias = $this->joinMeta($query, $key);
184
185
        return $query->addSelect("{$alias}.meta_value as {$key}");
0 ignored issues
show
Bug introduced by
The method addSelect() does not exist on Sofa\Eloquence\Builder. Did you maybe mean select()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
186
    }
187
188
    /**
189
     * Get single value result from the meta attribute.
190
     *
191
     * @param  \Sofa\Eloquence\Builder $query
192
     * @param  string $method
193
     * @param  string $alias
194
     * @return mixed
195
     */
196
    protected function metaSingleResult(Builder $query, $method, $alias)
197
    {
198
        return $query->getQuery()->select("{$alias}.meta_value")->{$method}("{$alias}.meta_value");
199
    }
200
201
202
    /**
203
     * Join meta attributes table.
204
     *
205
     * @param  \Sofa\Eloquence\Builder $query
206
     * @param  string $column
207
     * @return string
208
     */
209
    protected function joinMeta(Builder $query, $column)
210
    {
211
        $query->prefixColumnsForJoin();
212
213
        $alias = $this->generateMetaAlias();
214
215
        $table = (new Attribute)->getTable();
216
217
        $query->leftJoin("{$table} as {$alias}", function ($join) use ($alias, $column) {
0 ignored issues
show
Bug introduced by
The call to leftJoin() misses some required arguments starting with $operator.
Loading history...
218
            $join->on("{$alias}.metable_id", '=', $this->getQualifiedKeyName())
0 ignored issues
show
Bug introduced by
It seems like getQualifiedKeyName() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
219
                ->where("{$alias}.metable_type", '=', $this->getMorphClass())
0 ignored issues
show
Bug introduced by
It seems like getMorphClass() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
220
                ->where("{$alias}.meta_key", '=', $column);
221
        });
222
223
        return $alias;
224
    }
225
226
    /**
227
     * Generate unique alias for meta attributes table.
228
     *
229
     * @return string
230
     */
231
    protected function generateMetaAlias()
232
    {
233
        return md5(microtime(true)) . '_meta_alias';
234
    }
235
236
    /**
237
     * Add whereHas subquery on the meta attributes relation.
238
     *
239
     * @param  \Sofa\Eloquence\Builder $query
240
     * @param  string $method
241
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
242
     * @return \Sofa\Eloquence\Builder
243
     */
244
    protected function metaHasQuery(Builder $query, $method, ArgumentBag $args)
245
    {
246
        $boolean = $this->getMetaBoolean($args);
247
248
        $operator = $this->getMetaOperator($method, $args);
249
250
        if (in_array($method, ['whereBetween', 'where'])) {
251
            $this->unbindNumerics($args);
252
        }
253
254
        return $query
255
            ->has('metaAttributes', $operator, 1, $boolean, $this->getMetaWhereConstraint($method, $args))
256
            ->with('metaAttributes');
257
    }
258
259
    /**
260
     * Get boolean called on the original method and set it to default.
261
     *
262
     * @param  \Sofa\EloquenceArgumentBag $args
263
     * @return string
264
     */
265
    protected function getMetaBoolean(ArgumentBag $args)
266
    {
267
        $boolean = $args->get('boolean');
268
269
        $args->set('boolean', 'and');
270
271
        return $boolean;
272
    }
273
274
    /**
275
     * Determine the operator for count relation query.
276
     *
277
     * @param  string $method
278
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
279
     * @return string
280
     */
281
    protected function getMetaOperator($method, ArgumentBag $args)
282
    {
283
        if ($not = $args->get('not')) {
284
            $args->set('not', false);
285
        }
286
287
        return ($not ^ $this->isWhereNull($method, $args)) ? '<' : '>=';
0 ignored issues
show
Bug introduced by
It seems like isWhereNull() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
288
    }
289
290
    /**
291
     * Integers and floats must be passed in raw form in order to avoid string
292
     * comparison, due to the fact that all meta values are stored as strings.
293
     *
294
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
295
     * @return void
296
     */
297
    protected function unbindNumerics(ArgumentBag $args)
298
    {
299
        if (($value = $args->get('value')) && (is_int($value) || is_float($value))) {
300
            $args->set('value', $this->raw($value));
0 ignored issues
show
Bug introduced by
It seems like raw() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
301
        } elseif ($values = $args->get('values')) {
302
            foreach ($values as $key => $value) {
303
                if (is_int($value) || is_float($value)) {
304
                    $values[$key] = $this->raw($value);
0 ignored issues
show
Bug introduced by
It seems like raw() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
305
                }
306
            }
307
308
            $args->set('values', $values);
309
        }
310
    }
311
312
    /**
313
     * Get the relation constraint closure.
314
     *
315
     * @param  string $method
316
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
317
     * @return \Closure
318
     */
319
    protected function getMetaWhereConstraint($method, ArgumentBag $args)
320
    {
321
        $column = $args->get('column');
322
323
        $args->set('column', 'meta_value');
324
325
        if ($method === 'whereBetween') {
326
            return $this->getMetaBetweenConstraint($column, $args->get('values'));
327
        }
328
329
        return function ($query) use ($column, $method, $args) {
330
            $query->where('meta_key', $column);
331
332
            if ($args->get('value') || $args->get('values')) {
333
                call_user_func_array([$query, $method], $args->all());
334
            }
335
        };
336
    }
337
338
    /**
339
     * Query Builder whereBetween override required to pass raw numeric values.
340
     *
341
     * @param  string $column
342
     * @param  array  $values
343
     * @return \Closure
344
     */
345
    protected function getMetaBetweenConstraint($column, array $values)
346
    {
347
        $min = $values[0];
348
        $max = $values[1];
349
350
        return function ($query) use ($column, $min, $max) {
351
            $query->where('meta_key', $column)
352
                ->where('meta_value', '>=', $min)
353
                ->where('meta_value', '<=', $max);
354
        };
355
    }
356
357
    /**
358
     * Save new or updated meta attributes and delete the ones that were unset.
359
     *
360
     * @return void
361
     */
362
    protected function saveMeta()
363
    {
364
        foreach ($this->getMetaAttributes() as $attribute) {
365
            if (is_null($attribute->getValue())) {
366
                $attribute->delete();
367
            } else {
368
                $this->metaAttributes()->save($attribute);
369
            }
370
        }
371
    }
372
373
    /**
374
     * Determine whether meta attribute is allowed for the model.
375
     *
376
     * @param  string $key
377
     * @return boolean
378
     */
379
    public function allowsMeta($key)
380
    {
381
        $allowed = $this->getAllowedMeta();
382
383
        return empty($allowed) || in_array($key, $allowed);
384
    }
385
386
    /**
387
     * Determine whether meta attribute exists on the model.
388
     *
389
     * @param  string $key
390
     * @return boolean
391
     */
392
    public function hasMeta($key)
393
    {
394
        return array_key_exists($key, $this->getMetaAttributesArray());
395
    }
396
397
    /**
398
     * Get meta attribute value.
399
     *
400
     * @param  string $key
401
     * @return mixed
402
     */
403
    public function getMeta($key)
404
    {
405
        return $this->getMetaAttributes()->getValue($key);
406
    }
407
408
    /**
409
     * Set meta attribute.
410
     *
411
     * @param  string $key
412
     * @param  mixed  $value
413
     * @return void
414
     */
415
    public function setMeta($key, $value)
416
    {
417
        $this->getMetaAttributes()->set($key, $value);
418
    }
419
420
    /**
421
     * Meta attributes relation.
422
     *
423
     * @codeCoverageIgnore
424
     *
425
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
426
     */
427
    public function metaAttributes()
428
    {
429
        return $this->morphMany('Sofa\Eloquence\Metable\Attribute', 'metable');
0 ignored issues
show
Bug introduced by
It seems like morphMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
430
    }
431
432
    /**
433
     * Get meta attributes as collection.
434
     *
435
     * @return \Sofa\Eloquence\Metable\AttributeBag
436
     */
437
    public function getMetaAttributes()
438
    {
439
        $this->loadMetaAttributes();
440
441
        return $this->getRelation('metaAttributes');
0 ignored issues
show
Bug introduced by
It seems like getRelation() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
442
    }
443
444
    /**
445
     * Accessor for metaAttributes property
446
     *
447
     * @return \Sofa\Eloquence\Metable\AttributeBag
448
     */
449
    public function getMetaAttributesAttribute()
450
    {
451
        return $this->getMetaAttributes();
452
    }
453
454
    /**
455
     * Get meta attributes as associative array.
456
     *
457
     * @return array
458
     */
459
    public function getMetaAttributesArray()
460
    {
461
        return $this->getMetaAttributes()->toArray();
462
    }
463
464
    /**
465
     * Load meta attributes relation.
466
     *
467
     * @return void
468
     */
469
    protected function loadMetaAttributes()
470
    {
471
        if (!array_key_exists('metaAttributes', $this->relations)) {
0 ignored issues
show
Bug introduced by
The property relations does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
472
            $this->reloadMetaAttributes();
473
        }
474
475
        $attributes = $this->getRelation('metaAttributes');
0 ignored issues
show
Bug introduced by
It seems like getRelation() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
476
477
        if (!$attributes instanceof AttributeBag) {
478
            $this->setRelation('metaAttributes', (new Attribute)->newBag($attributes->all()));
0 ignored issues
show
Bug introduced by
It seems like setRelation() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
479
        }
480
    }
481
482
    /**
483
     * Reload meta attributes from db or set empty bag for newly created model.
484
     *
485
     * @return $this
486
     */
487
    protected function reloadMetaAttributes()
488
    {
489
        return ($this->exists)
0 ignored issues
show
Bug introduced by
The property exists does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
490
            ? $this->load('metaAttributes')
0 ignored issues
show
Bug introduced by
It seems like load() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
491
            : $this->setRelation('metaAttributes', (new Attribute)->newBag());
0 ignored issues
show
Bug introduced by
It seems like setRelation() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
492
    }
493
494
    /**
495
     * Get allowed meta attributes array.
496
     *
497
     * @return array
498
     */
499
    public function getAllowedMeta()
500
    {
501
        return (property_exists($this, 'allowedMeta')) ? $this->allowedMeta : [];
502
    }
503
}
504