Completed
Push — master ( e5c932...e18320 )
by Alex
02:04
created

MetadataTrait::getRelationshipsFromMethods()   D

Complexity

Conditions 20
Paths 6

Size

Total Lines 95
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 50
CRAP Score 20

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 95
ccs 50
cts 50
cp 1
rs 4.7294
cc 20
eloc 70
nc 6
nop 1
crap 20

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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