Completed
Push — master ( bd7a3a...ee577f )
by Ryan
08:36
created

EloquentModel::toRoutable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php namespace Anomaly\Streams\Platform\Model;
2
3
use Anomaly\Streams\Platform\Collection\CacheCollection;
4
use Anomaly\Streams\Platform\Traits\Hookable;
5
use Illuminate\Contracts\Support\Arrayable;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Eloquent\Collection;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Foundation\Bus\DispatchesJobs;
10
11
/**
12
 * Class EloquentModel
13
 *
14
 * @link    http://anomaly.is/streams-platform
15
 * @author  AnomalyLabs, Inc. <[email protected]>
16
 * @author  Ryan Thompson <[email protected]>
17
 * @package Anomaly\Streams\Platform\Model
18
 */
19
class EloquentModel extends Model implements Arrayable
20
{
21
22
    use DispatchesJobs;
23
    use Hookable;
24
25
    /**
26
     * Disable timestamps for this model.
27
     *
28
     * @var bool
29
     */
30
    public $timestamps = false;
31
32
    /**
33
     * Translatable attributes.
34
     *
35
     * @var array
36
     */
37
    protected $translatedAttributes = [];
38
39
    /**
40
     * The number of minutes to cache query results.
41
     *
42
     * @var null|false|int
43
     */
44
    protected $ttl = false;
45
46
    /**
47
     * The attributes that are
48
     * not mass assignable. Let upper
49
     * models handle this themselves.
50
     *
51
     * @var array
52
     */
53
    protected $guarded = [];
54
55
    /**
56
     * The title key.
57
     *
58
     * @var string
59
     */
60
    protected $titleKey = 'id';
61
62
    /**
63
     * Observable model events.
64
     *
65
     * @var array
66
     */
67
    protected $observables = [
68
        'updatingMultiple',
69
        'updatedMultiple',
70
        'deletingMultiple',
71
        'deletedMultiple'
72
    ];
73
74
    /**
75
     * Runtime cache.
76
     *
77
     * @var array
78
     */
79
    protected $cache = [];
80
81
    /**
82
     * Get the ID.
83
     *
84
     * @return integer
85
     */
86
    public function getId()
87
    {
88
        return $this->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
89
    }
90
91
    /**
92
     * Return the object's ETag fingerprint.
93
     *
94
     * @return string
95
     */
96
    public function etag()
97
    {
98
        return md5(get_class($this) . json_encode($this->toArray()));
99
    }
100
101
    /**
102
     * Alias for $this->setTtl($ttl)
103
     *
104
     * @param $ttl
105
     * @return EloquentModel
106
     */
107
    public function cache($ttl)
108
    {
109
        return $this->setTtl($ttl);
110
    }
111
112
    /**
113
     * Fire a model event.
114
     *
115
     * @param $event
116
     * @return mixed
117
     */
118
    public function fireEvent($event)
119
    {
120
        return $this->fireModelEvent($event);
121
    }
122
123
    /**
124
     * Return a new collection class with our models.
125
     *
126
     * @param  array $items
127
     * @return Collection
128
     */
129 View Code Duplication
    public function newCollection(array $items = array())
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...
130
    {
131
        $collection = substr(get_class($this), 0, -5) . 'Collection';
132
133
        if (class_exists($collection)) {
134
            return new $collection($items);
135
        }
136
137
        return new EloquentCollection($items);
138
    }
139
140
    /**
141
     * Return the translatable flag.
142
     *
143
     * @return bool
144
     */
145
    public function isTranslatable()
146
    {
147
        return isset($this->translationModel);
148
    }
149
150
    /**
151
     * Set the translatable flag.
152
     *
153
     * @param $translatable
154
     * @return $this
155
     */
156
    public function setTranslatable($translatable)
157
    {
158
        $this->translatable = $translatable;
0 ignored issues
show
Documentation introduced by
The property translatable does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
159
160
        return $this;
161
    }
162
163
    /**
164
     * Set the ttl.
165
     *
166
     * @param  $ttl
167
     * @return $this
168
     */
169
    public function setTtl($ttl)
170
    {
171
        $this->ttl = $ttl;
172
173
        return $this;
174
    }
175
176
    /**
177
     * Get the ttl.
178
     *
179
     * @return int|mixed
180
     */
181
    public function getTtl()
182
    {
183
        return $this->ttl;
184
    }
185
186
    /**
187
     * Get cache collection key.
188
     *
189
     * @return string
190
     */
191
    public function getCacheCollectionKey()
192
    {
193
        return get_called_class();
194
    }
195
196
    /**
197
     * Get the model title.
198
     *
199
     * @return mixed
200
     */
201
    public function getTitle()
202
    {
203
        return $this->{$this->getTitleName()};
204
    }
205
206
    /**
207
     * Get the title key.
208
     *
209
     * @return string
210
     */
211
    public function getTitleName()
212
    {
213
        return $this->titleName ?: 'id';
0 ignored issues
show
Documentation introduced by
The property titleName does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
214
    }
215
216
    /**
217
     * Return if a row is deletable or not.
218
     *
219
     * @return bool
220
     */
221
    public function isDeletable()
222
    {
223
        return true;
224
    }
225
226
    /**
227
     * Return if the model is restorable or not.
228
     *
229
     * @return bool
230
     */
231
    public function isRestorable()
232
    {
233
        return true;
234
    }
235
236
    /**
237
     * Return whether the model is being
238
     * force deleted or not.
239
     *
240
     * @return bool
241
     */
242
    public function isForceDeleting()
243
    {
244
        return isset($this->forceDeleting) && $this->forceDeleting == true;
0 ignored issues
show
Documentation introduced by
The property forceDeleting does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
245
    }
246
247
    /**
248
     * Flush the model's cache.
249
     *
250
     * @return $this
251
     */
252
    public function flushCache()
253
    {
254
        (new CacheCollection())->setKey($this->getCacheCollectionKey())->flush();
255
256
        return $this;
257
    }
258
259
    /**
260
     * Get a new query builder for the model's table.
261
     *
262
     * @return \Illuminate\Database\Eloquent\Builder
263
     */
264 View Code Duplication
    public function newQuery()
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...
265
    {
266
        $builder = new EloquentQueryBuilder($this->newBaseQueryBuilder());
267
268
        // Once we have the query builders, we will set the model instances so the
269
        // builder can easily access any information it may need from the model
270
        // while it is constructing and executing various queries against it.
271
        $builder->setModel($this)->with($this->with);
272
273
        return $this->applyGlobalScopes($builder);
274
    }
275
276
    /*
277
     * Alias for getTranslation()
278
     */
279
    public function translate($locale = null, $withFallback = false)
280
    {
281
        return $this->getTranslation($locale, $withFallback);
282
    }
283
284
    /*
285
     * Alias for getTranslation()
286
     */
287
    public function translateOrDefault($locale)
288
    {
289
        return $this->getTranslation($locale, true) ?: $this;
290
    }
291
292
    /*
293
     * Alias for getTranslationOrNew()
294
     */
295
    public function translateOrNew($locale)
296
    {
297
        return $this->getTranslationOrNew($locale);
298
    }
299
300
    /**
301
     * Get related translations.
302
     *
303
     * @return EloquentCollection
304
     */
305
    public function getTranslations()
306
    {
307
        foreach ($translations = $this->translations()->get() as $translation) {
308
            $translation->setRelation('parent', $this);
309
        }
310
311
        return $translations;
312
    }
313
314
    /**
315
     * @param null      $locale
316
     * @param bool|null $withFallback
317
     * @return Model|null
318
     */
319
    public function getTranslation($locale = null, $withFallback = false)
320
    {
321
        $locale = $locale ?: $this->getFallbackLocale();
322
323
        if ($translation = $this->getTranslationByLocaleKey($locale)) {
324
            return $translation;
325
        } elseif ($withFallback
326
            && $this->getFallbackLocale()
327
            && $this->getTranslationByLocaleKey($this->getFallbackLocale())
328
        ) {
329
            return $this->getTranslationByLocaleKey($this->getFallbackLocale());
330
        }
331
332
        return null;
333
    }
334
335
    public function hasTranslation($locale = null)
336
    {
337
        $locale = $locale ?: $this->getFallbackLocale();
338
339 View Code Duplication
        foreach ($this->translations as $translation) {
0 ignored issues
show
Documentation introduced by
The property translations does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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...
340
341
            $translation->setRelation('parent', $this);
342
343
            if ($translation->getAttribute($this->getLocaleKey()) == $locale) {
344
                return true;
345
            }
346
        }
347
348
        return false;
349
    }
350
351
    /**
352
     * Get the translation model.
353
     *
354
     * @return EloquentModel
355
     */
356
    public function getTranslationModel()
357
    {
358
        return new $this->translationModel;
0 ignored issues
show
Documentation introduced by
The property translationModel does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
359
    }
360
361
    /**
362
     * Get the translation model name.
363
     *
364
     * @return string
365
     */
366
    public function getTranslationModelName()
367
    {
368
        return $this->translationModel;
0 ignored issues
show
Documentation introduced by
The property translationModel does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
369
    }
370
371
    /**
372
     * Get the translation table name.
373
     *
374
     * @return string
375
     */
376
    public function getTranslationTableName()
377
    {
378
        $model = $this->getTranslationModel();
379
380
        return $model->getTableName();
381
    }
382
383
    public function getTranslationModelNameDefault()
384
    {
385
        return get_class($this) . 'Translation';
386
    }
387
388
    public function getRelationKey()
389
    {
390
        return $this->translationForeignKey ?: $this->getForeignKey();
0 ignored issues
show
Documentation introduced by
The property translationForeignKey does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
391
    }
392
393
    public function getLocaleKey()
394
    {
395
        return $this->localeKey ?: 'locale';
0 ignored issues
show
Documentation introduced by
The property localeKey does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
396
    }
397
398
    public function translations()
399
    {
400
        return $this->hasMany($this->getTranslationModelName(), $this->getRelationKey());
401
    }
402
403
    public function getAttribute($key)
404
    {
405
        if ($this->isTranslatedAttribute($key)) {
406
            if ($this->getTranslation() === null) {
407
                return null;
408
            }
409
410
            $translation = $this->getTranslation();
411
412
            $translation->setRelation('parent', $this);
413
414
            return $translation->$key;
415
        }
416
417
        return parent::getAttribute($key);
418
    }
419
420
    /**
421
     * Set an attribute.
422
     *
423
     * @param string $key
424
     * @param mixed  $value
425
     * @return $this
426
     */
427
    public function setAttribute($key, $value)
428
    {
429
        if (in_array($key, $this->translatedAttributes)) {
430
            $this->getTranslationOrNew(config('app.locale'))->$key = $value;
431
        } else {
432
            parent::setAttribute($key, $value);
433
        }
434
435
        return $this;
436
    }
437
438
    /**
439
     * Save the model.
440
     *
441
     * We have some customization here to
442
     * accommodate translations. First sa
443
     * then save translations is translatable.
444
     *
445
     * @param array $options
446
     * @return bool
447
     */
448
    public function save(array $options = array())
449
    {
450
        if (!$this->getTranslationModelName()) {
451
            return $this->saveModel($options);
452
        }
453
454
        if ($this->exists) {
455
456
            if (count($this->getDirty()) > 0) {
457
458
                // If $this->exists and dirty, $this->saveModel() has to return true. If not,
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
459
                // an error has occurred. Therefore we shouldn't save the translations.
460
                if ($this->saveModel($options)) {
461
                    return $this->saveTranslations();
462
                }
463
464
                return false;
465
            } else {
466
467
                // If $this->exists and not dirty, $this->saveModel() skips saving and returns
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
468
                // false. So we have to save the translations
469
                return $this->saveTranslations();
470
            }
471
        } elseif ($this->saveModel($options)) {
472
473
            // We save the translations only if the instance is saved in the database.
474
            return $this->saveTranslations();
475
        }
476
477
        return false;
478
    }
479
480
    /**
481
     * Save the model to the database.
482
     *
483
     * This is a direct port from Eloquent
484
     * with the only exception being that if
485
     * the model is translatable it will NOT
486
     * fire the saved event. The saveTranslations
487
     * method will do that instead.
488
     *
489
     * @param  array $options
490
     * @return bool
491
     */
492
    public function saveModel(array $options = array())
493
    {
494
        $query = $this->newQueryWithoutScopes();
495
496
        // If the "saving" event returns false we'll bail out of the save and return
497
        // false, indicating that the save failed. This provides a chance for any
498
        // listeners to cancel save operations if validations fail or whatever.
499
        if ($this->fireModelEvent('saving') === false) {
500
            return false;
501
        }
502
503
        // If the model already exists in the database we can just update our record
504
        // that is already in this database using the current IDs in this "where"
505
        // clause to only update this model. Otherwise, we'll just insert them.
506
        if ($this->exists) {
507
            $saved = $this->performUpdate($query, $options);
0 ignored issues
show
Bug introduced by
It seems like $query defined by $this->newQueryWithoutScopes() on line 494 can also be of type object<Illuminate\Database\Eloquent\Model>; however, Illuminate\Database\Eloq...\Model::performUpdate() does only seem to accept object<Illuminate\Database\Eloquent\Builder>, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
508
        }
509
510
        // If the model is brand new, we'll insert it into our database and set the
511
        // ID attribute on the model to the value of the newly inserted row's ID
512
        // which is typically an auto-increment value managed by the database.
513
        else {
514
            $saved = $this->performInsert($query, $options);
0 ignored issues
show
Bug introduced by
It seems like $query defined by $this->newQueryWithoutScopes() on line 494 can also be of type object<Illuminate\Database\Eloquent\Model>; however, Illuminate\Database\Eloq...\Model::performInsert() does only seem to accept object<Illuminate\Database\Eloquent\Builder>, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
515
        }
516
517
        if ($saved && !$this->isTranslatable()) {
518
            $this->finishSave($options);
519
        }
520
521
        return $saved;
522
    }
523
524
    /**
525
     * Save translations to the database.
526
     *
527
     * @return bool
528
     */
529
    protected function saveTranslations()
530
    {
531
        $saved = true;
532
533
        foreach ($this->translations as $translation) {
0 ignored issues
show
Documentation introduced by
The property translations does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
534
535
            $translation->setRelation('parent', $this);
536
537
            /* @var EloquentModel $translation */
538
            if ($saved && $this->isTranslationDirty($translation)) {
539
540
                $translation->setAttribute($this->getRelationKey(), $this->getKey());
541
542
                $saved = $translation->save();
543
            }
544
        }
545
546
        if ($this->translations->isEmpty()) {
0 ignored issues
show
Documentation introduced by
The property translations does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
547
548
            $translation = $this->translateOrNew(config('streams::locales.default'));
549
550
            $translation->save();
551
        }
552
553
        $this->finishSave([]);
554
555
        return $saved;
556
    }
557
558
    protected function getTranslationOrNew($locale)
559
    {
560
        if (($translation = $this->getTranslation($locale, false)) === null) {
561
            $translation = $this->getNewTranslation($locale);
562
        }
563
564
        return $translation;
565
    }
566
567
    public function fill(array $attributes)
568
    {
569
        foreach ($attributes as $key => $values) {
570
            if (is_array($values) && $this->isKeyALocale($key)) {
571
                foreach ($values as $translationAttribute => $translationValue) {
572
                    if ($this->alwaysFillable() || $this->isFillable($translationAttribute)) {
573
                        $this->getTranslationOrNew($key)->$translationAttribute = $translationValue;
574
                    }
575
                }
576
                unset($attributes[$key]);
577
            }
578
        }
579
580
        return parent::fill($attributes);
581
    }
582
583
    private function getTranslationByLocaleKey($key)
584
    {
585 View Code Duplication
        foreach ($this->translations as $translation) {
0 ignored issues
show
Documentation introduced by
The property translations does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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...
586
587
            $translation->setRelation('parent', $this);
588
589
            if ($translation->getAttribute($this->getLocaleKey()) == $key) {
590
                return $translation;
591
            }
592
        }
593
594
        return null;
595
    }
596
597
    public function isTranslatedAttribute($key)
598
    {
599
        return in_array($key, $this->translatedAttributes);
600
    }
601
602
    protected function isTranslationAttribute($key)
603
    {
604
        return in_array($key, $this->translatedAttributes);
605
    }
606
607
    protected function isKeyALocale($key)
608
    {
609
        return config('streams::locales.supported.' . $key) !== null;
610
    }
611
612
    protected function isTranslationDirty(Model $translation)
613
    {
614
        $dirtyAttributes = $translation->getDirty();
615
        unset($dirtyAttributes[$this->getLocaleKey()]);
616
617
        return count($dirtyAttributes) > 0;
618
    }
619
620
    public function getNewTranslation($locale)
621
    {
622
        $modelName = $this->getTranslationModelName();
623
624
        /* @var EloquentModel $translation */
625
        $translation = new $modelName;
626
627
        $translation->setRelation('parent', $this);
628
629
        $translation->setAttribute($this->getLocaleKey(), $locale);
630
        $translation->setAttribute($this->getRelationKey(), $this->getKey());
631
632
        $this->translations->add($translation);
0 ignored issues
show
Documentation introduced by
The property translations does not exist on object<Anomaly\Streams\P...rm\Model\EloquentModel>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
633
634
        return $translation;
635
    }
636
637
    public function scopeTranslatedIn(Builder $query, $locale)
638
    {
639
        return $query->whereHas(
640
            'translations',
641
            function (Builder $q) use ($locale) {
642
                $q->where($this->getLocaleKey(), '=', $locale);
643
            }
644
        );
645
    }
646
647
    public function scopeTranslated(Builder $query)
648
    {
649
        return $query->has('translations');
650
    }
651
652
    /**
653
     * Return unguarded attributes.
654
     *
655
     * @return array
656
     */
657
    public function getUnguardedAttributes()
658
    {
659
        foreach ($attributes = $this->getAttributes() as $attribute => $value) {
660
            $attributes[$attribute] = $this->{$attribute};
661
        }
662
663
        return array_diff_key($attributes, array_flip($this->getGuarded()));
664
    }
665
666
    /**
667
     * Get the fallback locale.
668
     *
669
     * @return string
670
     */
671
    protected function getFallbackLocale()
672
    {
673
        if (isset($this->cache['fallback_locale'])) {
674
            return $this->cache['fallback_locale'];
675
        }
676
677
        return $this->cache['fallback_locale'] = config('app.fallback_locale');
678
    }
679
680
    /**
681
     * This is to keep consistency with the
682
     * entry interface above us.
683
     *
684
     * @return string
685
     */
686
    public function getTableName()
687
    {
688
        return $this->getTable();
689
    }
690
691
    /**
692
     * Return if the entry is trashed or not.
693
     *
694
     * @return bool
695
     */
696
    public function trashed()
697
    {
698
        return parent::trashed();
699
    }
700
701
    public function toArray()
702
    {
703
        $attributes = $this->attributesToArray();
704
705
        foreach ($this->translatedAttributes as $field) {
706
            if ($translation = $this->getTranslation()) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $translation is correct as $this->getTranslation() (which targets Anomaly\Streams\Platform...Model::getTranslation()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
707
                $attributes[$field] = $translation->$field;
708
            }
709
        }
710
711
        return $attributes;
712
    }
713
714
    /**
715
     * Return the routable array information.
716
     *
717
     * @return array
718
     */
719
    public function toRoutable()
720
    {
721
        return $this->toArray();
722
    }
723
724
    private function alwaysFillable()
725
    {
726
        return false;
727
    }
728
729
    /**
730
     * Determine if the given attribute exists.
731
     * Make sure to skip where there could be an
732
     * issue with relational "looking" properties.
733
     *
734
     * @param  mixed $offset
735
     * @return bool
736
     */
737
    public function offsetExists($offset)
738
    {
739
        return !method_exists($this, $offset) && isset($this->$offset);
740
    }
741
742
    /**
743
     * Get the criteria class.
744
     *
745
     * @return string
746
     */
747
    public function getCriteriaName()
748
    {
749
        $criteria = substr(get_class($this), 0, -5) . 'Criteria';
750
751
        return class_exists($criteria) ? $criteria : EloquentCriteria::class;
752
    }
753
754
    public function __get($key)
755
    {
756
        if ($this->hasHook($key)) {
757
            return $this->call($key, []);
758
        }
759
760
        return parent::__get($key); // TODO: Change the autogenerated stub
761
    }
762
763
    public function __call($method, $parameters)
764
    {
765
        if ($this->hasHook($hook = snake_case($method))) {
766
            return $this->call($hook, $parameters);
767
        }
768
769
        return parent::__call($method, $parameters);
770
    }
771
772
    /**
773
     * Check if an attribute exists.
774
     *
775
     * @param string $key
776
     * @return bool
777
     */
778
    public function __isset($key)
779
    {
780
        return (in_array($key, $this->translatedAttributes) || parent::__isset($key));
781
    }
782
783
    /**
784
     * Return the string form of the model.
785
     *
786
     * @return string
787
     */
788
    function __toString()
789
    {
790
        return json_encode($this->toArray());
791
    }
792
}
793