Completed
Push — master ( 6b6e89...d619c6 )
by Alex
02:08
created

MetadataTrait::metadata()   C

Complexity

Conditions 10
Paths 38

Size

Total Lines 69
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 41
CRAP Score 10

Importance

Changes 0
Metric Value
dl 0
loc 69
ccs 41
cts 41
cp 1
rs 6.0493
c 0
b 0
f 0
cc 10
eloc 43
nc 38
nop 0
crap 10

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubMonomorphic;
5
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubPolymorphic;
6
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubRelationType;
7
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityField;
8
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityFieldPrimitiveType;
9
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityFieldType;
10
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityGubbins;
11
use AlgoWeb\PODataLaravel\Query\LaravelReadQuery;
12
use Illuminate\Database\Eloquent\Model;
13
use Illuminate\Database\Eloquent\Relations\BelongsTo;
14
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
15
use Illuminate\Database\Eloquent\Relations\MorphMany;
16
use Illuminate\Database\Eloquent\Relations\MorphOne;
17
use Illuminate\Database\Eloquent\Relations\MorphToMany;
18
use Illuminate\Database\Eloquent\Relations\Relation;
19
use Illuminate\Support\Facades\App;
20
21
trait MetadataTrait
22
{
23
    protected static $relationHooks = [];
24
    protected static $relationCategories = [];
25
    protected static $methodPrimary = [];
26
    protected static $methodAlternate = [];
27
    protected $loadEagerRelations = [];
28 3
    protected static $tableColumns = [];
29
    protected static $tableColumnsDoctrine = [];
30 3
    protected static $tableData = [];
31
    protected static $dontCastTypes = ['object', 'array', 'collection', 'int'];
32
33 2
    /*
34 2
     * Retrieve and assemble this model's metadata for OData packaging
35
     */
36 2
    public function metadata()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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

Loading history...
37
    {
38 2
        assert($this instanceof Model, get_class($this));
39 1
40
        // Break these out separately to enable separate reuse
41
        $connect = $this->getConnection();
42 1
        $builder = $connect->getSchemaBuilder();
43 1
44 1
        $table = $this->getTable();
45
46 1
        if (!$builder->hasTable($table)) {
47
            return [];
48 1
        }
49 1
        if (0 !== count(self::$tableData)) {
50 1
            return self::$tableData;
51
        }
52 1
53 1
        $columns = $this->getTableColumns();
54 1
        $mask = $this->metadataMask();
55
        $columns = array_intersect($columns, $mask);
56 1
57
        $tableData = [];
58 1
59 1
        $rawFoo = $this->getTableDoctrineColumns();
60 1
        $foo = [];
61 1
        $getters = $this->collectGetters();
62 1
        $getters = array_intersect($getters, $mask);
63 1
        $casts = $this->retrieveCasts();
64 1
65
        foreach ($rawFoo as $key => $val) {
66 1
            // Work around glitch in Doctrine when reading from MariaDB which added ` characters to root key value
67
            $key = trim($key, '`');
68
            $foo[$key] = $val;
69
        }
70
71
        foreach ($columns as $column) {
72
            // Doctrine schema manager returns columns with lowercased names
73 4
            $rawColumn = $foo[strtolower($column)];
74
            $nullable = !($rawColumn->getNotNull());
75 4
            $fillable = in_array($column, $this->getFillable());
76
            $rawType = $rawColumn->getType();
77 4
            $type = $rawType->getName();
78 4
            $default = $this->$column;
79 4
            $tableData[$column] = ['type' => $type,
80 2
                'nullable' => $nullable,
81 4
                'fillable' => $fillable,
82 1
                'default' => $default
83 1
            ];
84
        }
85 4
86
        foreach ($getters as $get) {
87
            if (isset($tableData[$get])) {
88
                continue;
89
            }
90
            $default = $this->$get;
91 5
            $tableData[$get] = ['type' => 'text', 'nullable' => true, 'fillable' => false, 'default' => $default];
92
        }
93 5
94 5
        // now, after everything's gathered up, apply Eloquent model's $cast array
95 1
        foreach ($casts as $key => $type) {
96
            $type = strtolower($type);
97
            if (array_key_exists($key, $tableData) && !in_array($type, self::$dontCastTypes)) {
98 4
                $tableData[$key]['type'] = $type;
99
            }
100 4
        }
101
102 4
        self::$tableData = $tableData;
103 4
        return $tableData;
104 4
    }
105 4
106 4
    /*
107 4
     * Return the set of fields that are permitted to be in metadata
108
     * - following same visible-trumps-hidden guideline as Laravel
109 4
     */
110 1
    public function metadataMask()
111 1
    {
112 1
        $attribs = array_keys($this->getAllAttributes());
113 1
114
        $visible = $this->getVisible();
115 4
        $hidden = $this->getHidden();
116 4
        if (0 < count($visible)) {
117
            assert(!empty($visible));
118 4
            $attribs = array_intersect($visible, $attribs);
119
        } elseif (0 < count($hidden)) {
120
            assert(!empty($hidden));
121
            $attribs = array_diff($attribs, $hidden);
122
        }
123
124
        return $attribs;
125
    }
126
127
    /*
128
     * Get the endpoint name being exposed
129
     *
130
     */
131
    public function getEndpointName()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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

Loading history...
132
    {
133
        $endpoint = isset($this->endpoint) ? $this->endpoint : null;
134
135
        if (!isset($endpoint)) {
136
            $bitter = get_class();
137
            $name = substr($bitter, strrpos($bitter, '\\')+1);
138
            return ($name);
139
        }
140
        return ($endpoint);
141
    }
142
143
    /**
144
     * Get model's relationships.
145
     *
146
     * @return array
147
     */
148
    public function getRelationships()
149
    {
150
        if (empty(static::$relationHooks)) {
151
            $hooks = [];
152
153
            $rels = $this->getRelationshipsFromMethods(true);
154
155
            $this->getRelationshipsUnknownPolyMorph($rels, $hooks);
156
157
            $this->getRelationshipsKnownPolyMorph($rels, $hooks);
158
159
            $this->getRelationshipsHasOne($rels, $hooks);
160
161
            $this->getRelationshipsHasMany($rels, $hooks);
162
163
            static::$relationHooks = $hooks;
164
        }
165
166
        return static::$relationHooks;
167
    }
168
169
    protected function getAllAttributes()
170
    {
171
        // Adapted from http://stackoverflow.com/a/33514981
172
        // $columns = $this->getFillable();
173
        // Another option is to get all columns for the table like so:
174
        $columns = $this->getTableColumns();
175 3
        // but it's safer to just get the fillable fields
176
177 3
        $attributes = $this->getAttributes();
178
179 3
        foreach ($columns as $column) {
180 3
            if (!array_key_exists($column, $attributes)) {
181 3
                $attributes[$column] = null;
182 3
            }
183 3
        }
184 3
185 3
        $methods = $this->collectGetters();
186 3
187 3
        foreach ($methods as $method) {
188 3
            $attributes[$method] = null;
189
        }
190 3
191
        return $attributes;
192 3
    }
193 3
194 3
    /**
195 3
     * @param bool $biDir
196 3
     *
197 3
     * @return array
198 3
     */
199 3
    protected function getRelationshipsFromMethods($biDir = false)
200 3
    {
201 3
        $biDirVal = intval($biDir);
202
        $isCached = isset(static::$relationCategories[$biDirVal]) && !empty(static::$relationCategories[$biDirVal]);
203 3
        if (!$isCached) {
204 3
            $model = $this;
205 3
            $relationships = [
206 3
                'HasOne' => [],
207 3
                'UnknownPolyMorphSide' => [],
208 3
                'HasMany' => [],
209 3
                'KnownPolyMorphSide' => []
210 3
            ];
211
            $methods = get_class_methods($model);
212 3
            if (!empty($methods)) {
213 3
                foreach ($methods as $method) {
214 3
                    if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
215
                        && !method_exists(Mock::class, $method)
216 3
                        && !method_exists(MetadataTrait::class, $method)
217 3
                    ) {
218 3
                        //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
219 3
                        $reflection = new \ReflectionMethod($model, $method);
220 3
                        $fileName = $reflection->getFileName();
221
222 1
                        $file = new \SplFileObject($fileName);
223 3
                        $file->seek($reflection->getStartLine()-1);
224
                        $code = '';
225 1
                        while ($file->key() < $reflection->getEndLine()) {
226
                            $code .= $file->current();
227 1
                            $file->next();
228
                        }
229 1
230
                        $code = trim(preg_replace('/\s\s+/', '', $code));
231 3
                        assert(
232 2
                            false !== stripos($code, 'function'),
233 2
                            'Function definition must have keyword \'function\''
234 3
                        );
235 3
                        $begin = strpos($code, 'function(');
236 3
                        $code = substr($code, $begin, strrpos($code, '}')-$begin+1);
237 3
                        $lastCode = $code[strlen($code)-1];
238 3
                        assert('}' == $lastCode, 'Final character of function definition must be closing brace');
239 3
                        foreach ([
240 3
                                     'hasMany',
241
                                     'hasManyThrough',
242
                                     'belongsToMany',
243
                                     'hasOne',
244
                                     'belongsTo',
245
                                     'morphOne',
246
                                     'morphTo',
247
                                     'morphMany',
248
                                     'morphToMany',
249
                                     'morphedByMany'
250
                                 ] as $relation) {
251
                            $search = '$this->' . $relation . '(';
252
                            if ($pos = stripos($code, $search)) {
253
                                //Resolve the relation's model to a Relation object.
254
                                $relationObj = $model->$method();
255
                                if ($relationObj instanceof Relation) {
256
                                    $relObject = $relationObj->getRelated();
257
                                    $relatedModel = '\\' . get_class($relObject);
258
                                    if (in_array(MetadataTrait::class, class_uses($relatedModel))) {
259
                                        $relations = [
260
                                            'hasManyThrough',
261
                                            'belongsToMany',
262
                                            'hasMany',
263
                                            'morphMany',
264
                                            'morphToMany',
265
                                            'morphedByMany'
266
                                        ];
267
                                        if (in_array($relation, $relations)) {
268
                                            //Collection or array of models (because Collection is Arrayable)
269
                                            $relationships['HasMany'][$method] = $biDir ? $relationObj : $relatedModel;
270
                                        } elseif ('morphTo' === $relation) {
271
                                            // Model isn't specified because relation is polymorphic
272
                                            $relationships['UnknownPolyMorphSide'][$method] =
273
                                                $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
274
                                        } else {
275
                                            //Single model is returned
276
                                            $relationships['HasOne'][$method] = $biDir ? $relationObj : $relatedModel;
277
                                        }
278
                                        if (in_array($relation, ['morphMany', 'morphOne', 'morphToMany'])) {
279
                                            $relationships['KnownPolyMorphSide'][$method] =
280
                                                $biDir ? $relationObj : $relatedModel;
281
                                        }
282
                                        if (in_array($relation, ['morphedByMany'])) {
283
                                            $relationships['UnknownPolyMorphSide'][$method] =
284
                                                $biDir ? $relationObj : $relatedModel;
285
                                        }
286
                                    }
287
                                }
288
                            }
289
                        }
290
                    }
291
                }
292
            }
293
            static::$relationCategories[$biDirVal] = $relationships;
294
        }
295
        return static::$relationCategories[$biDirVal];
296
    }
