Completed
Pull Request — develop (#132)
by
unknown
01:13
created

Attributable::isEntityAttributeRelation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Rinvex\Attributes\Traits;
6
7
use Schema;
8
use Closure;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Str;
11
use SuperClosure\Serializer;
12
use Rinvex\Attributes\Models\Value;
13
use Rinvex\Attributes\Models\Attribute;
14
use Illuminate\Database\Eloquent\Builder;
15
use Illuminate\Database\Eloquent\Collection;
16
use Rinvex\Attributes\Events\EntityWasSaved;
17
use Rinvex\Attributes\Scopes\EagerLoadScope;
18
use Rinvex\Attributes\Events\EntityWasDeleted;
19
use Rinvex\Attributes\Support\RelationBuilder;
20
use Rinvex\Attributes\Support\ValueCollection;
21
use Illuminate\Support\Collection as BaseCollection;
22
23
trait Attributable
24
{
25
    /**
26
     * The entity attributes.
27
     *
28
     * @var \Illuminate\Database\Eloquent\Collection
29
     */
30
    protected static $entityAttributes;
31
32
    /**
33
     * The entity attribute value trash.
34
     *
35
     * @var \Illuminate\Support\Collection
36
     */
37
    protected $entityAttributeValueTrash;
38
39
    /**
40
     * The entity attribute relations.
41
     *
42
     * @var array
43
     */
44
    protected $entityAttributeRelations = [];
45
46
    /**
47
     * Determine if the entity attribute relations have been booted.
48
     *
49
     * @var bool
50
     */
51
    protected $entityAttributeRelationsBooted = false;
52
53
    /**
54
     * Boot the attributable trait for a model.
55
     *
56
     * @return void
57
     */
58
    public static function bootAttributable()
59
    {
60
        static::addGlobalScope(new EagerLoadScope());
61
        static::saved(EntityWasSaved::class.'@handle');
62
        static::deleted(EntityWasDeleted::class.'@handle');
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    protected function bootIfNotBooted()
69
    {
70
        parent::bootIfNotBooted();
71
72
        if (! $this->entityAttributeRelationsBooted) {
73
            app(RelationBuilder::class)->build($this);
74
75
            $this->entityAttributeRelationsBooted = true;
76
        }
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function relationsToArray()
83
    {
84
        $eavAttributes = [];
85
        $attributes = parent::relationsToArray();
86
        $relations = array_keys($this->getEntityAttributeRelations());
87
88
        foreach ($relations as $relation) {
89
            if (array_key_exists($relation, $attributes)) {
90
                $eavAttributes[$relation] = $this->getAttribute($relation) instanceof BaseCollection
91
                    ? $this->getAttribute($relation)->toArray() : $this->getAttribute($relation);
92
93
                // By unsetting the relation from the attributes array we will make
94
                // sure we do not provide a duplicity when adding the namespace.
95
                // Otherwise it would keep the relation as a key in the root.
96
                unset($attributes[$relation]);
97
            }
98
        }
99
100
        if (is_null($namespace = $this->getEntityAttributesNamespace())) {
101
            $attributes = array_merge($attributes, $eavAttributes);
102
        } else {
103
            Arr::set($attributes, $namespace, $eavAttributes);
104
        }
105
106
        return $attributes;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function setRelation($key, $value)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
113
    {
114
        if ($value instanceof ValueCollection) {
115
            $value->link($this, $this->getEntityAttributes()->get($key));
116
        }
117
118
        return parent::setRelation($key, $value);
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function getRelationValue($key)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
125
    {
126
        $value = parent::getRelationValue($key);
127
128
        // In case any relation value is found, we will just provide it as is.
129
        // Otherwise, we will check if exists any attribute relation for the
130
        // given key. If so, we will load the relation calling its method.
131
        if (is_null($value) && ! $this->relationLoaded($key) && $this->isEntityAttributeRelation($key)) {
0 ignored issues
show
Documentation Bug introduced by
The method relationLoaded does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
132
            $value = $this->getRelationshipFromMethod($key);
0 ignored issues
show
Documentation Bug introduced by
The method getRelationshipFromMethod does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
133
        }
134
135
        if ($value instanceof ValueCollection) {
136
            $value->link($this, $this->getEntityAttributes()->get($key));
137
        }
138
139
        return $value;
140
    }
141
142
    /**
143
     * Get the entity attributes namespace if exists.
144
     *
145
     * @return string|null
146
     */
147
    public function getEntityAttributesNamespace(): ?string
148
    {
149
        return property_exists($this, 'entityAttributesNamespace') ? $this->entityAttributesNamespace : null;
0 ignored issues
show
Bug introduced by
The property entityAttributesNamespace does not seem to exist. Did you mean entityAttributes?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
150
    }
151
152
    /**
153
     * Get the entity attributes.
154
     *
155
     * @return \Illuminate\Database\Eloquent\Collection
156
     */
157
    public function getEntityAttributes(): Collection
158
    {
159
        $morphClass = $this->getMorphClass();
0 ignored issues
show
Documentation Bug introduced by
The method getMorphClass does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
160
        static::$entityAttributes = static::$entityAttributes ?? collect();
161
162
        if (! static::$entityAttributes->has($morphClass) && Schema::hasTable(config('rinvex.attributes.tables.attribute_entity'))) {
163
            $locale = app()->getLocale();
164
165
            /* This is a trial to implement per resource attributes,
166
               it's working but I don't like current implementation.
167
            $routeParam = request()->route($morphClass);
168
169
            // @TODO: This is REALLY REALLY BAD DESIGN!! But can't figure out a better way for now!!
170
            // Refactor required, we need to catch `$this` itself, we should NOT use request and routes here!!
171
            // But still at this very early stage, `$this` still not bound to model's data, so it's just empty object!
172
            $entityId = $routeParam && collect(class_uses_recursive(static::class))->contains(HashidsTrait::class) && ! is_numeric($routeParam)
173
                ? optional(Hashids::decode($routeParam))[0] : $routeParam;
174
175
            $attributes = app('rinvex.attributes.attribute_entity')->where('entity_type', $morphClass)->where('entity_id', $entityId)->get()->pluck('attribute_id');
176
             */
177
178
            $attributes = app('rinvex.attributes.attribute_entity')->where('entity_type', $morphClass)->get()->pluck('attribute_id');
179
            static::$entityAttributes->put($morphClass, app('rinvex.attributes.attribute')->whereIn('id', $attributes)->orderBy('sort_order', 'ASC')->orderBy("name->\${$locale}", 'ASC')->get()->keyBy('slug'));
180
        }
181
182
        return static::$entityAttributes->get($morphClass) ?? new Collection();
183
    }
184
185
    /**
186
     * Clear the static attributes cache for this model.
187
     *
188
     * @return void
189
     */
190
    public function clearAttributableCache()
191
    {
192
        $morphClass = $this->getMorphClass();
0 ignored issues
show
Documentation Bug introduced by
The method getMorphClass does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
193
        if (static::$entityAttributes && static::$entityAttributes->has($morphClass)) {
194
            static::$entityAttributes->forget($morphClass);
195
        }
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201
    protected function fillableFromArray(array $attributes)
202
    {
203
        foreach (array_diff_key($attributes, array_flip($this->getFillable())) as $key => $value) {
0 ignored issues
show
Documentation Bug introduced by
The method getFillable does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
204
            if ($this->isEntityAttribute($key)) {
205
                $this->setEntityAttribute($key, $value);
206
            }
207
        }
208
209
        if (count($this->getFillable()) > 0 && ! static::$unguarded) {
0 ignored issues
show
Documentation Bug introduced by
The method getFillable does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
210
            return array_intersect_key($attributes, array_flip($this->getFillable()));
0 ignored issues
show
Documentation Bug introduced by
The method getFillable does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
211
        }
212
213
        return $attributes;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function setAttribute($key, $value)
220
    {
221
        return $this->isEntityAttribute($key) ? $this->setEntityAttribute($key, $value) : parent::setAttribute($key, $value);
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227
    public function getAttribute($key)
228
    {
229
        return $this->isEntityAttribute($key) ? $this->getEntityAttribute($key) : parent::getAttribute($key);
230
    }
231
232
    /**
233
     * Set the entity attribute relation.
234
     *
235
     * @param string $relation
236
     * @param mixed  $value
237
     *
238
     * @return $this
239
     */
240
    public function setEntityAttributeRelation(string $relation, $value)
241
    {
242
        $this->entityAttributeRelations[$relation] = $value;
243
244
        return $this;
245
    }
246
247
    /**
248
     * Check if the given key is an entity attribute relation.
249
     *
250
     * @param string $key
251
     *
252
     * @return bool
253
     */
254
    public function isEntityAttributeRelation(string $key): bool
255
    {
256
        return isset($this->entityAttributeRelations[$key]);
257
    }
258
259
    /**
260
     * Get the entity attribute value trash.
261
     *
262
     * @return \Illuminate\Support\Collection
263
     */
264
    public function getEntityAttributeValueTrash(): BaseCollection
265
    {
266
        return $this->entityAttributeValueTrash ?: $this->entityAttributeValueTrash = collect([]);
267
    }
268
269
    /**
270
     * Get the entity attribute relations.
271
     *
272
     * @return array
273
     */
274
    public function getEntityAttributeRelations(): array
275
    {
276
        return $this->entityAttributeRelations;
277
    }
278
279
    /**
280
     * Check if the given key is an entity attribute.
281
     *
282
     * @param string $key
283
     *
284
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
285
     */
286
    public function isEntityAttribute(string $key)
287
    {
288
        $key = $this->getEntityAttributeName($key);
289
290
        return $this->getEntityAttributes()->has($key);
291
    }
292
293
    /**
294
     * Get the entity attribute.
295
     *
296
     * @param string $key
297
     *
298
     * @return mixed
299
     */
300
    public function getEntityAttribute(string $key)
301
    {
302
        if ($this->isRawEntityAttribute($key)) {
303
            return $this->getEntityAttributeRelation($key);
304
        }
305
306
        return $this->getEntityAttributeValue($key);
307
    }
308
309
    /**
310
     * Get the entity attribute value.
311
     *
312
     * @param string $key
313
     *
314
     * @return mixed
315
     */
316
    protected function getEntityAttributeValue(string $key)
317
    {
318
        $value = $this->getEntityAttributeRelation($key);
319
320
        // In case we are accessing to a multivalued attribute, we will return
321
        // a collection with pairs of id and value content. Otherwise we'll
322
        // just return the single model value content as a plain result.
323
        if ($this->getEntityAttributes()->get($key)->is_collection) {
324
            return $value->pluck('content');
325
        }
326
327
        return ! is_null($value) ? $value->getAttribute('content') : null;
328
    }
329
330
    /**
331
     * Get the entity attribute relationship.
332
     *
333
     * @param string $key
334
     *
335
     * @return mixed
336
     */
337
    protected function getEntityAttributeRelation(string $key)
338
    {
339
        $key = $this->getEntityAttributeName($key);
340
341
        if ($this->relationLoaded($key)) {
0 ignored issues
show
Documentation Bug introduced by
The method relationLoaded does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
342
            return $this->getRelation($key);
0 ignored issues
show
Bug introduced by
The method getRelation() does not exist on Rinvex\Attributes\Traits\Attributable. Did you maybe mean getRelationValue()?

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...
343
        }
344
345
        return $this->getRelationValue($key);
346
    }
347
348
    /**
349
     * Set the entity attribute.
350
     *
351
     * @param string $key
352
     * @param mixed  $value
353
     *
354
     * @return mixed
355
     */
356
    public function setEntityAttribute(string $key, $value)
357
    {
358
        $current = $this->getEntityAttributeRelation($key);
359
        $attribute = $this->getEntityAttributes()->get($key);
360
361
        // $current will always contain a collection when an attribute is multivalued
362
        // as morphMany provides collections even if no values were matched, making
363
        // us assume at least an empty collection object will be always provided.
364
        if ($attribute->is_collection) {
365
            if (is_null($current)) {
366
                $this->setRelation($key, $current = new ValueCollection());
367
            }
368
369
            $current->replace($value);
370
371
            return $this;
372
        }
373
374
        // If the attribute to set is a collection, it will be replaced by the
375
        // new value. If the value model does not exist, we will just create
376
        // and set a new value model, otherwise its value will get updated.
377
        if (is_null($current)) {
378
            return $this->setEntityAttributeValue($attribute, $value);
379
        }
380
381
        if ($value instanceof Value) {
382
            $value = $value->getAttribute('content');
383
        }
384
385
        $current->setAttribute('entity_type', $this->getMorphClass());
0 ignored issues
show
Documentation Bug introduced by
The method getMorphClass does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
386
387
        return $current->setAttribute('content', $value);
388
    }
389
390
    /**
391
     * Set the entity attribute value.
392
     *
393
     * @param \Rinvex\Attributes\Models\Attribute $attribute
394
     * @param mixed                               $value
395
     *
396
     * @return $this
397
     */
398
    protected function setEntityAttributeValue(Attribute $attribute, $value)
399
    {
400
        if (! is_null($value) && ! $value instanceof Value) {
401
            $model = Attribute::getTypeModel($attribute->getAttribute('type'));
402
            $instance = new $model();
403
404
            $instance->setAttribute('entity_id', $this->getKey());
0 ignored issues
show
Documentation Bug introduced by
The method getKey does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
405
            $instance->setAttribute('entity_type', $this->getMorphClass());
0 ignored issues
show
Documentation Bug introduced by
The method getMorphClass does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
406
            $instance->setAttribute($attribute->getForeignKey(), $attribute->getKey());
407
            $instance->setAttribute('content', $value);
408
409
            $value = $instance;
410
        }
411
412
        return $this->setRelation($attribute->getAttribute('slug'), $value);
413
    }
414
415
    /**
416
     * Determine if the given key is a raw entity attribute.
417
     *
418
     * @param string $key
419
     *
420
     * @return bool
421
     */
422
    protected function isRawEntityAttribute(string $key): bool
423
    {
424
        return (bool) preg_match('/^raw(\w+)object$/i', $key);
425
    }
426
427
    /**
428
     * Get entity attribute bare name.
429
     *
430
     * @param string $key
431
     *
432
     * @return string
433
     */
434
    protected function getEntityAttributeName(string $key): string
435
    {
436
        return $this->isRawEntityAttribute($key) ? Str::camel(str_ireplace(['raw', 'object'], ['', ''], $key)) : $key;
437
    }
438
439
    /**
440
     * Get the attributes attached to this entity.
441
     *
442
     * @return \Illuminate\Database\Eloquent\Collection|null
443
     */
444
    public function attributes(): ?Collection
445
    {
446
        return $this->getEntityAttributes();
447
    }
448
449
    /**
450
     * Scope query with the given entity attribute.
451
     *
452
     * @param \Illuminate\Database\Eloquent\Builder $builder
453
     * @param string                                $key
454
     * @param mixed                                 $value
455
     *
456
     * @return \Illuminate\Database\Eloquent\Builder
457
     */
458
    public function scopeHasAttribute(Builder $builder, string $key, $value): Builder
459
    {
460
        return $builder->whereHas($key, function (Builder $builder) use ($value) {
461
            $builder->where('content', $value)->where('entity_type', $this->getMorphClass());
0 ignored issues
show
Documentation Bug introduced by
The method getMorphClass does not exist on object<Rinvex\Attributes\Traits\Attributable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
462
        });
463
    }
464
465
    /**
466
     * Dynamically pipe calls to attribute relations.
467
     *
468
     * @param string $method
469
     * @param array  $parameters
470
     *
471
     * @return mixed
472
     */
473
    public function __call($method, $parameters)
474
    {
475
        if ($this->isEntityAttributeRelation($method)) {
476
            $relation = $this->entityAttributeRelations[$method] instanceof Closure
477
                ? $this->entityAttributeRelations[$method]
478
                : (new Serializer())->unserialize($this->entityAttributeRelations[$method]);
479
480
            return call_user_func_array($relation, $parameters);
481
        }
482
483
        return parent::__call($method, $parameters);
484
    }
485
486
    /**
487
     * Prepare the instance for serialization.
488
     *
489
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<integer|string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
490
     */
491
    public function __sleep()
492
    {
493
        if ($this->entityAttributeRelations && current($this->entityAttributeRelations) instanceof Closure) {
494
            $relations = $this->entityAttributeRelations;
495
            $this->entityAttributeRelations = [];
496
497
            foreach ($relations as $key => $value) {
498
                if ($value instanceof Closure) {
499
                    $this->setEntityAttributeRelation($key, (new Serializer())->serialize($value));
500
                }
501
            }
502
        }
503
504
        return array_keys(get_object_vars($this));
505
    }
506
507
    /**
508
     * Restore the model after serialization.
509
     *
510
     * @return void
511
     */
512
    public function __wakeup()
513
    {
514
        parent::__wakeup();
515
516
        if ($this->entityAttributeRelations && is_string(current($this->entityAttributeRelations))) {
517
            $relations = $this->entityAttributeRelations;
518
            $this->entityAttributeRelations = [];
519
520
            foreach ($relations as $key => $value) {
521
                if (is_string($value)) {
522
                    $this->setEntityAttributeRelation($key, (new Serializer())->unserialize($value));
523
                }
524
            }
525
        }
526
    }
527
}
528