Passed
Pull Request — master (#201)
by Alex
06:08
created

MetadataTrait::addRelationsHook()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 10
c 2
b 0
f 0
dl 0
loc 14
ccs 0
cts 0
cp 0
rs 9.9332
cc 3
nc 4
nop 8
crap 12

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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