297
298
    /**
299
     * Get the visible attributes for the model.
300
     *
301
     * @return array
302
     */
303
    abstract public function getVisible();
304
305
    /**
306
     * Get the hidden attributes for the model.
307
     *
308
     * @return array
309
     */
310
    abstract public function getHidden();
311
312
    /**
313
     * Get the primary key for the model.
314
     *
315
     * @return string
316
     */
317
    abstract public function getKeyName();
318
319
    /**
320
     * Get the current connection name for the model.
321
     *
322
     * @return string
323
     */
324
    abstract public function getConnectionName();
325
326
    /**
327
     * Get the database connection for the model.
328
     *
329
     * @return \Illuminate\Database\Connection
330
     */
331
    abstract public function getConnection();
332
333
    /**
334
     * Get all of the current attributes on the model.
335
     *
336
     * @return array
337
     */
338
    abstract public function getAttributes();
339
340
    /**
341
     * Get the table associated with the model.
342
     *
343
     * @return string
344
     */
345
    abstract public function getTable();
346
347
    /**
348
     * Get the fillable attributes for the model.
349
     *
350
     * @return array
351
     */
352
    abstract public function getFillable();
353
354
    /**
355
     * Dig up all defined getters on the model.
356
     *
357
     * @return array
358
     */
359
    protected function collectGetters()
