Completed
Push — master ( 38efb8...5cc558 )
by Ryan
05:49
created

EloquentModel::getTranslation()   D

Complexity

Conditions 9
Paths 4

Size

Total Lines 36
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 12
nc 4
nop 2
dl 0
loc 36
rs 4.909
c 0
b 0
f 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
use Robbo\Presenter\PresentableInterface;
11
12
/**
13
 * Class EloquentModel
14
 *
15
 * @link   http://pyrocms.com/
16
 * @author PyroCMS, Inc. <[email protected]>
17
 * @author Ryan Thompson <[email protected]>
18
 */
19
class EloquentModel extends Model implements Arrayable, PresentableInterface
20
{
21
22
    use Hookable;
23
    use DispatchesJobs;
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
     * The cascading actions.
76
     *
77
     * @var array
78
     */
79
    protected $cascades = [];
80
81
    /**
82
     * Runtime cache.
83
     *
84
     * @var array
85
     */
86
    protected $cache = [];
87
88
    /**
89
     * Get the ID.
90
     *
91
     * @return integer
92
     */
93
    public function getId()
94
    {
95
        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...
96
    }
97
98
    /**
99
     * Return the object's ETag fingerprint.
100
     *
101
     * @return string
102
     */
103
    public function etag()
104
    {
105
        return md5(get_class($this) . json_encode($this->toArray()));
106
    }
107
108
    /**
109
     * Alias for $this->setTtl($ttl)
110
     *
111
     * @param $ttl
112
     * @return EloquentModel
113
     */
114
    public function cache($ttl)
115
    {
116
        return $this->setTtl($ttl);
117
    }
118
119
    /**
120
     * Fire a model event.
121
     *
122
     * @param $event
123
     * @return mixed
124
     */
125
    public function fireEvent($event)
126
    {
127
        return $this->fireModelEvent($event);
128
    }
129
130
    /**
131
     * Return the entry presenter.
132
     *
133
     * This is against standards but required
134
     * by the presentable interface.
135
     *
136
     * @return EloquentPresenter
137
     */
138 View Code Duplication
    public function getPresenter()
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...
139
    {
140
        $presenter = substr(get_class($this), 0, -5) . 'Presenter';
141
142
        if (class_exists($presenter)) {
143
            return app()->make($presenter, ['object' => $this]);
144
        }
145
146
        return new EloquentPresenter($this);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Anomaly\Stre...oquentPresenter($this); (Anomaly\Streams\Platform\Model\EloquentPresenter) is incompatible with the return type declared by the interface Robbo\Presenter\PresentableInterface::getPresenter of type Robbo\Presenter\Robbo\Presenter\Presenter.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
147
    }
148
149
    /**
150
     * Return a new collection class with our models.
151
     *
152
     * @param  array $items
153
     * @return Collection
154
     */
155 View Code Duplication
    public function newCollection(array $items = [])
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...
156
    {
157
        $collection = substr(get_class($this), 0, -5) . 'Collection';
158
159
        if (class_exists($collection)) {
160
            return new $collection($items);
161
        }
162
163
        return new EloquentCollection($items);
164
    }
165
166
    /**
167
     * Return the translatable flag.
168
     *
169
     * @return bool
170
     */
171
    public function isTranslatable()
172
    {
173
        return isset($this->translationModel);
174
    }
175
176
    /**
177
     * Set the translatable flag.
178
     *
179
     * @param $translatable
180
     * @return $this
181
     */
182
    public function setTranslatable($translatable)
183
    {
184
        $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...
185
186
        return $this;
187
    }
188
189
    /**
190
     * Set the ttl.
191
     *
192
     * @param  $ttl
193
     * @return $this
194
     */
195
    public function setTtl($ttl)
196
    {
197
        $this->ttl = $ttl;
198
199
        return $this;
200
    }
201
202
    /**
203
     * Get the ttl.
204
     *
205
     * @return int|mixed
206
     */
207
    public function getTtl()
208
    {
209
        return $this->ttl;
210
    }
211
212
    /**
213
     * Get cache collection key.
214
     *
215
     * @return string
216
     */
217
    public function getCacheCollectionKey()
218
    {
219
        return get_called_class();
220
    }
221
222
    /**
223
     * Get the model title.
224
     *
225
     * @return mixed
226
     */
227
    public function getTitle()
228
    {
229
        return $this->{$this->getTitleName()};
230
    }
231
232
    /**
233
     * Get the title key.
234
     *
235
     * @return string
236
     */
237
    public function getTitleName()
238
    {
239
        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...
240
    }
241
242
    /**
243
     * Return if a row is deletable or not.
244
     *
245
     * @return bool
246
     */
247
    public function isDeletable()
248
    {
249
        return true;
250
    }
251
252
    /**
253
     * Return if the model is restorable or not.
254
     *
255
     * @return bool
256
     */
257
    public function isRestorable()
258
    {
259
        return true;
260
    }
261
262
    /**
263
     * Return whether the model is being
264
     * force deleted or not.
265
     *
266
     * @return bool
267
     */
268
    public function isForceDeleting()
269
    {
270
        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...
271
    }
272
273
    /**
274
     * Flush the model's cache.
275
     *
276
     * @return $this
277
     */
278
    public function flushCache()
279
    {
280
        (new CacheCollection())->setKey($this->getCacheCollectionKey())->flush();
281
282
        return $this;
283
    }
284
285
    /**
286
     * Create a new Eloquent query builder for the model.
287
     *
288
     * @param  \Illuminate\Database\Query\Builder $query
289
     * @return \Illuminate\Database\Eloquent\Builder|static
290
     */
291
    public function newEloquentBuilder($query)
292
    {
293
        return new EloquentQueryBuilder($query);
294
    }
295
296
    /*
297
     * Alias for getTranslation()
298
     */
299
    public function translate($locale = null, $withFallback = false)
300
    {
301
        return $this->getTranslation($locale, $withFallback);
302
    }
303
304
    /*
305
     * Alias for getTranslation()
306
     */
307
    public function translateOrDefault($locale)
308
    {
309
        return $this->getTranslation($locale, true) ?: $this;
310
    }
311
312
    /*
313
     * Alias for getTranslationOrNew()
314
     */
315
    public function translateOrNew($locale)
316
    {
317
        return $this->getTranslationOrNew($locale);
318
    }
319
320
    /**
321
     * Get related translations.
322
     *
323
     * @return EloquentCollection
324
     */
325
    public function getTranslations()
326
    {
327
        foreach ($translations = $this->translations()->get() as $translation) {
328
            $translation->setRelation('parent', $this);
329
        }
330
331
        return $translations;
332
    }
333
334
    /**
335
     * @param  null      $locale
336
     * @param  bool|null $withFallback
337
     * @return Model|null
338
     */
339
    public function getTranslation($locale = null, $withFallback = true)
340
    {
341
342
        /**
343
         * If we have a desired locale and
344
         * it exists then just use that locale.
345
         */
346
        if ($locale && $translation = $this->getTranslationByLocaleKey($locale)) {
347
            return $translation;
348
        }
349
350
        /**
351
         * If we don't have a locale or it doesn't exist
352
         * then go ahead and try using a fallback in using
353
         * the system's designated DEFAULT (not active) locale.
354
         */
355
        if ($withFallback
356
            && $translation = $this->getTranslationByLocaleKey($this->getDefaultLocale())
357
        ) {
358
            return $translation;
359
        }
360
361
        /**
362
         * If we still don't have a translation then
363
         * try looking up the FALLBACK translation.
364
         */
365
        if ($withFallback
366
            && $this->getFallbackLocale()
367
            && $this->getTranslationByLocaleKey($this->getFallbackLocale())
368
            && $translation = $this->getTranslationByLocaleKey($this->getFallbackLocale())
369
        ) {
370
            return $translation;
371
        }
372
373
        return null;
374
    }
375
376
    public function hasTranslation($locale = null)
377
    {
378
        $locale = $locale ?: $this->getFallbackLocale();
379
380 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...
381
            $translation->setRelation('parent', $this);
382
383
            if ($translation->getAttribute($this->getLocaleKey()) == $locale) {
384
                return true;
385
            }
386
        }
387
388
        return false;
389
    }
