Metable::getMetaWhereConstraint()   A
last analyzed

Complexity

Conditions 4
Paths 2

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 2
nop 2
dl 0
loc 18
rs 9.2
c 0
b 0
f 0
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) && $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', 'pluck']))
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
    protected function listsMeta(Builder $query, ArgumentBag $args, $alias)
148
    {
149
        return $this->pluckMeta($query, $args, $alias);
150
    }
151
152
    /**
153
     * Get an array with the values of given meta attribute.
154
     *
155
     * @param  \Sofa\Eloquence\Builder $query
156
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
157
     * @param  string $alias
158
     * @return array
159
     */
160
    protected function pluckMeta(Builder $query, ArgumentBag $args, $alias)
161
    {
162
        list($column, $key) = [$args->get('column'), $args->get('key')];
163
164
        $query->select("{$alias}.meta_value as {$column}");
165
166
        if (!is_null($key)) {
167
            $this->metaSelectListsKey($query, $key);
168
        }
169
170
        return $query->callParent('pluck', $args->all());
171
    }
172
173
    /**
174
     * Add select clause for key of the list array.
175
     *
176
     * @param  \Sofa\Eloquence\Builder $query
177
     * @param  string $key
178
     * @return \Sofa\Eloquence\Builder
179
     */
180
    protected function metaSelectListsKey(Builder $query, $key)
181
    {
182
        if (strpos($key, '.') !== false) {
183
            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...
184
        } 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...
185
            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...
186
        }
187
188
        $alias = $this->joinMeta($query, $key);
189
190
        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...
191
    }
192
193
    /**
194
     * Get single value result from the meta attribute.
195
     *
196
     * @param  \Sofa\Eloquence\Builder $query
197
     * @param  string $method
198
     * @param  string $alias
199
     * @return mixed
200
     */
201
    protected function metaSingleResult(Builder $query, $method, $alias)
202
    {
203
        return $query->getQuery()->select("{$alias}.meta_value")->{$method}("{$alias}.meta_value");
204
    }
205
206
207
    /**
208
     * Join meta attributes table.
209
     *
210
     * @param  \Sofa\Eloquence\Builder $query
211
     * @param  string $column
212
     * @return string
213
     */
214
    protected function joinMeta(Builder $query, $column)
215
    {
216
        $query->prefixColumnsForJoin();
217
218
        $alias = $this->generateMetaAlias();
219
220
        $table = (new Attribute)->getTable();
221
222
        $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...
223
            $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...
224
                ->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...
225
                ->where("{$alias}.meta_key", '=', $column);
226
        });
227
228
        return $alias;
229
    }
230
231
    /**
232
     * Generate unique alias for meta attributes table.
233
     *
234
     * @return string
235
     */
236
    protected function generateMetaAlias()
237
    {
238
        return md5(microtime(true)) . '_meta_alias';
239
    }
240
241
    /**
242
     * Add whereHas subquery on the meta attributes relation.
243
     *
244
     * @param  \Sofa\Eloquence\Builder $query
245
     * @param  string $method
246
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
247
     * @return \Sofa\Eloquence\Builder
248
     */
249
    protected function metaHasQuery(Builder $query, $method, ArgumentBag $args)
250
    {
251
        $boolean = $this->getMetaBoolean($args);
252
253
        $operator = $this->getMetaOperator($method, $args);
254
255
        if (in_array($method, ['whereBetween', 'where'])) {
256
            $this->unbindNumerics($args);
257
        }
258
259
        return $query
260
            ->has('metaAttributes', $operator, 1, $boolean, $this->getMetaWhereConstraint($method, $args))
261
            ->with('metaAttributes');
262
    }
263
264
    /**
265
     * Get boolean called on the original method and set it to default.
266
     *
267
     * @param  \Sofa\EloquenceArgumentBag $args
268
     * @return string
269
     */
270
    protected function getMetaBoolean(ArgumentBag $args)
271
    {
272
        $boolean = $args->get('boolean');
273
274
        $args->set('boolean', 'and');
275
276
        return $boolean;
277
    }
278
279
    /**
280
     * Determine the operator for count relation query.
281
     *
282
     * @param  string $method
283
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
284
     * @return string
285
     */
286
    protected function getMetaOperator($method, ArgumentBag $args)
287
    {
288
        if ($not = $args->get('not')) {
289
            $args->set('not', false);
290
        }
291
292
        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...
293
    }
294
295
    /**
296
     * Integers and floats must be passed in raw form in order to avoid string
297
     * comparison, due to the fact that all meta values are stored as strings.
298
     *
299
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
300
     * @return void
301
     */
302
    protected function unbindNumerics(ArgumentBag $args)
303
    {
304
        if (($value = $args->get('value')) && (is_int($value) || is_float($value))) {
305
            $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...
306
        } elseif ($values = $args->get('values')) {
307
            foreach ($values as $key => $value) {
308
                if (is_int($value) || is_float($value)) {
309
                    $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...
310
                }
311
            }
312
313
            $args->set('values', $values);
314
        }
315
    }