360
    {
361
        $getterz = [];
362
        $methods = get_class_methods($this);
363
        foreach ($methods as $method) {
364
            if (12 < strlen($method) && 'get' == substr($method, 0, 3)) {
365
                if ('Attribute' == substr($method, -9)) {
366
                    $getterz[] = $method;
367
                }
368
            }
369
        }
370
        $methods = [];
371
372
        foreach ($getterz as $getter) {
373
            $residual = substr($getter, 3);
374
            $residual = substr($residual, 0, -9);
375
            $methods[] = $residual;
376
        }
377
        return $methods;
378
    }
379
380
    /**
381
     * @param       $foo
382
     * @param mixed $condition
383
     *
384
     * @return array
385
     */
386
    private function polyglotKeyMethodNames($foo, $condition = false)
387
    {
388
        $fkList = ['getQualifiedForeignKeyName', 'getForeignKey'];
389
        $rkList = ['getQualifiedRelatedKeyName', 'getOtherKey', 'getOwnerKey'];
390
391
        $fkMethodName = null;
392
        $rkMethodName = null;
393
        if ($condition) {
394
            if (array_key_exists(get_class($foo), static::$methodPrimary)) {
395
                $line = static::$methodPrimary[get_class($foo)];
396
                $fkMethodName = $line['fk'];
397
                $rkMethodName = $line['rk'];
398
            } else {
399
                $methodList = get_class_methods(get_class($foo));
400
                $fkMethodName = 'getQualifiedForeignPivotKeyName';
401
                foreach ($fkList as $option) {
402
                    if (in_array($option, $methodList)) {
403
                        $fkMethodName = $option;
404
                        break;
405
                    }
406
                }
407
                assert(
408
                    in_array($fkMethodName, $methodList),
409
                    'Selected method, ' . $fkMethodName . ', not in method list'
410
                );
411
                $rkMethodName = 'getQualifiedRelatedPivotKeyName';
412
                foreach ($rkList as $option) {
413
                    if (in_array($option, $methodList)) {
414
                        $rkMethodName = $option;
415
                        break;
416
                    }
417
                }
418
                assert(
419
                    in_array($rkMethodName, $methodList),
420
                    'Selected method, ' . $rkMethodName . ', not in method list'
421
                );
422
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
423
                static::$methodPrimary[get_class($foo)] = $line;
424
            }
425
        }
426
        return [$fkMethodName, $rkMethodName];
427
    }
