Test Failed
Pull Request — master (#85)
by Alex
06:31
created

MetadataTrait::isKnownPolymorphSide()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
ccs 0
cts 0
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
crap 2
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\ResourceStreamInfo;
12
use POData\Providers\Metadata\Type\EdmPrimitiveType;
13
use Illuminate\Database\Eloquent\Model;
14
15
trait MetadataTrait
16
{
17
    protected static $relationHooks = [];
18
    protected static $relationCategories = [];
19
    protected static $methodPrimary = [];
20
    protected static $methodAlternate = [];
21
    protected $loadEagerRelations = [];
22
23
    /*
24
     * Array to record mapping between doctrine types and OData types
25
     */
26
    protected $mapping = [
27
        'integer' => EdmPrimitiveType::INT32,
28 3
        'string' => EdmPrimitiveType::STRING,
29
        'datetime' => EdmPrimitiveType::DATETIME,
30 3
        'float' => EdmPrimitiveType::SINGLE,
31
        'decimal' => EdmPrimitiveType::DECIMAL,
32
        'text' => EdmPrimitiveType::STRING,
33 2
        'boolean' => EdmPrimitiveType::BOOLEAN,
34 2
        'blob' => 'stream'
35
    ];
36 2
37
    /*
38 2
     * Retrieve and assemble this model's metadata for OData packaging
39 1
     */
40
    public function metadata()
41
    {
42 1
        assert($this instanceof Model, get_class($this));
43 1
44 1
        // Break these out separately to enable separate reuse
45
        $connect = $this->getConnection();
46 1
        $builder = $connect->getSchemaBuilder();
47
48 1
        $table = $this->getTable();
49 1
50 1
        if (!$builder->hasTable($table)) {
51
            return [];
52 1
        }
53 1
54 1
        $columns = $builder->getColumnListing($table);
55
        $mask = $this->metadataMask();
56 1
        $columns = array_intersect($columns, $mask);
57
58 1
        $tableData = [];
59 1
60 1
        $rawFoo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
61 1
        $foo = [];
62 1
        $getters = $this->collectGetters();
63 1
64 1
        foreach ($rawFoo as $key => $val) {
65
            // Work around glitch in Doctrine when reading from MariaDB which added ` characters to root key value
66 1
            $key = trim($key, '`');
67
            $foo[$key] = $val;
68
        }
69
70
        foreach ($columns as $column) {
71
            // Doctrine schema manager returns columns with lowercased names
72
            $rawColumn = $foo[strtolower($column)];
73 4
            $nullable = !($rawColumn->getNotNull());
74
            $fillable = in_array($column, $this->getFillable());
75 4
            $rawType = $rawColumn->getType();
76
            $type = $rawType->getName();
77 4
            $default = $this->$column;
78 4
            $tableData[$column] = ['type' => $type,
79 4
                'nullable' => $nullable,
80 2
                'fillable' => $fillable,
81 4
                'default' => $default
82 1
            ];
83 1
        }
84
85 4
        foreach ($getters as $get) {
86
            if (isset($tableData[$get])) {
87
                continue;
88
            }
89
            $default = $this->$get;
90
            $tableData[$get] = ['type' => 'text', 'nullable' => true, 'fillable' => false, 'default' => $default];
91 5
        }
92
93 5
        return $tableData;
94 5
    }
95 1
96
    /*
97
     * Return the set of fields that are permitted to be in metadata
98 4
     * - following same visible-trumps-hidden guideline as Laravel
99
     */
100 4
    public function metadataMask()
101
    {
102 4
        $attribs = array_keys($this->getAllAttributes());
103 4
104 4
        $visible = $this->getVisible();
105 4
        $hidden = $this->getHidden();
106 4
        if (0 < count($visible)) {
107 4
            assert(!empty($visible));
108
            $attribs = array_intersect($visible, $attribs);
109 4
        } elseif (0 < count($hidden)) {
110 1
            assert(!empty($hidden));
111 1
            $attribs = array_diff($attribs, $hidden);
112 1
        }
113 1
114
        return $attribs;
115 4
    }
116 4
117
    /*
118 4
     * Get the endpoint name being exposed
119
     *
120
     */
121
    public function getEndpointName()
122
    {
123
        $endpoint = isset($this->endpoint) ? $this->endpoint : null;
124
125
        if (!isset($endpoint)) {
126
            $bitter = get_class();
127
            $name = substr($bitter, strrpos($bitter, '\\')+1);
128
            return strtolower($name);
129
        }
130
        return strtolower($endpoint);
131
    }
132
133
    /*
134
     * Assemble this model's OData metadata as xml schema
135
     *
136
     * @return ResourceEntityType
137
     */
138
    public function getXmlSchema($metaNamespace = 'Data')
139
    {
140
        $raw = $this->metadata();
141
        if ([] == $raw) {
142
            return null;
143
        }
144
145
        $metadata = App::make('metadata');
146
147
        $reflec = new \ReflectionClass(get_class($this));
148
        $complex = $metadata->addEntityType($reflec, $reflec->getShortName(), $metaNamespace);
149
        $keyName = $this->getKeyName();
150
        if (null != $keyName) {
151
            $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
152
        }
153
154
        foreach ($raw as $key => $secret) {
155
            if ($key == $keyName) {
156
                continue;
157
            }
158
            if ($secret['type'] == 'blob') {
159
                $complex->setMediaLinkEntry(true);
160
                $streamInfo = new ResourceStreamInfo($key);
161
                assert($complex->isMediaLinkEntry());
162
                $complex->addNamedStream($streamInfo);
163
                continue;
164
            }
165
            $nullable = $secret['nullable'];
166
            $default = $secret['default'];
167
            // tag as isBag?
168
            $metadata->addPrimitiveProperty(
169
                $complex,
170
                $key,
171
                $this->mapping[$secret['type']],
172
                false,
173
                $default,
174
                $nullable
175 3
            );
176
        }
177 3
178
        return $complex;
179 3
    }
180 3
181 3
    public function hookUpRelationships($entityTypes, $resourceSets)
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...
182 3
    {
183 3
        assert(is_array($entityTypes) && is_array($resourceSets), 'Both entityTypes and resourceSets must be arrays');
184 3
        $metadata = App::make('metadata');
185 3
        $rel = $this->getRelationshipsFromMethods();
186 3
        $thisClass = get_class($this);
187 3
        $thisInTypes = array_key_exists($thisClass, $entityTypes);
188 3
        $thisInSets = array_key_exists($thisClass, $resourceSets);
189
190 3
        if (!($thisInSets && $thisInTypes)) {
191
            return $rel;
192 3
        }
193 3
194 3
        $resourceType = $entityTypes[$thisClass];
195 3
        // if $r is in $combined keys, then its in keyspaces of both $entityTypes and $resourceSets
196 3
        $combinedKeys = array_intersect(array_keys($entityTypes), array_keys($resourceSets));
197 3 View Code Duplication
        foreach ($rel['HasOne'] as $n => $r) {
198 3
            $r = trim($r, '\\');
199 3
            if (in_array($r, $combinedKeys)) {
200 3
                $targResourceSet = $resourceSets[$r];
201 3
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
202
            }
203 3
        }
204 3 View Code Duplication
        foreach ($rel['HasMany'] as $n => $r) {
205 3
            $r = trim($r, '\\');
206 3
            if (in_array($r, $combinedKeys)) {
207 3
                $targResourceSet = $resourceSets[$r];
208 3
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
209 3
            }
210 3
        }
211
        return $rel;
212 3
    }
213 3
214 3
    /**
215
     * Get model's relationships
216 3
     *
217 3
     * @return array
218 3
     */
219 3
    public function getRelationships()
220 3
    {
221
        if (empty(static::$relationHooks)) {
222 1
            $hooks = [];
223 3
224
            $rels = $this->getRelationshipsFromMethods(true);
225 1
226
            $this->getRelationshipsUnknownPolyMorph($rels, $hooks);
227 1
228
            $this->getRelationshipsKnownPolyMorph($rels, $hooks);
229 1
230
            $this->getRelationshipsHasOne($rels, $hooks);
231 3
232 2
            $this->getRelationshipsHasMany($rels, $hooks);
233 2
234 3
            static::$relationHooks = $hooks;
235 3
        }
236 3
237 3
        return static::$relationHooks;
238 3
    }
239 3
240 3
    protected function getAllAttributes()
241
    {
242
        // Adapted from http://stackoverflow.com/a/33514981
243
        // $columns = $this->getFillable();
244
        // Another option is to get all columns for the table like so:
245
        $builder = $this->getConnection()->getSchemaBuilder();
246
        $columns = $builder->getColumnListing($this->getTable());
247
        // but it's safer to just get the fillable fields
248
249
        $attributes = $this->getAttributes();
250
251
        foreach ($columns as $column) {
252
            if (!array_key_exists($column, $attributes)) {
253
                $attributes[$column] = null;
254
            }
255
        }
256
257
        $methods = $this->collectGetters();
258
259
        foreach ($methods as $method) {
260
            $attributes[$method] = null;
261
        }
262
263
        return $attributes;
264
    }
265
266
    protected function getRelationshipsFromMethods($biDir = false)
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...
267
    {
268
        $biDirVal = intval($biDir);
269
        $isCached = isset(static::$relationCategories[$biDirVal]) && !empty(static::$relationCategories[$biDirVal]);
270
        if (!$isCached) {
271
            $model = $this;
272
            $relationships = [
273
                'HasOne' => [],
274
                'UnknownPolyMorphSide' => [],
275
                'HasMany' => [],
276
                'KnownPolyMorphSide' => []
277
            ];
278
            $methods = get_class_methods($model);
279
            if (!empty($methods)) {
280
                foreach ($methods as $method) {
281
                    if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
282
                    ) {
283
                        //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
284
                        $reflection = new \ReflectionMethod($model, $method);
285
286
                        $file = new \SplFileObject($reflection->getFileName());
287
                        $file->seek($reflection->getStartLine() - 1);
288
                        $code = '';
289
                        while ($file->key() < $reflection->getEndLine()) {
290
                            $code .= $file->current();
291
                            $file->next();
292
                        }
293
294
                        $code = trim(preg_replace('/\s\s+/', '', $code));
295
                        assert(
296
                            false !== stripos($code, 'function'),
297
                            'Function definition must have keyword \'function\''
298
                        );
299
                        $begin = strpos($code, 'function(');
300
                        $code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
301
                        $lastCode = $code[strlen($code) - 1];
302
                        assert('}' == $lastCode, 'Final character of function definition must be closing brace');
303
                        foreach ([
304
                                     'hasMany',
305
                                     'hasManyThrough',
306
                                     'belongsToMany',
307
                                     'hasOne',
308
                                     'belongsTo',
309
                                     'morphOne',
310
                                     'morphTo',
311
                                     'morphMany',
312
                                     'morphToMany',
313
                                     'morphedByMany'
314
                                 ] as $relation) {
315
                            $search = '$this->' . $relation . '(';
316
                            if ($pos = stripos($code, $search)) {
317
                                //Resolve the relation's model to a Relation object.
318
                                $relationObj = $model->$method();
319
                                if ($relationObj instanceof Relation) {
320
                                    $relatedModel = '\\' . get_class($relationObj->getRelated());
321
                                    $relations = [
322
                                        'hasManyThrough',
323
                                        'belongsToMany',
324
                                        'hasMany',
325
                                        'morphMany',
326
                                        'morphToMany',
327
                                        'morphedByMany'
328
                                    ];
329
                                    if (in_array($relation, $relations)) {
330
                                        //Collection or array of models (because Collection is Arrayable)
331
                                        $relationships['HasMany'][$method] = $biDir ? $relationObj : $relatedModel;
332
                                    } elseif ('morphTo' === $relation) {
333
                                        // Model isn't specified because relation is polymorphic
334
                                        $relationships['UnknownPolyMorphSide'][$method] =
335
                                            $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
336
                                    } else {
337
                                        //Single model is returned
338
                                        $relationships['HasOne'][$method] = $biDir ? $relationObj : $relatedModel;
339
                                    }
340
                                    if (in_array($relation, ['morphMany', 'morphOne', 'morphToMany'])) {
341
                                        $relationships['KnownPolyMorphSide'][$method] =
342
                                            $biDir ? $relationObj : $relatedModel;
343
                                    }
344
                                    if (in_array($relation, ['morphedByMany'])) {
345
                                        $relationships['UnknownPolyMorphSide'][$method] =
346
                                            $biDir ? $relationObj : $relatedModel;
347
                                    }
348
                                }
349
                            }
350
                        }
351
                    }
352
                }
353
            }
354
            static::$relationCategories[$biDirVal] = $relationships;
355
        }
356
        return static::$relationCategories[$biDirVal];
357
    }
358
359
    /**
360
     * Get the visible attributes for the model.
361
     *
362
     * @return array
363
     */
364
    abstract public function getVisible();
365
366
    /**
367
     * Get the hidden attributes for the model.
368
     *
369
     * @return array
370
     */
371
    abstract public function getHidden();
372
373
    /**
374
     * Get the primary key for the model.
375
     *
376
     * @return string
377
     */
378
    abstract public function getKeyName();
379
380
    /**
381
     * Get the current connection name for the model.
382
     *
383
     * @return string
384
     */
385
    abstract public function getConnectionName();
386
387
    /**
388
     * Get the database connection for the model.
389
     *
390
     * @return \Illuminate\Database\Connection
391
     */
392
    abstract public function getConnection();
393
394
    /**
395
     * Get all of the current attributes on the model.
396
     *
397
     * @return array
398
     */
399
    abstract public function getAttributes();
400
401
    /**
402
     * Get the table associated with the model.
403
     *
404
     * @return string
405
     */
406
    abstract public function getTable();
407
408
    /**
409
     * Get the fillable attributes for the model.
410
     *
411
     * @return array
412
     */
413
    abstract public function getFillable();
414
415
    /**
416
     * Dig up all defined getters on the model
417
     *
418
     * @return array
419
     */
420
    protected function collectGetters()
421
    {
422
        $getterz = [];
423
        $methods = get_class_methods($this);
424
        foreach ($methods as $method) {
425
            if (12 < strlen($method) && 'get' == substr($method, 0, 3)) {
426
                if ('Attribute' == substr($method, -9)) {
427
                    $getterz[] = $method;
428
                }
429
            }
430
        }
431
        $methods = [];
432
433
        foreach ($getterz as $getter) {
434
            $residual = substr($getter, 3);
435
            $residual = substr($residual, 0, -9);
436
            $methods[] = $residual;
437
        }
438
        return $methods;
439
    }
440
441
    /**
442
     * @param $foo
443
     * @return array
444
     */
445
    private function polyglotKeyMethodNames($foo, $condition = false)
446
    {
447
        $fkList = ['getQualifiedForeignKeyName', 'getForeignKey'];
448
        $rkList = ['getQualifiedRelatedKeyName', 'getOtherKey', 'getOwnerKey'];
449
450
        $fkMethodName = null;
451
        $rkMethodName = null;
452 View Code Duplication
        if ($condition) {
453
            if (array_key_exists(get_class($foo), static::$methodPrimary)) {
454
                $line = static::$methodPrimary[get_class($foo)];
455
                $fkMethodName = $line['fk'];
456
                $rkMethodName = $line['rk'];
457
            } else {
458
                $methodList = get_class_methods(get_class($foo));
459
                $fkMethodName = 'getQualifiedForeignPivotKeyName';
460
                foreach ($fkList as $option) {
461
                    if (in_array($option, $methodList)) {
462
                        $fkMethodName = $option;
463
                        break;
464
                    }
465
                }
466
                assert(in_array($fkMethodName, $methodList), 'Selected method, '.$fkMethodName.', not in method list');
467
                $rkMethodName = 'getQualifiedRelatedPivotKeyName';
468
                foreach ($rkList as $option) {
469
                    if (in_array($option, $methodList)) {
470
                        $rkMethodName = $option;
471
                        break;
472
                    }
473
                }
474
                assert(in_array($rkMethodName, $methodList), 'Selected method, '.$rkMethodName.', not in method list');
475
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
476
                static::$methodPrimary[get_class($foo)] = $line;
477
            }
478
        }
479
        return [$fkMethodName, $rkMethodName];
480
    }
481
482
    private function polyglotKeyMethodBackupNames($foo, $condition = false)
483
    {
484
        $fkList = ['getForeignKey'];
485
        $rkList = ['getOtherKey'];
486
487
        $fkMethodName = null;
488
        $rkMethodName = null;
489 View Code Duplication
        if ($condition) {
490
            if (array_key_exists(get_class($foo), static::$methodAlternate)) {
491
                $line = static::$methodAlternate[get_class($foo)];
492
                $fkMethodName = $line['fk'];
493
                $rkMethodName = $line['rk'];
494
            } else {
495
                $methodList = get_class_methods(get_class($foo));
496
                $fkMethodName = 'getForeignKeyName';
497
                foreach ($fkList as $option) {
498
                    if (in_array($option, $methodList)) {
499
                        $fkMethodName = $option;
500
                        break;
501
                    }
502
                }
503
                assert(in_array($fkMethodName, $methodList), 'Selected method, '.$fkMethodName.', not in method list');
504
                $rkMethodName = 'getQualifiedParentKeyName';
505
                foreach ($rkList as $option) {
506
                    if (in_array($option, $methodList)) {
507
                        $rkMethodName = $option;
508
                        break;
509
                    }
510
                }
511
                assert(in_array($rkMethodName, $methodList), 'Selected method, '.$rkMethodName.', not in method list');
512
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
513
                static::$methodAlternate[get_class($foo)] = $line;
514
            }
515
        }
516
        return [$fkMethodName, $rkMethodName];
517
    }
518
519
    /**
520
     * @param $hooks
521
     * @param $first
522
     * @param $property
523
     * @param $last
524
     * @param $mult
525
     * @param $targ
526
     */
527
    private function addRelationsHook(&$hooks, $first, $property, $last, $mult, $targ)
528
    {
529
        if (!isset($hooks[$first])) {
530
            $hooks[$first] = [];
531
        }
532
        $hooks[$first][$targ] = [
533
            'property' => $property,
534
            'local' => $last,
535
            'multiplicity' => $mult
536
        ];
537
    }
538
539
    /**
540
     * @param $rels
541
     * @param $hooks
542
     */
543
    private function getRelationshipsHasMany($rels, &$hooks)
544
    {
545
        foreach ($rels['HasMany'] as $property => $foo) {
546
            if ($foo instanceof MorphMany || $foo instanceof MorphToMany) {
547
                continue;
548
            }
549
            $isBelong = $foo instanceof BelongsToMany;
550
            $mult = '*';
551
            $targ = get_class($foo->getRelated());
552
553
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
554
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
555
556
            $keyRaw = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
557
            $keySegments = explode('.', $keyRaw);
558
            $keyName = $keySegments[count($keySegments) - 1];
559
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
560
            $localSegments = explode('.', $localRaw);
561
            $localName = $localSegments[count($localSegments) - 1];
562
            $first = $keyName;
563
            $last = $localName;
564
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
565
        }
566
    }
567
568
    /**
569
     * @param $rels
570
     * @param $hooks
571
     */
572
    private function getRelationshipsHasOne($rels, &$hooks)
573
    {
574
        foreach ($rels['HasOne'] as $property => $foo) {
575
            if ($foo instanceof MorphOne) {
576
                continue;
577
            }
578
            $isBelong = $foo instanceof BelongsTo;
579
            $mult = $isBelong ? '1' : '0..1';
580
            $targ = get_class($foo->getRelated());
581
582
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
583
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
584
585
            $keyName = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
586
            $keySegments = explode('.', $keyName);
587
            $keyName = $keySegments[count($keySegments) - 1];
588
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
589
            $localSegments = explode('.', $localRaw);
590
            $localName = $localSegments[count($localSegments) - 1];
591
            $first = $isBelong ? $localName : $keyName;
592
            $last = $isBelong ? $keyName : $localName;
593
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
594
        }
595
    }
596
597
    /**
598
     * @param $rels
599
     * @param $hooks
600
     */
601
    private function getRelationshipsKnownPolyMorph($rels, &$hooks)
602
    {
603
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
604
            $isMany = $foo instanceof MorphToMany;
605
            $targ = get_class($foo->getRelated());
606
            $mult = $isMany ? '*' : $foo instanceof MorphMany ? '*' : '1';
607
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
608
609
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
610
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
611
612
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
613
            $keySegments = explode('.', $keyRaw);
614
            $keyName = $keySegments[count($keySegments) - 1];
615
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
616
            $localSegments = explode('.', $localRaw);
617
            $localName = $localSegments[count($localSegments) - 1];
618
            $first = $isMany ? $keyName : $localName;
619
            $last = $isMany ? $localName : $keyName;
620
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
621
        }
622
    }
