Test Failed
Pull Request — master (#85)
by Alex
05:36
created

MetadataTrait   D

Complexity

Total Complexity 109

Size/Duplication

Total Lines 705
Duplicated Lines 9.79 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 72.85%

Importance

Changes 0
Metric Value
wmc 109
lcom 1
cbo 7
dl 69
loc 705
ccs 110
cts 151
cp 0.7285
rs 4
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
B metadata() 0 55 6
A metadataMask() 0 16 3
A getEndpointName() 0 11 3
C getXmlSchema() 0 51 7
C hookUpRelationships() 14 32 8
A getRelationships() 0 20 2
B getAllAttributes() 0 25 4
D getRelationshipsFromMethods() 0 92 19
getVisible() 0 1 ?
getHidden() 0 1 ?
getKeyName() 0 1 ?
getConnectionName() 0 1 ?
getConnection() 0 1 ?
getAttributes() 0 1 ?
getTable() 0 1 ?
getFillable() 0 1 ?
B collectGetters() 0 20 6
C polyglotKeyMethodNames() 28 36 7
C polyglotKeyMethodBackupNames() 27 36 7
A addRelationsHook() 0 11 2
B getRelationshipsHasMany() 0 24 6
C getRelationshipsHasOne() 0 24 8
C getRelationshipsKnownPolyMorph() 0 22 9
C getRelationshipsUnknownPolyMorph() 0 22 7
A retrieveCasts() 0 4 1
A getEagerLoad() 0 5 1
A setEagerLoad() 0 6 1
A isKnownPolymorphSide() 0 5 1
A isUnknownPolymorphSide() 0 5 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MetadataTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MetadataTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use Illuminate\Database\Eloquent\Relations\BelongsTo;
5
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
6
use Illuminate\Database\Eloquent\Relations\MorphMany;
7
use Illuminate\Database\Eloquent\Relations\MorphOne;
8
use Illuminate\Database\Eloquent\Relations\MorphToMany;
9
use Illuminate\Support\Facades\App as App;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use POData\Providers\Metadata\ResourceEntityType;
12
use POData\Providers\Metadata\ResourceStreamInfo;
13
use POData\Providers\Metadata\Type\EdmPrimitiveType;
14
use Illuminate\Database\Eloquent\Model;
15
16
trait MetadataTrait
17
{
18
    protected static $relationHooks = [];
19
    protected static $relationCategories = [];
20
    protected static $methodPrimary = [];
21
    protected static $methodAlternate = [];
22
    protected $loadEagerRelations = [];
23
24
    /*
25
     * Array to record mapping between doctrine types and OData types
26
     */
27
    protected $mapping = [
28 3
        'integer' => EdmPrimitiveType::INT32,
29
        'string' => EdmPrimitiveType::STRING,
30 3
        'datetime' => EdmPrimitiveType::DATETIME,
31
        'float' => EdmPrimitiveType::SINGLE,
32
        'decimal' => EdmPrimitiveType::DECIMAL,
33 2
        'text' => EdmPrimitiveType::STRING,
34 2
        'boolean' => EdmPrimitiveType::BOOLEAN,
35
        'blob' => 'stream'
36 2
    ];
37
38 2
    /*
39 1
     * Retrieve and assemble this model's metadata for OData packaging
40
     */
41
    public function metadata()
42 1
    {
43 1
        assert($this instanceof Model, get_class($this));
44 1
45
        // Break these out separately to enable separate reuse
46 1
        $connect = $this->getConnection();
47
        $builder = $connect->getSchemaBuilder();
48 1
49 1
        $table = $this->getTable();
50 1
51
        if (!$builder->hasTable($table)) {
52 1
            return [];
53 1
        }
54 1
55
        $columns = $builder->getColumnListing($table);
56 1
        $mask = $this->metadataMask();
57
        $columns = array_intersect($columns, $mask);
58 1
59 1
        $tableData = [];
60 1
61 1
        $rawFoo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
62 1
        $foo = [];
63 1
        $getters = $this->collectGetters();
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
        return $tableData;
95 1
    }
96
97
    /*
98 4
     * Return the set of fields that are permitted to be in metadata
99
     * - following same visible-trumps-hidden guideline as Laravel
100 4
     */
101
    public function metadataMask()
102 4
    {
103 4
        $attribs = array_keys($this->getAllAttributes());
104 4
105 4
        $visible = $this->getVisible();
106 4
        $hidden = $this->getHidden();
107 4
        if (0 < count($visible)) {
108
            assert(!empty($visible));
109 4
            $attribs = array_intersect($visible, $attribs);
110 1
        } elseif (0 < count($hidden)) {
111 1
            assert(!empty($hidden));
112 1
            $attribs = array_diff($attribs, $hidden);
113 1
        }
114
115 4
        return $attribs;
116 4
    }
117
118 4
    /*
119
     * Get the endpoint name being exposed
120
     *
121
     */
122
    public function getEndpointName()
123
    {
124
        $endpoint = isset($this->endpoint) ? $this->endpoint : null;
125
126
        if (!isset($endpoint)) {
127
            $bitter = get_class();
128
            $name = substr($bitter, strrpos($bitter, '\\')+1);
129
            return strtolower($name);
130
        }
131
        return strtolower($endpoint);
132
    }
133
134
    /*
135
     * Assemble this model's OData metadata as xml schema
136
     *
137
     * @return ResourceEntityType
138
     */
139
    public function getXmlSchema($metaNamespace = 'Data')
0 ignored issues
show
Unused Code introduced by
The parameter $metaNamespace is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
140
    {
141
        $raw = $this->metadata();
142
        if ([] == $raw) {
143
            return null;
144
        }
145
146
        $metadata = App::make('metadata');
147
148
        $isUnknown = $this->isUnknownPolymorphSide();
149
        if ($isUnknown) {
150
            $baseType = $metadata->resolveEntityType('polyMorphicPlaceholder');
151
            assert($baseType instanceof ResourceEntityType);
152
            assert($baseType->isAbstract());
153
        } else {
154
            $baseType = null;
155
        }
156
157
        $reflec = new \ReflectionClass(get_class($this));
158
        $complex = $metadata->addEntityType($reflec, $reflec->getShortName(), false, $baseType);
159
        $keyName = $this->getKeyName();
160
        if (null != $keyName) {
161
            $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
162
        }
163
164
        foreach ($raw as $key => $secret) {
165
            if ($key == $keyName) {
166
                continue;
167
            }
168
            if ($secret['type'] == 'blob') {
169
                $complex->setMediaLinkEntry(true);
170
                $streamInfo = new ResourceStreamInfo($key);
171
                assert($complex->isMediaLinkEntry());
172
                $complex->addNamedStream($streamInfo);
173
                continue;
174
            }
175 3
            $nullable = $secret['nullable'];
176
            $default = $secret['default'];
177 3
            // tag as isBag?
178
            $metadata->addPrimitiveProperty(
179 3
                $complex,
180 3
                $key,
181 3
                $this->mapping[$secret['type']],
182 3
                false,
183 3
                $default,
184 3
                $nullable
185 3
            );
186 3
        }
187 3
188 3
        return $complex;
189
    }
190 3
191
    /**
192 3
     * @param $entityTypes
193 3
     * @param $resourceSets
194 3
     * @return array
195 3
     */
196 3
    public function hookUpRelationships($entityTypes, $resourceSets)
197 3
    {
198 3
        assert(is_array($entityTypes) && is_array($resourceSets), 'Both entityTypes and resourceSets must be arrays');
199 3
        $metadata = App::make('metadata');
200 3
        $rel = $this->getRelationshipsFromMethods();
201 3
        $thisClass = get_class($this);
202
        $thisInTypes = array_key_exists($thisClass, $entityTypes);
203 3
        $thisInSets = array_key_exists($thisClass, $resourceSets);
204 3
205 3
        if (!($thisInSets && $thisInTypes)) {
206 3
            return $rel;
207 3
        }
208 3
209 3
        $resourceType = $entityTypes[$thisClass];
210 3
        // if $r is in $combined keys, then its in keyspaces of both $entityTypes and $resourceSets
211
        $combinedKeys = array_intersect(array_keys($entityTypes), array_keys($resourceSets));
212 3 View Code Duplication
        foreach ($rel['HasOne'] as $n => $r) {
213 3
            $r = trim($r, '\\');
214 3
            if (in_array($r, $combinedKeys)) {
215
                $targResourceSet = $resourceSets[$r];
216 3
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
217 3
            }
218 3
        }
219 3 View Code Duplication
        foreach ($rel['HasMany'] as $n => $r) {
220 3
            $r = trim($r, '\\');
221
            if (in_array($r, $combinedKeys)) {
222 1
                $targResourceSet = $resourceSets[$r];
223 3
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
224
            }
225 1
        }
226
        return $rel;
227 1
    }
228
229 1
    /**
230
     * Get model's relationships
231 3
     *
232 2
     * @return array
233 2
     */
234 3
    public function getRelationships()
235 3
    {
236 3
        if (empty(static::$relationHooks)) {
237 3
            $hooks = [];
238 3
239 3
            $rels = $this->getRelationshipsFromMethods(true);
240 3
241
            $this->getRelationshipsUnknownPolyMorph($rels, $hooks);
242
243
            $this->getRelationshipsKnownPolyMorph($rels, $hooks);
244
245
            $this->getRelationshipsHasOne($rels, $hooks);
246
247
            $this->getRelationshipsHasMany($rels, $hooks);
248
249
            static::$relationHooks = $hooks;
250
        }
251
252
        return static::$relationHooks;
253
    }
254
255
    protected function getAllAttributes()
256
    {
257
        // Adapted from http://stackoverflow.com/a/33514981
258
        // $columns = $this->getFillable();
259
        // Another option is to get all columns for the table like so:
260
        $builder = $this->getConnection()->getSchemaBuilder();
261
        $columns = $builder->getColumnListing($this->getTable());
262
        // but it's safer to just get the fillable fields
263
264
        $attributes = $this->getAttributes();
265
266
        foreach ($columns as $column) {
267
            if (!array_key_exists($column, $attributes)) {
268
                $attributes[$column] = null;
269
            }
270
        }
271
272
        $methods = $this->collectGetters();
273
274
        foreach ($methods as $method) {
275
            $attributes[$method] = null;
276
        }
277
278
        return $attributes;
279
    }
280
281
    /**
282
     * @param bool $biDir
283
     * @return array
284
     */
285
    protected function getRelationshipsFromMethods($biDir = false)
286
    {
287
        $biDirVal = intval($biDir);
288
        $isCached = isset(static::$relationCategories[$biDirVal]) && !empty(static::$relationCategories[$biDirVal]);
289
        if (!$isCached) {
290
            $model = $this;
291
            $relationships = [
292
                'HasOne' => [],
293
                'UnknownPolyMorphSide' => [],
294
                'HasMany' => [],
295
                'KnownPolyMorphSide' => []
296
            ];
297
            $methods = get_class_methods($model);
298
            if (!empty($methods)) {
299
                foreach ($methods as $method) {
300
                    if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
301
                    ) {
302
                        //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
303
                        $reflection = new \ReflectionMethod($model, $method);
304
305
                        $file = new \SplFileObject($reflection->getFileName());
306
                        $file->seek($reflection->getStartLine() - 1);
307
                        $code = '';
308
                        while ($file->key() < $reflection->getEndLine()) {
309
                            $code .= $file->current();
310
                            $file->next();
311
                        }
312
313
                        $code = trim(preg_replace('/\s\s+/', '', $code));
314
                        assert(
315
                            false !== stripos($code, 'function'),
316
                            'Function definition must have keyword \'function\''
317
                        );
318
                        $begin = strpos($code, 'function(');
319
                        $code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
320
                        $lastCode = $code[strlen($code) - 1];
321
                        assert('}' == $lastCode, 'Final character of function definition must be closing brace');
322
                        foreach ([
323
                                     'hasMany',
324
                                     'hasManyThrough',
325
                                     'belongsToMany',
326
                                     'hasOne',
327
                                     'belongsTo',
328
                                     'morphOne',
329
                                     'morphTo',
330
                                     'morphMany',
331
                                     'morphToMany',
332
                                     'morphedByMany'
333
                                 ] as $relation) {
334
                            $search = '$this->' . $relation . '(';
335
                            if ($pos = stripos($code, $search)) {
336
                                //Resolve the relation's model to a Relation object.
337
                                $relationObj = $model->$method();
338
                                if ($relationObj instanceof Relation) {
339
                                    $relatedModel = '\\' . get_class($relationObj->getRelated());
340
                                    $relations = [
341
                                        'hasManyThrough',
342
                                        'belongsToMany',
343
                                        'hasMany',
344
                                        'morphMany',
345
                                        'morphToMany',
346
                                        'morphedByMany'
347
                                    ];
348
                                    if (in_array($relation, $relations)) {
349
                                        //Collection or array of models (because Collection is Arrayable)
350
                                        $relationships['HasMany'][$method] = $biDir ? $relationObj : $relatedModel;
351
                                    } elseif ('morphTo' === $relation) {
352
                                        // Model isn't specified because relation is polymorphic
353
                                        $relationships['UnknownPolyMorphSide'][$method] =
354
                                            $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
355
                                    } else {
356
                                        //Single model is returned
357
                                        $relationships['HasOne'][$method] = $biDir ? $relationObj : $relatedModel;
358
                                    }
359
                                    if (in_array($relation, ['morphMany', 'morphOne', 'morphToMany'])) {
360
                                        $relationships['KnownPolyMorphSide'][$method] =
361
                                            $biDir ? $relationObj : $relatedModel;
362
                                    }
363
                                    if (in_array($relation, ['morphedByMany'])) {
364
                                        $relationships['UnknownPolyMorphSide'][$method] =
365
                                            $biDir ? $relationObj : $relatedModel;
366
                                    }
367
                                }
368
                            }
369
                        }
370
                    }
371
                }
372
            }
373
            static::$relationCategories[$biDirVal] = $relationships;
374
        }
375
        return static::$relationCategories[$biDirVal];
376
    }
377
378
    /**
379
     * Get the visible attributes for the model.
380
     *
381
     * @return array
382
     */
383
    abstract public function getVisible();
384
385
    /**
386
     * Get the hidden attributes for the model.
387
     *
388
     * @return array
389
     */
390
    abstract public function getHidden();
391
392
    /**
393
     * Get the primary key for the model.
394
     *
395
     * @return string
396
     */
397
    abstract public function getKeyName();
398
399
    /**
400
     * Get the current connection name for the model.
401
     *
402
     * @return string
403
     */
404
    abstract public function getConnectionName();
405
406
    /**
407
     * Get the database connection for the model.
408
     *
409
     * @return \Illuminate\Database\Connection
410
     */
411
    abstract public function getConnection();
412
413
    /**
414
     * Get all of the current attributes on the model.
415
     *
416
     * @return array
417
     */
418
    abstract public function getAttributes();
419
420
    /**
421
     * Get the table associated with the model.
422
     *
423
     * @return string
424
     */
425
    abstract public function getTable();
426
427
    /**
428
     * Get the fillable attributes for the model.
429
     *
430
     * @return array
431
     */
432
    abstract public function getFillable();
433
434
    /**
435
     * Dig up all defined getters on the model
436
     *
437
     * @return array
438
     */
439
    protected function collectGetters()
440
    {
441
        $getterz = [];
442
        $methods = get_class_methods($this);
443
        foreach ($methods as $method) {
444
            if (12 < strlen($method) && 'get' == substr($method, 0, 3)) {
445
                if ('Attribute' == substr($method, -9)) {
446
                    $getterz[] = $method;
447
                }
448
            }
449
        }
450
        $methods = [];
451
452
        foreach ($getterz as $getter) {
453
            $residual = substr($getter, 3);
454
            $residual = substr($residual, 0, -9);
455
            $methods[] = $residual;
456
        }
457
        return $methods;
458
    }
459
460
    /**
461
     * @param $foo
462
     * @return array
463
     */
464
    private function polyglotKeyMethodNames($foo, $condition = false)
465
    {
466
        $fkList = ['getQualifiedForeignKeyName', 'getForeignKey'];
467
        $rkList = ['getQualifiedRelatedKeyName', 'getOtherKey', 'getOwnerKey'];
468
469
        $fkMethodName = null;
470
        $rkMethodName = null;
471 View Code Duplication
        if ($condition) {
472
            if (array_key_exists(get_class($foo), static::$methodPrimary)) {
473
                $line = static::$methodPrimary[get_class($foo)];
474
                $fkMethodName = $line['fk'];
475
                $rkMethodName = $line['rk'];
476
            } else {
477
                $methodList = get_class_methods(get_class($foo));
478
                $fkMethodName = 'getQualifiedForeignPivotKeyName';
479
                foreach ($fkList as $option) {
480
                    if (in_array($option, $methodList)) {
481
                        $fkMethodName = $option;
482
                        break;
483
                    }
484
                }
485
                assert(in_array($fkMethodName, $methodList), 'Selected method, '.$fkMethodName.', not in method list');
486
                $rkMethodName = 'getQualifiedRelatedPivotKeyName';
487
                foreach ($rkList as $option) {
488
                    if (in_array($option, $methodList)) {
489
                        $rkMethodName = $option;
490
                        break;
491
                    }
492
                }
493
                assert(in_array($rkMethodName, $methodList), 'Selected method, '.$rkMethodName.', not in method list');
494
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
495
                static::$methodPrimary[get_class($foo)] = $line;
496
            }
497
        }
498
        return [$fkMethodName, $rkMethodName];
499
    }
500
501
    private function polyglotKeyMethodBackupNames($foo, $condition = false)
502
    {
503
        $fkList = ['getForeignKey'];
504
        $rkList = ['getOtherKey'];
505
506
        $fkMethodName = null;
507
        $rkMethodName = null;
508 View Code Duplication
        if ($condition) {
509
            if (array_key_exists(get_class($foo), static::$methodAlternate)) {
510
                $line = static::$methodAlternate[get_class($foo)];
511
                $fkMethodName = $line['fk'];
512
                $rkMethodName = $line['rk'];
513
            } else {
514
                $methodList = get_class_methods(get_class($foo));
515
                $fkMethodName = 'getForeignKeyName';
516
                foreach ($fkList as $option) {
517
                    if (in_array($option, $methodList)) {
518
                        $fkMethodName = $option;
519
                        break;
520
                    }
521
                }
522
                assert(in_array($fkMethodName, $methodList), 'Selected method, '.$fkMethodName.', not in method list');
523
                $rkMethodName = 'getQualifiedParentKeyName';
524
                foreach ($rkList as $option) {
525
                    if (in_array($option, $methodList)) {
526
                        $rkMethodName = $option;
527
                        break;
528
                    }
529
                }
530
                assert(in_array($rkMethodName, $methodList), 'Selected method, '.$rkMethodName.', not in method list');
531
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
532
                static::$methodAlternate[get_class($foo)] = $line;
533
            }
534
        }
535
        return [$fkMethodName, $rkMethodName];
536
    }
537
538
    /**
539
     * @param $hooks
540
     * @param $first
541
     * @param $property
542
     * @param $last
543
     * @param $mult
544
     * @param $targ
545
     */
546
    private function addRelationsHook(&$hooks, $first, $property, $last, $mult, $targ)
547
    {
548
        if (!isset($hooks[$first])) {
549
            $hooks[$first] = [];
550
        }
551
        $hooks[$first][$targ] = [
552
            'property' => $property,
553
            'local' => $last,
554
            'multiplicity' => $mult
555
        ];
556
    }
557
558
    /**
559
     * @param $rels
560
     * @param $hooks
561
     */
562
    private function getRelationshipsHasMany($rels, &$hooks)
563
    {
564
        foreach ($rels['HasMany'] as $property => $foo) {
565
            if ($foo instanceof MorphMany || $foo instanceof MorphToMany) {
566
                continue;
567
            }
568
            $isBelong = $foo instanceof BelongsToMany;
569
            $mult = '*';
570
            $targ = get_class($foo->getRelated());
571
572
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
573
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
574
575
            $keyRaw = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
576
            $keySegments = explode('.', $keyRaw);
577
            $keyName = $keySegments[count($keySegments) - 1];
578
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
579
            $localSegments = explode('.', $localRaw);
580
            $localName = $localSegments[count($localSegments) - 1];
581
            $first = $keyName;
582
            $last = $localName;
583
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
584
        }
585
    }
586
587
    /**
588
     * @param $rels
589
     * @param $hooks
590
     */
591
    private function getRelationshipsHasOne($rels, &$hooks)
592
    {
593
        foreach ($rels['HasOne'] as $property => $foo) {
594
            if ($foo instanceof MorphOne) {
595
                continue;
596
            }
597
            $isBelong = $foo instanceof BelongsTo;
598
            $mult = $isBelong ? '1' : '0..1';
599
            $targ = get_class($foo->getRelated());
600
601
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
602
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
603
604
            $keyName = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
605
            $keySegments = explode('.', $keyName);
606
            $keyName = $keySegments[count($keySegments) - 1];
607
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
608
            $localSegments = explode('.', $localRaw);
609
            $localName = $localSegments[count($localSegments) - 1];
610
            $first = $isBelong ? $localName : $keyName;
611
            $last = $isBelong ? $keyName : $localName;
612
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
613
        }
614
    }
615
616
    /**
617
     * @param $rels
618
     * @param $hooks
619
     */
620
    private function getRelationshipsKnownPolyMorph($rels, &$hooks)
621
    {
622
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
623
            $isMany = $foo instanceof MorphToMany;
624
            $targ = get_class($foo->getRelated());
625
            $mult = $isMany ? '*' : $foo instanceof MorphMany ? '*' : '1';
626
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
627
628
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
629
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
630
631
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
632
            $keySegments = explode('.', $keyRaw);
633
            $keyName = $keySegments[count($keySegments) - 1];
634
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
635
            $localSegments = explode('.', $localRaw);
636
            $localName = $localSegments[count($localSegments) - 1];
637
            $first = $isMany ? $keyName : $localName;
638
            $last = $isMany ? $localName : $keyName;
639
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
640
        }
641
    }