390
391
    /**
392
     * Get the translation model.
393
     *
394
     * @return EloquentModel
395
     */
396
    public function getTranslationModel()
397
    {
398
        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...
399
    }
400
401
    /**
402
     * Get the translation model name.
403
     *
404
     * @return string
405
     */
406
    public function getTranslationModelName()
407
    {
408
        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...
409
    }
410
411
    /**
412
     * Get the translation table name.
413
     *
414
     * @return string
415
     */
416
    public function getTranslationTableName()
417
    {
418
        $model = $this->getTranslationModel();
419
420
        return $model->getTableName();
421
    }
422
423
    public function getTranslationModelNameDefault()
424
    {
425
        return get_class($this) . 'Translation';
426
    }
427
428
    public function getRelationKey()
429
    {
430
        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...
431
    }
432
433
    public function getLocaleKey()
434
    {
435
        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...
436
    }
437
438
    public function translations()
439
    {
440
        return $this->hasMany($this->getTranslationModelName(), $this->getRelationKey());
441
    }
442
443
    public function getAttribute($key)
444
    {
445
        if ($this->isTranslatedAttribute($key)) {
446
447
            if (($translation = $this->getTranslation()) === null) {
448
                return null;
449
            }
450
451
            $translation->setRelation('parent', $this);
452
453
            return $translation->$key;
454
        }
455
456
        return parent::getAttribute($key);
457
    }
