Test Failed
Push — master ( b488b6...028484 )
by Alex
01:42
created

MetadataTrait::metadataMask()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

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