Test Setup Failed
Pull Request — master (#68)
by Alex
02:38
created

MetadataTrait::getEagerLoad()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 2
1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use Illuminate\Database\Eloquent\Relations\BelongsTo;
5
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
6
use Illuminate\Database\Eloquent\Relations\MorphMany;
7
use Illuminate\Database\Eloquent\Relations\MorphOne;
8
use Illuminate\Database\Eloquent\Relations\MorphToMany;
9
use Illuminate\Support\Facades\App as App;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use POData\Providers\Metadata\ResourceStreamInfo;
12
use POData\Providers\Metadata\Type\EdmPrimitiveType;
13
use Illuminate\Database\Eloquent\Model;
14
15
trait MetadataTrait
16
{
17
    protected static $methodPrimary = [];
18
    protected static $methodAlternate = [];
19
    protected $loadEagerRelations = [];
20
21
    /*
22
     * Array to record mapping between doctrine types and OData types
23
     */
24
    protected $mapping = [
25
        'integer' => EdmPrimitiveType::INT32,
26
        'string' => EdmPrimitiveType::STRING,
27
        'datetime' => EdmPrimitiveType::DATETIME,
28 3
        'float' => EdmPrimitiveType::SINGLE,
29
        'decimal' => EdmPrimitiveType::DECIMAL,
30 3
        'text' => EdmPrimitiveType::STRING,
31
        'boolean' => EdmPrimitiveType::BOOLEAN,
32
        'blob' => "stream"
33 2
    ];
34 2
35
    /*
36 2
     * Retrieve and assemble this model's metadata for OData packaging
37
     */
38 2
    public function metadata()
39 1
    {
40
        assert($this instanceof Model, get_class($this));
41
42 1
        // Break these out separately to enable separate reuse
43 1
        $connect = $this->getConnection();
44 1
        $builder = $connect->getSchemaBuilder();
45
46 1
        $table = $this->getTable();
47
48 1
        if (!$builder->hasTable($table)) {
49 1
            return [];
50 1
        }
51
52 1
        $columns = $builder->getColumnListing($table);
53 1
        $mask = $this->metadataMask();
54 1
        $columns = array_intersect($columns, $mask);
55
56 1
        $tableData = [];
57
58 1
        $rawFoo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
59 1
        $foo = [];
60 1
        $getters = $this->collectGetters();
61 1
62 1
        foreach ($rawFoo as $key => $val) {
63 1
            // Work around glitch in Doctrine when reading from MariaDB which added ` characters to root key value
64 1
            $key = trim($key, '`');
65
            $foo[$key] = $val;
66 1
        }
67
68
        foreach ($columns as $column) {
69
            // Doctrine schema manager returns columns with lowercased names
70
            $rawColumn = $foo[strtolower($column)];
71
            $nullable = !($rawColumn->getNotNull());
72
            $fillable = in_array($column, $this->getFillable());
73 4
            $rawType = $rawColumn->getType();
74
            $type = $rawType->getName();
75 4
            $default = $this->$column;
76
            $tableData[$column] = ['type' => $type,
77 4
                'nullable' => $nullable,
78 4
                'fillable' => $fillable,
79 4
                'default' => $default
80 2
            ];
81 4
        }
82 1
83 1
        foreach ($getters as $get) {
84
            if (isset($tableData[$get])) {
85 4
                continue;
86
            }
87
            $default = $this->$get;
88
            $tableData[$get] = ['type' => 'text', 'nullable' => true, 'fillable' => false, 'default' => $default];
89
        }
90
91 5
        return $tableData;
92
    }
93 5
94 5
    /*
95 1
     * Return the set of fields that are permitted to be in metadata
96
     * - following same visible-trumps-hidden guideline as Laravel
97
     */
98 4
    public function metadataMask()
99
    {
100 4
        $attribs = array_keys($this->getAllAttributes());
101
102 4
        $visible = $this->getVisible();
103 4
        $hidden = $this->getHidden();
104 4
        if (0 < count($visible)) {
105 4
            assert(!empty($visible));
106 4
            $attribs = array_intersect($visible, $attribs);
107 4
        } elseif (0 < count($hidden)) {
108
            assert(!empty($hidden));
109 4
            $attribs = array_diff($attribs, $hidden);
110 1
        }
111 1
112 1
        return $attribs;
113 1
    }
114
115 4
    /*
116 4
     * Get the endpoint name being exposed
117
     *
118 4
     */
119
    public function getEndpointName()
120
    {
121
        $endpoint = isset($this->endpoint) ? $this->endpoint : null;
122
123
        if (!isset($endpoint)) {
124
            $bitter = get_class();
125
            $name = substr($bitter, strrpos($bitter, '\\')+1);
126
            return strtolower($name);
127
        }
128
        return strtolower($endpoint);
129
    }
130
131
    /*
132
     * Assemble this model's OData metadata as xml schema
133
     */
134
    public function getXmlSchema($MetaNamespace = "Data")
135
    {
136
        $raw = $this->metadata();
137
        if ([] == $raw) {
138
            return null;
139
        }
140
141
        $metadata = App::make('metadata');
142
143
        $rf = new \ReflectionClass(get_class($this));
144
        $complex = $metadata->addEntityType($rf, $rf->getShortName(), $MetaNamespace);
145
        $keyName = $this->getKeyName();
146
        if (null != $keyName) {
147
            $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
148
        }
149
150
        foreach ($raw as $key => $secret) {
151
            if ($key == $keyName) {
152
                continue;
153
            }
154
            if ($secret['type'] == "blob") {
155
                $complex->setMediaLinkEntry(true);
156
                $streamInfo = new ResourceStreamInfo($key);
157
                assert($complex->isMediaLinkEntry());
158
                $complex->addNamedStream($streamInfo);
159
                continue;
160
            }
161
            $nullable = $secret['nullable'];
162
            $default = $secret['default'];
163
            // tag as isBag?
164
            $metadata->addPrimitiveProperty(
165
                $complex,
166
                $key,
167
                $this->mapping[$secret['type']],
168
                false,
169
                $default,
170
                $nullable
171
            );
172
        }
173
174
        return $complex;
175 3
    }
176
177 3
    public function hookUpRelationships($entityTypes, $resourceSets)
178
    {
179 3
        assert(is_array($entityTypes) && is_array($resourceSets), "Both entityTypes and resourceSets must be arrays");
180 3
        $metadata = App::make('metadata');
181 3
        $rel = $this->getRelationshipsFromMethods();
182 3
        $thisClass = get_class($this);
183 3
        $thisInTypes = array_key_exists($thisClass, $entityTypes);
184 3
        $thisInSets = array_key_exists($thisClass, $resourceSets);
185 3
186 3
        if (!($thisInSets && $thisInTypes)) {
187 3
            return $rel;
188 3
        }
189
190 3
        $resourceType = $entityTypes[$thisClass];
191
        // if $r is in $combined keys, then its in keyspaces of both $entityTypes and $resourceSets
192 3
        $combinedKeys = array_intersect(array_keys($entityTypes), array_keys($resourceSets));
193 3 View Code Duplication
        foreach ($rel["HasOne"] as $n => $r) {
194 3
            $r = trim($r, '\\');
195 3
            if (in_array($r, $combinedKeys)) {
196 3
                $targResourceSet = $resourceSets[$r];
197 3
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
198 3
            }
199 3
        }
200 3 View Code Duplication
        foreach ($rel["HasMany"] as $n => $r) {
201 3
            $r = trim($r, '\\');
202
            if (in_array($r, $combinedKeys)) {
203 3
                $targResourceSet = $resourceSets[$r];
204 3
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
205 3
            }
206 3
        }
207 3
        return $rel;
208 3
    }
209 3
210 3
    public function getRelationships()
211
    {
212 3
        $hooks = [];
213 3
214 3
        $rels = $this->getRelationshipsFromMethods(true);
215
216 3
        $this->getRelationshipsUnknownPolyMorph($rels, $hooks);
217 3
218 3
        $this->getRelationshipsKnownPolyMorph($rels, $hooks);
219 3
220 3
        $this->getRelationshipsHasOne($rels, $hooks);
221
222 1
        $this->getRelationshipsHasMany($rels, $hooks);
223 3
224
        return $hooks;
225 1
    }
226
227 1
    protected function getAllAttributes()
228
    {
229 1
        // Adapted from http://stackoverflow.com/a/33514981
230
        // $columns = $this->getFillable();
231 3
        // Another option is to get all columns for the table like so:
232 2
        $builder = $this->getConnection()->getSchemaBuilder();
233 2
        $columns = $builder->getColumnListing($this->getTable());
234 3
        // but it's safer to just get the fillable fields
235 3
236 3
        $attributes = $this->getAttributes();
237 3
238 3
        foreach ($columns as $column) {
239 3
            if (!array_key_exists($column, $attributes)) {
240 3
                $attributes[$column] = null;
241
            }
242
        }
243
244
        $methods = $this->collectGetters();
245
246
        foreach ($methods as $method) {
247
            $attributes[$method] = null;
248
        }
249
250
        return $attributes;
251
    }
252
253
    protected function getRelationshipsFromMethods($biDir = false)
254
    {
255
        $model = $this;
256
        $relationships = array(
257
            "HasOne" => array(),
258
            "UnknownPolyMorphSide"=>array(),
259
            "HasMany"=>array(),
260
            "KnownPolyMorphSide"=>array()
261
        );
262
        $methods = get_class_methods($model);
263
        if (!empty($methods)) {
264
            foreach ($methods as $method) {
265
                if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
266
                ) {
267
                    //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
268
                    $reflection = new \ReflectionMethod($model, $method);
269
270
                    $file = new \SplFileObject($reflection->getFileName());
271
                    $file->seek($reflection->getStartLine()-1);
272
                    $code = '';
273
                    while ($file->key() < $reflection->getEndLine()) {
274
                        $code .= $file->current();
275
                        $file->next();
276
                    }
277
278
                    $code = trim(preg_replace('/\s\s+/', '', $code));
279
                    assert(false !== stripos($code, 'function'), 'Function definition must have keyword \'function\'');
280
                    $begin = strpos($code, 'function(');
281
                    $code = substr($code, $begin, strrpos($code, '}')-$begin+1);
282
                    $lastCode = $code[strlen($code)-1];
283
                    assert("}" == $lastCode, "Final character of function definition must be closing brace");
284
                    foreach (array(
285
                                'hasMany',
286
                                'hasManyThrough',
287
                                'belongsToMany',
288
                                'hasOne',
289
                                'belongsTo',
290
                                'morphOne',
291
                                'morphTo',
292
                                'morphMany',
293
                                'morphToMany',
294
                                'morphedByMany'
295
                                ) as $relation) {
296
                        $search = '$this->'.$relation.'(';
297
                        if ($pos = stripos($code, $search)) {
298
                            //Resolve the relation's model to a Relation object.
299
                            $relationObj = $model->$method();
300
                            if ($relationObj instanceof Relation) {
301
                                $relatedModel = '\\'.get_class($relationObj->getRelated());
302
                                $relations = [
303
                                    'hasManyThrough',
304
                                    'belongsToMany',
305
                                    'hasMany',
306
                                    'morphMany',
307
                                    'morphToMany',
308
                                    'morphedByMany'
309
                                ];
310
                                if (in_array($relation, $relations)) {
311
                                    //Collection or array of models (because Collection is Arrayable)
312
                                    $relationships["HasMany"][$method] = $biDir ? $relationObj : $relatedModel;
313
                                } elseif ($relation === "morphTo") {
314
                                    // Model isn't specified because relation is polymorphic
315
                                    $relationships["UnknownPolyMorphSide"][$method] =
316
                                        $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
317
                                } else {
318
                                    //Single model is returned
319
                                    $relationships["HasOne"][$method] = $biDir ? $relationObj : $relatedModel;
320
                                }
321
                                if (in_array($relation, ["morphMany", "morphOne", "morphedByMany"])) {
322
                                    $relationships["KnownPolyMorphSide"][$method] =
323
                                        $biDir ? $relationObj : $relatedModel;
324
                                }
325
                                if (in_array($relation, ["morphToMany"])) {
326
                                    $relationships["UnknownPolyMorphSide"][$method] =
327
                                        $biDir ? $relationObj : $relatedModel;
328
                                }
329
                            }
330
                        }
331
                    }
332
                }
333
            }
334
        }
335
        return $relationships;
336
    }
337
338
    /**
339
     * Get the visible attributes for the model.
340
     *
341
     * @return array
342
     */
343
    abstract public function getVisible();
344
345
    /**
346
     * Get the hidden attributes for the model.
347
     *
348
     * @return array
349
     */
350
    abstract public function getHidden();
351
352
    /**
353
     * Get the primary key for the model.
354
     *
355
     * @return string
356
     */
357
    abstract public function getKeyName();
358
359
    /**
360
     * Get the current connection name for the model.
361
     *
362
     * @return string
363
     */
364
    abstract public function getConnectionName();
365
366
    /**
367
     * Get the database connection for the model.
368
     *
369
     * @return \Illuminate\Database\Connection
370
     */
371
    abstract public function getConnection();
372
373
    /**
374
     * Get all of the current attributes on the model.
375
     *
376
     * @return array
377
     */
378
    abstract public function getAttributes();
379
380
    /**
381
     * Get the table associated with the model.
382
     *
383
     * @return string
384
     */
385
    abstract public function getTable();
386
387
    /**
388
     * Get the fillable attributes for the model.
389
     *
390
     * @return array
391
     */
392
    abstract public function getFillable();
393
394
    /**
395
     * Dig up all defined getters on the model
396
     *
397
     * @return array
398
     */
399
    protected function collectGetters()
400
    {
401
        $getterz = [];
402
        $methods = get_class_methods($this);
403
        foreach ($methods as $method) {
404
            if (12 < strlen($method) && 'get' == substr($method, 0, 3)) {
405
                if ('Attribute' == substr($method, -9)) {
406
                    $getterz[] = $method;
407
                }
408
            }
409
        }
410
        $methods = [];
411
412
        foreach ($getterz as $getter) {
413
            $residual = substr($getter, 3);
414
            $residual = substr($residual, 0, -9);
415
            $methods[] = $residual;
416
        }
417
        return $methods;
418
    }
419
420
    /**
421
     * @param $foo
422
     * @return array
423
     */
424
    private function polyglotKeyMethodNames($foo, $condition = false)
425
    {
426
        $fkList = ['getQualifiedForeignKeyName', 'getForeignKey'];
427
        $rkList = ['getQualifiedRelatedKeyName', 'getOtherKey', 'getOwnerKey'];
428
429
        $fkMethodName = null;
430
        $rkMethodName = null;
431 View Code Duplication
        if ($condition) {
432
            if (array_key_exists(get_class($foo), static::$methodPrimary)) {
433
                $line = static::$methodPrimary[get_class($foo)];
434
                $fkMethodName = $line['fk'];
435
                $rkMethodName = $line['rk'];
436
            } else {
437
                $methodList = get_class_methods(get_class($foo));
438
                $fkMethodName = 'getQualifiedForeignPivotKeyName';
439
                foreach ($fkList as $option) {
440
                    if (in_array($option, $methodList)) {
441
                        $fkMethodName = $option;
442
                        break;
443
                    }
444
                }
445
                assert(in_array($fkMethodName, $methodList), "Selected method, ".$fkMethodName.", not in method list");
446
                $rkMethodName = 'getQualifiedRelatedPivotKeyName';
447
                foreach ($rkList as $option) {
448
                    if (in_array($option, $methodList)) {
449
                        $rkMethodName = $option;
450
                        break;
451
                    }
452
                }
453
                assert(in_array($rkMethodName, $methodList), "Selected method, ".$rkMethodName.", not in method list");
454
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
455
                static::$methodPrimary[get_class($foo)] = $line;
456
            }
457
        }
458
        return array($fkMethodName, $rkMethodName);
459
    }
460
461
    private function polyglotKeyMethodBackupNames($foo, $condition = false)
462
    {
463
        $fkList = ['getForeignKey'];
464
        $rkList = ['getOtherKey'];
465
466
        $fkMethodName = null;
467
        $rkMethodName = null;
468 View Code Duplication
        if ($condition) {
469
            if (array_key_exists(get_class($foo), static::$methodAlternate)) {
470
                $line = static::$methodAlternate[get_class($foo)];
471
                $fkMethodName = $line['fk'];
472
                $rkMethodName = $line['rk'];
473
            } else {
474
                $methodList = get_class_methods(get_class($foo));
475
                $fkMethodName = 'getForeignKeyName';
476
                foreach ($fkList as $option) {
477
                    if (in_array($option, $methodList)) {
478
                        $fkMethodName = $option;
479
                        break;
480
                    }
481
                }
482
                assert(in_array($fkMethodName, $methodList), "Selected method, ".$fkMethodName.", not in method list");
483
                $rkMethodName = 'getQualifiedParentKeyName';
484
                foreach ($rkList as $option) {
485
                    if (in_array($option, $methodList)) {
486
                        $rkMethodName = $option;
487
                        break;
488
                    }
489
                }
490
                assert(in_array($rkMethodName, $methodList), "Selected method, ".$rkMethodName.", not in method list");
491
                $line = ['fk' => $fkMethodName, 'rk' => $rkMethodName];
492
                static::$methodAlternate[get_class($foo)] = $line;
493
            }
494
        }
495
        return array($fkMethodName, $rkMethodName);
496
    }
497
498
    /**
499
     * @param $hooks
500
     * @param $first
501
     * @param $property
502
     * @param $last
503
     * @param $mult
504
     * @param $targ
505
     */
506
    private function addRelationsHook(&$hooks, $first, $property, $last, $mult, $targ)
507
    {
508
        if (!isset($hooks[$first])) {
509
            $hooks[$first] = [];
510
        }
511
        $hooks[$first][$targ] = [
512
            'property' => $property,
513
            'local' => $last,
514
            'multiplicity' => $mult
515
        ];
516
    }
517
518
    /**
519
     * @param $rels
520
     * @param $hooks
521
     */
522
    private function getRelationshipsHasMany($rels, &$hooks)
523
    {
524
        foreach ($rels['HasMany'] as $property => $foo) {
525
            if ($foo instanceof MorphMany || $foo instanceof MorphToMany) {
526
                continue;
527
            }
528
            $isBelong = $foo instanceof BelongsToMany;
529
            $mult = '*';
530
            $targ = get_class($foo->getRelated());
531
532
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
533
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
534
535
            $keyRaw = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
536
            $keySegments = explode('.', $keyRaw);
537
            $keyName = $keySegments[count($keySegments) - 1];
538
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
539
            $localSegments = explode('.', $localRaw);
540
            $localName = $localSegments[count($localSegments) - 1];
541
            $first = $keyName;
542
            $last = $localName;
543
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
544
        }
545
    }
546
547
    /**
548
     * @param $rels
549
     * @param $hooks
550
     */
551
    private function getRelationshipsHasOne($rels, &$hooks)
552
    {
553
        foreach ($rels['HasOne'] as $property => $foo) {
554
            if ($foo instanceof MorphOne) {
555
                continue;
556
            }
557
            $isBelong = $foo instanceof BelongsTo;
558
            $mult = $isBelong ? '1' : '0..1';
559
            $targ = get_class($foo->getRelated());
560
561
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isBelong);
562
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isBelong);
563
564
            $keyName = $isBelong ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
565
            $keySegments = explode('.', $keyName);
566
            $keyName = $keySegments[count($keySegments) - 1];
567
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
568
            $localSegments = explode('.', $localRaw);
569
            $localName = $localSegments[count($localSegments) - 1];
570
            $first = $isBelong ? $localName : $keyName;
571
            $last = $isBelong ? $keyName : $localName;
572
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
573
        }
