Completed
Pull Request — master (#135)
by Alex
15:29
created

MetadataTrait::getTableDoctrineColumns()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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