458
459
    /**
460
     * Set an attribute.
461
     *
462
     * @param  string $key
463
     * @param  mixed  $value
464
     * @return $this
465
     */
466
    public function setAttribute($key, $value)
467
    {
468
        if (in_array($key, $this->translatedAttributes)) {
469
            $this->getTranslationOrNew(config('app.locale'))->$key = $value;
470
        } else {
471
            parent::setAttribute($key, $value);
472
        }
473
474
        return $this;
475
    }
476
477
    /**
478
     * Save the model.
479
     *
480
     * We have some customization here to
481
     * accommodate translations. First sa
482
     * then save translations is translatable.
483
     *
484
     * @param  array $options
485
     * @return bool
486
     */
487
    public function save(array $options = [])
488
    {
489
        if (!$this->getTranslationModelName()) {
490
            return $this->saveModel($options);
491
        }
492
493
        if ($this->exists) {
494
            if (count($this->getDirty()) > 0) {
495
496
                // 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...
497
                // an error has occurred. Therefore we shouldn't save the translations.
498
                if ($this->saveModel($options)) {
499
                    return $this->saveTranslations();
500
                }
501
502
                return false;
503
            } else {
504
505
                // 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...
506
                // false. So we have to save the translations
507
                return $this->saveTranslations();
508
            }
509
        } elseif ($this->saveModel($options)) {
510
511
            // We save the translations only if the instance is saved in the database.
512
            return $this->saveTranslations();
513
        }
514
515
        return false;
516
    }
517
518
    /**
519
     * Save the model to the database.
520
     *
521
     * This is a direct port from Eloquent
522
     * with the only exception being that if
523
     * the model is translatable it will NOT
524
     * fire the saved event. The saveTranslations
525
     * method will do that instead.
526
     *
527
     * @param  array $options
528
     * @return bool
529
     */
530
    public function saveModel(array $options = [])
531
    {
532
        $query = $this->newQueryWithoutScopes();
533
534
        // If the "saving" event returns false we'll bail out of the save and return
535
        // false, indicating that the save failed. This provides a chance for any
536
        // listeners to cancel save operations if validations fail or whatever.
537
        if ($this->fireModelEvent('saving') === false) {
538
            return false;
539
        }
540
541
        // If the model already exists in the database we can just update our record
542
        // that is already in this database using the current IDs in this "where"
543
        // clause to only update this model. Otherwise, we'll just insert them.
544
        if ($this->exists) {
545
            $saved = $this->performUpdate($query, $options);
0 ignored issues
show
Bug introduced by
It seems like $query defined by $this->newQueryWithoutScopes() on line 532 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...
546
        }
547
548
        // If the model is brand new, we'll insert it into our database and set the
549
        // ID attribute on the model to the value of the newly inserted row's ID
550
        // which is typically an auto-increment value managed by the database.
551
        else {
552
            $saved = $this->performInsert($query, $options);
0 ignored issues
show
Bug introduced by
It seems like $query defined by $this->newQueryWithoutScopes() on line 532 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...
Unused Code introduced by
The call to EloquentModel::performInsert() has too many arguments starting with $options.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
553
        }
554
555
        if ($saved && !$this->isTranslatable()) {
556
            $this->finishSave($options);
557
        }
558
559
        return $saved;
560
    }
