Test Failed
Push — master ( bce4af...5f600c )
by Alex
01:42
created

MetadataTrait::polyglotKeyMethodBackupNames()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 28
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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