428
429
    private function polyglotKeyMethodBackupNames($foo, $condition = false)
430
    {
431
        $fkList = ['getForeignKey', 'getForeignKeyName'];
432
        $rkList = ['getOtherKey', 'getQualifiedParentKeyName'];
433
434
        $fkMethodName = null;
435
        $rkMethodName = null;
436
        if ($condition) {
437
            if (array_key_exists(get_class($foo), static::$methodAlternate)) {
438
                $line = static::$methodAlternate[get_class($foo)];
439
                $fkMethodName = $line['fk'];
440
                $rkMethodName = $line['rk'];
441
            } else {
442
                $methodList = get_class_methods(get_class($foo));
443
                $fkCombo = array_values(array_intersect($fkList, $methodList));
444
                assert(1 <= count($fkCombo), 'Expected at least 1 element in foreign-key list, got ' . count($fkCombo));
445
                $fkMethodName = $fkCombo[0];
446
                assert(
447
                    in_array($fkMethodName, $methodList),
448
                    'Selected method, ' . $fkMethodName . ', not in method list'
449
                );
450
                $rkCombo = array_values(array_intersect($rkList, $methodList));
451
                assert(1 <= count($rkCombo), 'Expected at least 1 element in related-key list, got ' . count($rkCombo));
452
                $rkMethodName = $rkCombo[0];
453
                assert(
454
                    in_array($rkMethodName, $methodList),
455
                    'Selected method, ' . $rkMethodName . ', not in method list'
456
                );
457
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
458
                static::$methodAlternate[get_class($foo)] = $line;
459
            }
460
        }
461
        return [$fkMethodName, $rkMethodName];
462
    }