561
562
    /**
563
     * Save translations to the database.
564
     *
565
     * @return bool
566
     */
567
    protected function saveTranslations()
568
    {
569
        $saved = true;
570
571
        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...
572
            $translation->setRelation('parent', $this);
573
574
            /* @var EloquentModel $translation */
575
            if ($saved && $this->isTranslationDirty($translation)) {
576
                $translation->setAttribute($this->getRelationKey(), $this->getKey());
577
578
                $saved = $translation->save();
579
            }
580
        }
581
582
        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...
583
            $translation = $this->translateOrNew(config('streams::locales.default'));
584
585
            $translation->save();
586
        }
587
588
        $this->finishSave([]);
589
590
        return $saved;
591
    }
592
593
    protected function getTranslationOrNew($locale)
594
    {
595
        if (($translation = $this->getTranslation($locale, false)) === null) {
596
            $translation = $this->getNewTranslation($locale);
597
        }
598
599
        return $translation;
600
    }
601
602
    public function fill(array $attributes)
603
    {
604
        foreach ($attributes as $key => $values) {
605
            if (is_array($values) && $this->isKeyALocale($key)) {
606
                foreach ($values as $translationAttribute => $translationValue) {
607
                    if ($this->alwaysFillable() || $this->isFillable($translationAttribute)) {
608
                        $this->getTranslationOrNew($key)->$translationAttribute = $translationValue;
609
                    }
610
                }
611
                unset($attributes[$key]);
612
            }
613
        }
614
615
        return parent::fill($attributes);
616
    }
617
618
    private function getTranslationByLocaleKey($key)
619
    {
620 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...
621
            $translation->setRelation('parent', $this);
622
623
            if ($translation->getAttribute($this->getLocaleKey()) == $key) {
624
                return $translation;
625
            }
626
        }
627
628
        return null;
629
    }
630
631
    public function isTranslatedAttribute($key)
632
    {
633
        return in_array($key, $this->translatedAttributes);
634
    }
635
636
    protected function isTranslationAttribute($key)
637
    {
638
        return in_array($key, $this->translatedAttributes);
639
    }
640
641
    protected function isKeyALocale($key)
642
    {
643
        return config('streams::locales.supported.' . $key) !== null;
644
    }
645
646
    protected function isTranslationDirty(Model $translation)
647
    {
648
        $dirtyAttributes = $translation->getDirty();
649
        unset($dirtyAttributes[$this->getLocaleKey()]);
650
651
        return count($dirtyAttributes) > 0;
652
    }
653
654
    public function getNewTranslation($locale)
655
    {
656
        $modelName = $this->getTranslationModelName();
657
658
        /* @var EloquentModel $translation */
659
        $translation = new $modelName;
660
661
        $translation->setRelation('parent', $this);
662
663
        $translation->setAttribute($this->getLocaleKey(), $locale);
664
        $translation->setAttribute($this->getRelationKey(), $this->getKey());
665
666
        $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...
667
668
        return $translation;
669
    }
670
671
    public function scopeTranslatedIn(Builder $query, $locale)
672
    {
673
        return $query->whereHas(
674
            'translations',
675
            function (Builder $q) use ($locale) {
676
                $q->where($this->getLocaleKey(), '=', $locale);
677
            }
678
        );
679
    }
680
681
    public function scopeTranslated(Builder $query)
682
    {
683
        return $query->has('translations');
684
    }