623
624
    /**
625
     * @param $rels
626
     * @param $hooks
627
     */
628
    private function getRelationshipsUnknownPolyMorph($rels, &$hooks)
629
    {
630
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
631
            $isMany = $foo instanceof MorphToMany;
632
            $targ = get_class($foo->getRelated());
633
            $mult = $isMany ? '*' : '1';
634
635
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
636
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
637
638
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
639
            $keySegments = explode('.', $keyRaw);
640
            $keyName = $keySegments[count($keySegments) - 1];
641
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
642
            $localSegments = explode('.', $localRaw);
643
            $localName = $localSegments[count($localSegments) - 1];
644
645
            $first = $keyName;
646
            $last = (isset($localName) && '' != $localName) ? $localName : $foo->getRelated()->getKeyName();
647
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
648
        }
649
    }
650
651
    /**
652
     * SUpplemental function to retrieve cast array for Laravel versions that do not supply hasCasts
653
     *
654
     * @return array
655
     */
656
    public function retrieveCasts()
657
    {
658
        return $this->casts;
1 ignored issue
show
Bug introduced by
The property casts does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
659
    }
660
661
    /**
662
     * Return list of relations to be eager-loaded by Laravel query provider
663
     *
664
     * @return array
665
     */
666
    public function getEagerLoad()
667
    {
668
        assert(is_array($this->loadEagerRelations));
669
        return $this->loadEagerRelations;
670
    }
671
672
    /**
673
     * Set list of relations to be eager-loaded
674
     *
675
     * @param array $relations
676
     */
677
    public function setEagerLoad(array $relations)
678
    {
679
        $check = array_map('strval', $relations);
680
        assert($relations == $check, 'All supplied relations must be resolvable to strings');
681
        $this->loadEagerRelations = $relations;
682
    }
683
684
    /*
685
     * Is this model the known side of at least one polymorphic relation?
686
     */
687
    public function isKnownPolymorphSide()
688
    {
689
        $rels = $this->getRelationshipsFromMethods();
690
        return !empty($rels['UnknownPolyMorphSide']);
691
    }
692
693
    /*
694
     * Is this model on the unknown side of at least one polymorphic relation?
695
     */
696
    public function isUnknownPolymorphSide()
697
    {
698
        $rels = $this->getRelationshipsFromMethods();
699
        return !empty($rels['KnownPolyMorphSide']);
700
    }
701
}
702