463
464
    /**
465
     * @param             $hooks
466
     * @param             $first
467
     * @param             $property
468
     * @param             $last
469
     * @param             $mult
470
     * @param             $targ
471
     * @param string|null $targ
472
     * @param null|mixed  $type
473
     */
474
    private function addRelationsHook(&$hooks, $first, $property, $last, $mult, $targ, $type = null)
475
    {
476
        if (!isset($hooks[$first])) {
477
            $hooks[$first] = [];
478
        }
479
        if (!isset($hooks[$first][$targ])) {
480
            $hooks[$first][$targ] = [];
481
        }
482
        $hooks[$first][$targ][$property] = [
483
            'property' => $property,
484
            'local' => $last,
485
            'multiplicity' => $mult,
486
            'type' => $type
487
        ];
488
    }
489
490
    /**
491
     * @param $rels
492
     * @param $hooks
493
     */
494
    private function getRelationshipsHasMany($rels, &$hooks)
495
    {
496
        foreach ($rels['HasMany'] as $property => $foo) {
497
            if ($foo instanceof MorphMany || $foo instanceof MorphToMany) {
498
                continue;
499
            }
500
            $isBelong = $foo instanceof BelongsToMany;
501
            $mult = '*';
502
            $targ = get_class($foo->getRelated());
503
504
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
505
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
506
507
            $keyRaw = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
508
            $keySegments = explode('.', $keyRaw);
509
            $keyName = $keySegments[count($keySegments)-1];
510
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
511
            $localSegments = explode('.', $localRaw);
512
            $localName = $localSegments[count($localSegments)-1];
513
            $first = $keyName;
514
            $last = $localName;
515
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
516
        }
517
    }
518
519
    /**
520
     * @param $rels
521
     * @param $hooks
522
     */
523
    private function getRelationshipsHasOne($rels, &$hooks)
524
    {
525
        foreach ($rels['HasOne'] as $property => $foo) {
526
            if ($foo instanceof MorphOne) {
527
                continue;
528
            }
529
            $isBelong = $foo instanceof BelongsTo;
530
            $mult = $isBelong ? '1' : '0..1';
531
            $targ = get_class($foo->getRelated());
532
533
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
534
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
535
536
            $keyName = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
537
            $keySegments = explode('.', $keyName);
538
            $keyName = $keySegments[count($keySegments)-1];
539
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
540
            $localSegments = explode('.', $localRaw);
541
            $localName = $localSegments[count($localSegments)-1];
542
            $first = $isBelong ? $localName : $keyName;
543
            $last = $isBelong ? $keyName : $localName;
544
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
545
        }
546
    }
547
548
    /**
549
     * @param $rels
550
     * @param $hooks
551
     */
552
    private function getRelationshipsKnownPolyMorph($rels, &$hooks)
