Completed
Push — master ( 9e0fa3...a1e1cc )
by Alex
02:05
created

MetadataTrait::synthLiteralPK()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
crap 6
1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubMonomorphic;
5
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubPolymorphic;
6
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\Associations\AssociationStubRelationType;
7
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityField;
8
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityFieldPrimitiveType;
9
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityFieldType;
10
use AlgoWeb\PODataLaravel\Models\ObjectMap\Entities\EntityGubbins;
11
use AlgoWeb\PODataLaravel\Query\LaravelReadQuery;
12
use Illuminate\Database\Eloquent\Model;
13
use Illuminate\Database\Eloquent\Relations\BelongsTo;
14
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
15
use Illuminate\Database\Eloquent\Relations\MorphMany;
16
use Illuminate\Database\Eloquent\Relations\MorphOne;
17
use Illuminate\Database\Eloquent\Relations\MorphToMany;
18
use Illuminate\Database\Eloquent\Relations\Relation;
19
use Mockery\Mock;
20
use POData\Providers\Metadata\Type\EdmPrimitiveType;
21
22
trait MetadataTrait
23
{
24
    protected static $relationHooks = [];
25
    protected static $relationCategories = [];
26
    protected static $methodPrimary = [];
27
    protected static $methodAlternate = [];
28 3
    protected $loadEagerRelations = [];
29
30 3
    /*
31
     * Retrieve and assemble this model's metadata for OData packaging
32
     */
33 2
    public function metadata()
34 2
    {
35
        assert($this instanceof Model, get_class($this));
36 2
37
        // Break these out separately to enable separate reuse
38 2
        $connect = $this->getConnection();
39 1
        $builder = $connect->getSchemaBuilder();
40
41
        $table = $this->getTable();
42 1
43 1
        if (!$builder->hasTable($table)) {
44 1
            return [];
45
        }
46 1
47
        $columns = $builder->getColumnListing($table);
48 1
        $mask = $this->metadataMask();
49 1
        $columns = array_intersect($columns, $mask);
50 1
51
        $tableData = [];
52 1
53 1
        $rawFoo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
54 1
        $foo = [];
55
        $getters = $this->collectGetters();
56 1
57
        foreach ($rawFoo as $key => $val) {
58 1
            // Work around glitch in Doctrine when reading from MariaDB which added ` characters to root key value
59 1
            $key = trim($key, '`');
60 1
            $foo[$key] = $val;
61 1
        }
62 1
63 1
        foreach ($columns as $column) {
64 1
            // Doctrine schema manager returns columns with lowercased names
65
            $rawColumn = $foo[strtolower($column)];
66 1
            $nullable = !($rawColumn->getNotNull());
67
            $fillable = in_array($column, $this->getFillable());
68
            $rawType = $rawColumn->getType();
69
            $type = $rawType->getName();
70
            $default = $this->$column;
71
            $tableData[$column] = ['type' => $type,
72
                'nullable' => $nullable,
73 4
                'fillable' => $fillable,
74
                'default' => $default
75 4
            ];
76
        }
77 4
78 4
        foreach ($getters as $get) {
79 4
            if (isset($tableData[$get])) {
80 2
                continue;
81 4
            }
82 1
            $default = $this->$get;
83 1
            $tableData[$get] = ['type' => 'text', 'nullable' => true, 'fillable' => false, 'default' => $default];
84
        }
85 4
86
        return $tableData;
87
    }
88
89
    /*
90
     * Return the set of fields that are permitted to be in metadata
91 5
     * - following same visible-trumps-hidden guideline as Laravel
92
     */
93 5
    public function metadataMask()
94 5
    {
95 1
        $attribs = array_keys($this->getAllAttributes());
96
97
        $visible = $this->getVisible();
98 4
        $hidden = $this->getHidden();
99
        if (0 < count($visible)) {
100 4
            assert(!empty($visible));
101
            $attribs = array_intersect($visible, $attribs);
102 4
        } elseif (0 < count($hidden)) {
103 4
            assert(!empty($hidden));
104 4
            $attribs = array_diff($attribs, $hidden);
105 4
        }
106 4
107 4
        return $attribs;
108
    }
109 4
110 1
    /*
111 1
     * Get the endpoint name being exposed
112 1
     *
113 1
     */
114
    public function getEndpointName()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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

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