685
686
    /**
687
     * Return unguarded attributes.
688
     *
689
     * @return array
690
     */
691
    public function getUnguardedAttributes()
692
    {
693
        foreach ($attributes = $this->getAttributes() as $attribute => $value) {
694
            $attributes[$attribute] = $value;
695
        }
696
697
        return array_diff_key($attributes, array_flip($this->getGuarded()));
698
    }
699
700
    /**
701
     * Get the default locale.
702
     *
703
     * @return string
704
     */
705
    protected function getDefaultLocale()
706
    {
707
        if (isset($this->cache['default_locale'])) {
708
            return $this->cache['default_locale'];
709
        }
710
711
        return $this->cache['default_locale'] = config('streams::locales.default');
712
    }
713
714
    /**
715
     * Get the fallback locale.
716
     *
717
     * @return string
718
     */
719
    protected function getFallbackLocale()
720
    {
721
        if (isset($this->cache['fallback_locale'])) {
722
            return $this->cache['fallback_locale'];
723
        }
724
725
        return $this->cache['fallback_locale'] = config('app.fallback_locale');
726
    }
727
728
    /**
729
     * This is to keep consistency with the
730
     * entry interface above us.
731
     *
732
     * @return string
733
     */
734
    public function getTableName()
735
    {
736
        return $this->getTable();
737
    }
738
739
    /**
740
     * Return if the entry is trashed or not.
741
     *
742
     * @return bool
743
     */
744
    public function trashed()
745
    {
746
        return parent::trashed();
747
    }
748
749
    public function toArray()
750
    {
751
        $attributes = $this->attributesToArray();
752
753
        foreach ($this->translatedAttributes as $field) {
754
            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...
755
                $attributes[$field] = $translation->$field;
756
            }
757
        }
758
759
        return $attributes;
760
    }
761
762
    /**
763
     * Return the routable array information.
764
     *
765
     * @return array
766
     */
767
    public function toRoutableArray()
768
    {
769
        return $this->toArray();
770
    }
771
772
    private function alwaysFillable()
773
    {
774
        return false;
775
    }
776
777
    /**
778
     * Determine if the given attribute exists.
779
     * Make sure to skip where there could be an
780
     * issue with relational "looking" properties.
781
     *
782
     * @param  mixed $offset
783
     * @return bool
784
     */
785
    public function offsetExists($offset)
786
    {
787
        return !method_exists($this, $offset) && isset($this->$offset);
788
    }
789
790
    /**
791
     * Get the criteria class.
792
     *
793
     * @return string
794
     */
795
    public function getCriteriaName()
796
    {
797
        $criteria = substr(get_class($this), 0, -5) . 'Criteria';
798
799
        return class_exists($criteria) ? $criteria : EloquentCriteria::class;
800
    }
801
802
    /**
803
     * Get the cascading actions.
804
     *
805
     * @return array
806
     */
807
    public function getCascades()
808
    {
809
        return $this->cascades;
810
    }
811
812
    public function __get($key)
813
    {
814
        if ($this->hasHook($hook = 'get_' . $key)) {
815
            return $this->call($hook, []);
816
        }
817
818
        return parent::__get($key);
819
    }
820
821
    public function __call($method, $parameters)
822
    {
823
        if ($this->hasHook($hook = snake_case($method))) {
824
            return $this->call($hook, $parameters);
825
        }
826
827
        return parent::__call($method, $parameters);
828
    }
829
830
    /**
831
     * Check if an attribute exists.
832
     *
833
     * @param  string $key
834
     * @return bool
835
     */
836
    public function __isset($key)
837
    {
838
        return (in_array($key, $this->translatedAttributes) || parent::__isset($key));
839
    }
840
841
    /**
842
     * Return the string form of the model.
843
     *
844
     * @return string
845
     */
846
    public function __toString()
847
    {
848
        return json_encode($this->toArray());
849
    }
850
851
    /**
852
     * Remove volatile cache from
853
     * objects before serialization.
854
     *
855
     * @return array
856
     */
857
    public function __sleep()
858
    {
859
        return array_diff(array_keys(get_object_vars($this)), ['cache']);
860
    }
861
}
862