553
    {
554
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
555
            $isMany = $foo instanceof MorphToMany;
556
            $targ = get_class($foo->getRelated());
557
            $mult = $isMany ? '*' : $foo instanceof MorphMany ? '*' : '1';
558
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
559
560
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
561
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
562
563
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
564
            $keySegments = explode('.', $keyRaw);
565
            $keyName = $keySegments[count($keySegments)-1];
566
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
567
            $localSegments = explode('.', $localRaw);
568
            $localName = $localSegments[count($localSegments)-1];
569
            $first = $isMany ? $keyName : $localName;
570
            $last = $isMany ? $localName : $keyName;
571
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ, 'unknown');
572
        }
573
    }
574
575
    /**
576
     * @param $rels
577
     * @param $hooks
578
     */
579
    private function getRelationshipsUnknownPolyMorph($rels, &$hooks)
580
    {
581
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
582
            $isMany = $foo instanceof MorphToMany;
583
            $targ = get_class($foo->getRelated());
584
            $mult = $isMany ? '*' : '1';
585
586
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
587
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
588
589
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
590
            $keySegments = explode('.', $keyRaw);
591
            $keyName = $keySegments[count($keySegments)-1];
592
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
593
            $localSegments = explode('.', $localRaw);
594
            $localName = $localSegments[count($localSegments)-1];
595
596
            $first = $keyName;
597
            $last = (isset($localName) && '' != $localName) ? $localName : $foo->getRelated()->getKeyName();
598
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ, 'known');
599
        }
600
    }
601
602
    /**
603
     * SUpplemental function to retrieve cast array for Laravel versions that do not supply hasCasts.
604
     *
605
     * @return array
606
     */
607
    public function retrieveCasts()
608
    {
609
        if (method_exists($this, 'getCasts')) {
610
            return $this->getCasts();
0 ignored issues
show
Bug introduced by
It seems like getCasts() must be provided by classes using this trait. How about adding it as abstract method to this trait?

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

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

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

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

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

Loading history...
611
        }
612
        return $this->casts;
613
    }
614
615
    /**
616
     * Return list of relations to be eager-loaded by Laravel query provider.
617
     *
618
     * @return array
619
     */
620
    public function getEagerLoad()
621
    {
622
        assert(is_array($this->loadEagerRelations));
623
        return $this->loadEagerRelations;
624
    }
625
626
    /**
627
     * Set list of relations to be eager-loaded.
628
     *
629
     * @param array $relations
630
     */
631
    public function setEagerLoad(array $relations)
632
    {
633
        $check = array_map('strval', $relations);
634
        assert($relations == $check, 'All supplied relations must be resolvable to strings');
635
        $this->loadEagerRelations = $relations;
636
    }
637
638
    /*
639
     * Is this model the known side of at least one polymorphic relation?
640
     */
641
    public function isKnownPolymorphSide()
642
    {
643
        // isKnownPolymorph needs to be checking KnownPolymorphSide results - if you're checking UnknownPolymorphSide,
644
        // you're turned around
645
        $rels = $this->getRelationshipsFromMethods();
646
        return !empty($rels['KnownPolyMorphSide']);
647
    }
648
649
    /*
650
     * Is this model on the unknown side of at least one polymorphic relation?
651
     */
652
    public function isUnknownPolymorphSide()
653
    {
654
        // isUnknownPolymorph needs to be checking UnknownPolymorphSide results - if you're checking KnownPolymorphSide,
655
        // you're turned around
656
        $rels = $this->getRelationshipsFromMethods();
657
        return !empty($rels['UnknownPolyMorphSide']);
658
    }
659
660
    /**
661
     * Extract entity gubbins detail for later downstream use.
662
     *
663
     * @return EntityGubbins
664
     */
665
    public function extractGubbins()