574
    }
575
576
    /**
577
     * @param $rels
578
     * @param $hooks
579
     */
580
    private function getRelationshipsKnownPolyMorph($rels, &$hooks)
581
    {
582
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
583
            $isMany = $foo instanceof MorphToMany;
584
            $targ = get_class($foo->getRelated());
585
            $mult = $isMany ? '*' : $foo instanceof MorphMany ? '*' : '1';
586
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
587
588
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
589
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
590
591
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
592
            $keySegments = explode('.', $keyRaw);
593
            $keyName = $keySegments[count($keySegments) - 1];
594
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
595
            $localSegments = explode('.', $localRaw);
596
            $localName = $localSegments[count($localSegments) - 1];
597
            $first = $isMany ? $keyName : $localName;
598
            $last = $isMany ? $localName : $keyName;
599
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
600
        }
601
    }
602
603
    /**
604
     * @param $rels
605
     * @param $hooks
606
     */
607
    private function getRelationshipsUnknownPolyMorph($rels, &$hooks)
608
    {
609
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
610
            $isMany = $foo instanceof MorphToMany;
611
            $targ = get_class($foo->getRelated());
612
            $mult = $isMany ? '*' : '1';
613
614
            list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo, $isMany);
615
            list($fkMethodAlternate, $rkMethodAlternate) = $this->polyglotKeyMethodBackupNames($foo, !$isMany);