316
317
    /**
318
     * Get the relation constraint closure.
319
     *
320
     * @param  string $method
321
     * @param  \Sofa\Hookable\Contracts\ArgumentBag $args
322
     * @return \Closure
323
     */
324
    protected function getMetaWhereConstraint($method, ArgumentBag $args)
325
    {
326
        $column = $args->get('column');
327
328
        $args->set('column', 'meta_value');
329
330
        if ($method === 'whereBetween') {
331
            return $this->getMetaBetweenConstraint($column, $args->get('values'));
332
        }
333
334
        return function ($query) use ($column, $method, $args) {
335
            $query->where('meta_key', $column);
336
337
            if ($args->get('value') || $args->get('values')) {
338
                call_user_func_array([$query, $method], $args->all());
339
            }
340
        };
341
    }
342
343
    /**
344
     * Query Builder whereBetween override required to pass raw numeric values.
345
     *
346
     * @param  string $column
347
     * @param  array  $values
348
     * @return \Closure
349
     */
350
    protected function getMetaBetweenConstraint($column, array $values)
351
    {
352
        $min = $values[0];
353
        $max = $values[1];
354
355
        return function ($query) use ($column, $min, $max) {
356
            $query->where('meta_key', $column)
357
                ->where('meta_value', '>=', $min)
358
                ->where('meta_value', '<=', $max);
359
        };
360
    }
361
362
    /**
363
     * Save new or updated meta attributes and delete the ones that were unset.
364
     *
365
     * @return void
366
     */
367
    protected function saveMeta()
368
    {
369
        foreach ($this->getMetaAttributes() as $attribute) {
370
            if (is_null($attribute->getValue())) {
371
                $attribute->delete();
372
            } else {
373
                $this->metaAttributes()->save($attribute);
374
            }
375
        }
376
    }
377
378
    /**
379
     * Determine whether meta attribute is allowed for the model.
380
     *
381
     * @param  string $key
382
     * @return boolean
383
     */
384
    public function allowsMeta($key)
385
    {
386
        $allowed = $this->getAllowedMeta();
387
388
        return empty($allowed) || in_array($key, $allowed);
389
    }
390
391
    /**
392
     * Determine whether meta attribute exists on the model.
393
     *
394
     * @param  string $key
395
     * @return boolean
396
     */
397
    public function hasMeta($key)
398
    {
399
        return array_key_exists($key, $this->getMetaAttributesArray());
400
    }
401
402
    /**
403
     * Get meta attribute value.
404
     *
405
     * @param  string $key
406
     * @return mixed
407
     */
408
    public function getMeta($key)
409
    {
410
        return $this->getMetaAttributes()->getValue($key);
411
    }
412
413
    /**
414
     * Set meta attribute.
415
     *
416
     * @param  string $key
417
     * @param  mixed  $value
418
     * @return void
419
     */
420
    public function setMeta($key, $value)
421
    {
422
        $this->getMetaAttributes()->set($key, $value);
423
    }
424
425
    /**
426
     * Meta attributes relation.
427
     *
428
     * @codeCoverageIgnore
429
     *
430
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
431
     */
432
    public function metaAttributes()
433
    {
434
        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...
435
    }
436
437
    /**
438
     * Get meta attributes as collection.
439
     *
440
     * @return \Sofa\Eloquence\Metable\AttributeBag
441
     */
442
    public function getMetaAttributes()
443
    {
444
        $this->loadMetaAttributes();
445
446
        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...
447
    }
448
449
    /**
450
     * Accessor for metaAttributes property
451
     *
452
     * @return \Sofa\Eloquence\Metable\AttributeBag
453
     */
454
    public function getMetaAttributesAttribute()
455
    {
456
        return $this->getMetaAttributes();
457
    }
458
459
    /**
460
     * Get meta attributes as associative array.
461
     *
462
     * @return array
463
     */
464
    public function getMetaAttributesArray()
465
    {
466
        return $this->getMetaAttributes()->toArray();
467
    }
468
469
    /**
470
     * Load meta attributes relation.
471
     *
472
     * @return void
473
     */
474
    protected function loadMetaAttributes()
475
    {
476
        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...
477
            $this->reloadMetaAttributes();
478
        }
479
480
        $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...
481
482
        if (!$attributes instanceof AttributeBag) {
483
            $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...
484
        }
485
    }
486
487
    /**
488
     * Reload meta attributes from db or set empty bag for newly created model.
489
     *
490
     * @return $this
491
     */
492
    protected function reloadMetaAttributes()
493
    {
494
        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...
495
            ? $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...
496
            : $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...
497
    }
498
499
    /**
500
     * Get allowed meta attributes array.
501
     *
502
     * @return array
503
     */
504
    public function getAllowedMeta()
505
    {
506
        return (property_exists($this, 'allowedMeta')) ? $this->allowedMeta : [];
507
    }
508
}
509