666
    {
667
        $multArray = [
668
            '*' => AssociationStubRelationType::MANY(),
669
            '1' => AssociationStubRelationType::ONE(),
670
            '0..1' => AssociationStubRelationType::NULL_ONE()
671
        ];
672
673
        $gubbins = new EntityGubbins();
674
        $gubbins->setName($this->getEndpointName());
675
        $gubbins->setClassName(get_class($this));
676
677
        $lowerNames = [];
678
679
        $fields = $this->metadata();
680
        $entityFields = [];
681
        foreach ($fields as $name => $field) {
682
            if (in_array(strtolower($name), $lowerNames)) {
683
                $msg = 'Property names must be unique, without regard to case';
684
                throw new \Exception($msg);
685
            }
686
            $lowerNames[] = strtolower($name);
687
            $nuField = new EntityField();
688
            $nuField->setName($name);
689
            $nuField->setIsNullable($field['nullable']);
690
            $nuField->setReadOnly(false);
691
            $nuField->setCreateOnly(false);
692
            $nuField->setDefaultValue($field['default']);
693
            $nuField->setIsKeyField($this->getKeyName() == $name);
694
            $nuField->setFieldType(EntityFieldType::PRIMITIVE());
695
            $nuField->setPrimitiveType(new EntityFieldPrimitiveType($field['type']));
696
            $entityFields[$name] = $nuField;
697
        }
698
        $isEmpty = (0 === count($entityFields));
699
        if (!($isEmpty && $this->isRunningInArtisan())) {
700
            $gubbins->setFields($entityFields);
701
        }
702
703
        $rawRels = $this->getRelationships();
704
        $stubs = [];
705
        foreach ($rawRels as $key => $rel) {
706
            foreach ($rel as $rawName => $deets) {
707
                foreach ($deets as $relName => $relGubbins) {
708
                    if (in_array(strtolower($relName), $lowerNames)) {
709
                        $msg = 'Property names must be unique, without regard to case';
710
                        throw new \Exception($msg);
711
                    }
712
                    $lowerNames[] = strtolower($relName);
713
                    $gubbinsType = $relGubbins['type'];
714
                    $property = $relGubbins['property'];
715
                    $isPoly = isset($gubbinsType);
716
                    $targType = 'known' != $gubbinsType ? $rawName : null;
717
                    $stub = $isPoly ? new AssociationStubPolymorphic() : new AssociationStubMonomorphic();
718
                    $stub->setBaseType(get_class($this));
719
                    $stub->setRelationName($property);
720
                    $stub->setKeyField($relGubbins['local']);
721
                    $stub->setForeignField($targType ? $key : null);
722
                    $stub->setMultiplicity($multArray[$relGubbins['multiplicity']]);
723
                    $stub->setTargType($targType);
724
                    assert($stub->isOk());
725
                    $stubs[$property] = $stub;
726
                }
727
            }
728
        }
729
        $gubbins->setStubs($stubs);
730
731
        return $gubbins;
732
    }
733
734
    public function synthLiteralPK()
735
    {
736
        if (!$this->isKnownPolymorphSide()) {
737
            return;
738
        }
739
        $fieldName = LaravelReadQuery::PK;
740
        $this->$fieldName = $this->getKey();
741
    }
742
743
    public function isRunningInArtisan()
744
    {
745
        return App::runningInConsole() && !App::runningUnitTests();
746
    }
747
748
    protected function getTableColumns()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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

Loading history...
749
    {
750
        if (0 === count(self::$tableColumns)) {
751
            $table = $this->getTable();
752
            $connect = $this->getConnection();
753
            $builder = $connect->getSchemaBuilder();
754
            $columns = $builder->getColumnListing($table);
755
756
            self::$tableColumns = $columns;
757
        }
758
        return self::$tableColumns;
759
    }
760
761
    protected function getTableDoctrineColumns()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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

Loading history...
762
    {
763
        if (0 === count(self::$tableColumnsDoctrine)) {
764
            $table = $this->getTable();
765
            $connect = $this->getConnection();
766
            $columns = $connect->getDoctrineSchemaManager()->listTableColumns($table);
767
768
            self::$tableColumnsDoctrine = $columns;
769
        }
770
        return self::$tableColumnsDoctrine;
771
    }
772
773
    public function reset()
774
    {
775
        self::$tableData = [];
776
        self::$tableColumnsDoctrine = [];
777
        self::$tableColumns = [];
778
    }
779
}
780