616
617
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->$fkMethodAlternate();
618
            $keySegments = explode('.', $keyRaw);
619
            $keyName = $keySegments[count($keySegments) - 1];
620
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->$rkMethodAlternate();
621
            $localSegments = explode('.', $localRaw);
622
            $localName = $localSegments[count($localSegments) - 1];
623
624
            $first = $keyName;
625
            $last = (isset($localName) && "" != $localName) ? $localName : $foo->getRelated()->getKeyName();
626
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
627
        }
628
    }
629
630
    /**
631
     * SUpplemental function to retrieve cast array for Laravel versions that do not supply hasCasts
632
     *
633
     * @return array
634
     */
635
    public function retrieveCasts()
636
    {
637
        return $this->casts;
1 ignored issue
show
Bug introduced by
The property casts does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
638
    }
639
640
    /**
641
     * Return list of relations to be eager-loaded by Laravel query provider
642
     *
643
     * @return array
644
     */
645
    public function getEagerLoad()
646
    {
647
        assert(is_array($this->loadEagerRelations));
648
        return $this->loadEagerRelations;
649
    }
650
651
    /**
652
     * Set list of relations to be eager-loaded
653
     *
654
     * @param array $relations
655
     */
656
    public function setEagerLoad(array $relations)
657
    {
658
        $check = array_map('strval', $relations);
659
        assert($relations == $check, "All supplied relations must be resolvable to strings");
660
        $this->loadEagerRelations = $relations;
661
    }
662
}
663