642
643
    /**
644
     * @param $rels
645
     * @param $hooks
646
     */
647
    private function getRelationshipsUnknownPolyMorph($rels, &$hooks)
648
    {
649
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
650
            $isMany = $foo instanceof MorphToMany;
651
            $targ = get_class($foo->getRelated());
652
            $mult = $isMany ? '*' : '1';
653
654
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
655
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
656
657
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
658
            $keySegments = explode('.', $keyRaw);
659
            $keyName = $keySegments[count($keySegments) - 1];
660
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
661
            $localSegments = explode('.', $localRaw);
662
            $localName = $localSegments[count($localSegments) - 1];
663
664
            $first = $keyName;
665
            $last = (isset($localName) && '' != $localName) ? $localName : $foo->getRelated()->getKeyName();
666
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
667
        }
668
    }
669
670
    /**
671
     * SUpplemental function to retrieve cast array for Laravel versions that do not supply hasCasts
672
     *
673
     * @return array
674
     */
675
    public function retrieveCasts()
676
    {
677
        return $this->casts;
678
    }
679
680
    /**
681
     * Return list of relations to be eager-loaded by Laravel query provider
682
     *
683
     * @return array
684
     */
685
    public function getEagerLoad()
686
    {
687
        assert(is_array($this->loadEagerRelations));
688
        return $this->loadEagerRelations;
689
    }
690
691
    /**
692
     * Set list of relations to be eager-loaded
693
     *
694
     * @param array $relations
695
     */
696
    public function setEagerLoad(array $relations)
697
    {
698
        $check = array_map('strval', $relations);
699
        assert($relations == $check, 'All supplied relations must be resolvable to strings');
700
        $this->loadEagerRelations = $relations;
701
    }
702
703
    /*
704
     * Is this model the known side of at least one polymorphic relation?
705
     */
706
    public function isKnownPolymorphSide()
707
    {
708
        $rels = $this->getRelationshipsFromMethods();
709
        return !empty($rels['UnknownPolyMorphSide']);
710
    }
711
712
    /*
713
     * Is this model on the unknown side of at least one polymorphic relation?
714
     */
715
    public function isUnknownPolymorphSide()
716
    {
717
        $rels = $this->getRelationshipsFromMethods();
718
        return !empty($rels['KnownPolyMorphSide']);
719
    }
720
}
721