Completed
Branch 5.6 (cd95fb)
by Rémi
10:17
created
src/EntityMap.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -621,7 +621,7 @@
 block discarded – undo
621 621
      *
622 622
      * @throws MappingException
623 623
      *
624
-     * @return mixed
624
+     * @return null|Collection
625 625
      */
626 626
     public function getEmptyValueForRelationship(string $relation)
627 627
     {
Please login to merge, or discard this patch.
Indentation   +1678 added lines, -1678 removed lines patch added patch discarded remove patch
@@ -26,1682 +26,1682 @@
 block discarded – undo
26 26
  */
27 27
 class EntityMap
28 28
 {
29
-    /**
30
-     * The mapping driver to use with this entity.
31
-     *
32
-     * @var string
33
-     */
34
-    protected $driver = 'illuminate';
35
-
36
-    /**
37
-     * The Database Connection name for the model.
38
-     *
39
-     * @var string
40
-     */
41
-    protected $connection;
42
-
43
-    /**
44
-     * The table associated with the entity.
45
-     *
46
-     * @var string
47
-     */
48
-    protected $table;
49
-
50
-    /**
51
-     * The primary key for the model. If the model is an Embedded Value object
52
-     * primary key is set to null.
53
-     *
54
-     * @var string|null
55
-     */
56
-    protected $primaryKey = 'id';
57
-
58
-    /**
59
-     * Name of the entity's array property that should
60
-     * contain the attributes.
61
-     * If set to null, analogue will only hydrate object's properties.
62
-     *
63
-     * @var string|null
64
-     */
65
-    protected $arrayName = 'attributes';
66
-
67
-    /**
68
-     * Array containing the list of database columns to be mapped
69
-     * in the attributes array of the entity.
70
-     *
71
-     * @var array
72
-     */
73
-    protected $attributes = [];
74
-
75
-    /**
76
-     * Array containing the list of database columns to be mapped
77
-     * to the entity's class properties.
78
-     *
79
-     * @var array
80
-     */
81
-    protected $properties = [];
82
-
83
-    /**
84
-     * The Custom Domain Class to use with this mapping.
85
-     *
86
-     * @var string
87
-     */
88
-    protected $class;
89
-
90
-    /**
91
-     * The event map for the entity.
92
-     *
93
-     * @var array
94
-     */
95
-    protected $events = [];
96
-
97
-    /**
98
-     * Embedded Value Objects.
99
-     *
100
-     * @deprecated 5.5 (use embedsOne() or embedsMany() relationships instead)
101
-     *
102
-     * @var array
103
-     */
104
-    protected $embeddables = [];
105
-
106
-    /**
107
-     * Determine the relationships method used on the entity.
108
-     * If not set, mapper will autodetect them.
109
-     *
110
-     * @var array
111
-     */
112
-    protected $relationships = [];
113
-
114
-    /**
115
-     * Relationships that should be treated as collection.
116
-     *
117
-     * @var array
118
-     */
119
-    protected $manyRelations = [];
120
-
121
-    /**
122
-     * Relationships that should be treated as single entity.
123
-     *
124
-     * @var array
125
-     */
126
-    protected $singleRelations = [];
127
-
128
-    /**
129
-     * Relationships for which the key is stored in the Entity itself.
130
-     *
131
-     * @var array
132
-     */
133
-    protected $localRelations = [];
134
-
135
-    /**
136
-     * List of local keys associated to local relation methods.
137
-     *
138
-     * @var array
139
-     */
140
-    protected $localForeignKeys = [];
141
-
142
-    /**
143
-     * Relationships for which the key is stored in the Related Entity.
144
-     *
145
-     * @var array
146
-     */
147
-    protected $foreignRelations = [];
148
-
149
-    /**
150
-     * Relationships which use a pivot record.
151
-     *
152
-     * @var array
153
-     */
154
-    protected $pivotRelations = [];
155
-
156
-    /**
157
-     * Polymorphic relationships.
158
-     *
159
-     * @var array
160
-     */
161
-    protected $polymorphicRelations = [];
162
-
163
-    /**
164
-     * Dynamic relationships.
165
-     *
166
-     * @var array
167
-     */
168
-    protected $dynamicRelationships = [];
169
-
170
-    /**
171
-     * Targeted class for the relationship method. value is set to `null` for
172
-     * polymorphic relations.
173
-     *
174
-     * @var array
175
-     */
176
-    protected $relatedClasses = [];
177
-
178
-    /**
179
-     * Some relation methods like embedded objects, or HasOne and MorphOne,
180
-     * will never have a proxy loaded on them.
181
-     *
182
-     * @var array
183
-     */
184
-    protected $nonProxyRelationships = [];
185
-
186
-    /**
187
-     * Relation methods that are embedded objects.
188
-     *
189
-     * @var array
190
-     */
191
-    protected $embeddedRelations = [];
192
-
193
-    /**
194
-     * The number of models to return for pagination.
195
-     *
196
-     * @var int
197
-     */
198
-    protected $perPage = 15;
199
-
200
-    /**
201
-     * The relations to eager load on every query.
202
-     *
203
-     * @var array
204
-     */
205
-    protected $with = [];
206
-
207
-    /**
208
-     * The class name to be used in polymorphic relations.
209
-     *
210
-     * @var string
211
-     */
212
-    protected $morphClass;
213
-
214
-    /**
215
-     * Indicates if the entity should be timestamped.
216
-     *
217
-     * @var bool
218
-     */
219
-    public $timestamps = false;
220
-
221
-    /**
222
-     * The name of the "created at" attribute.
223
-     *
224
-     * @var string
225
-     */
226
-    protected $createdAtColumn = 'created_at';
227
-
228
-    /**
229
-     * The name of the "updated at" attribute.
230
-     *
231
-     * @var string
232
-     */
233
-    protected $updatedAtColumn = 'updated_at';
234
-
235
-    /**
236
-     * Indicates if the entity uses softdeletes.
237
-     *
238
-     * @var bool
239
-     */
240
-    public $softDeletes = false;
241
-
242
-    /**
243
-     * The name of the "deleted at" attribute.
244
-     *
245
-     * @var string
246
-     */
247
-    protected $deletedAtColumn = 'deleted_at';
248
-
249
-    /**
250
-     * The date format to use with the current database connection.
251
-     *
252
-     * @var string
253
-     */
254
-    protected $dateFormat;
255
-
256
-    /**
257
-     * Set this property to true if the entity should be instantiated
258
-     * using the IoC Container.
259
-     *
260
-     * @var bool
261
-     */
262
-    protected $dependencyInjection = false;
263
-
264
-    /**
265
-     * Set the usage of inheritance, possible values are :
266
-     * "single_table"
267
-     * null.
268
-     *
269
-     * @var string
270
-     */
271
-    protected $inheritanceType;
272
-
273
-    /**
274
-     * Discriminator column name.
275
-     *
276
-     * @var string
277
-     */
278
-    protected $discriminatorColumn = 'type';
279
-
280
-    /**
281
-     * Allow using a string to define which entity type should be instantiated.
282
-     * If not set, analogue will uses entity's FQCN.
283
-     *
284
-     * @var array
285
-     */
286
-    protected $discriminatorColumnMap = [];
287
-
288
-    /**
289
-     * Indicate if the entity map has been booted.
290
-     *
291
-     * @var bool
292
-     */
293
-    private $isBooted = false;
294
-
295
-    /**
296
-     * Return Domain class attributes, useful when mapping to a Plain PHP Object.
297
-     *
298
-     * @return array
299
-     */
300
-    public function getAttributes(): array
301
-    {
302
-        return $this->attributes;
303
-    }
304
-
305
-    /**
306
-     * Set the domain class attributes.
307
-     *
308
-     * @param array $attributeNames
309
-     */
310
-    public function setAttributes(array $attributeNames)
311
-    {
312
-        $this->attributes = $attributeNames;
313
-    }
314
-
315
-    /**
316
-     * Return true if the Entity has an 'attributes' array property.
317
-     *
318
-     * @return bool
319
-     */
320
-    public function usesAttributesArray(): bool
321
-    {
322
-        if ($this->arrayName === null) {
323
-            return false;
324
-        }
325
-
326
-        if ($this->attributes === null) {
327
-            return false;
328
-        }
329
-
330
-        return true;
331
-    }
332
-
333
-    /**
334
-     * Return the name of the Entity's attributes property.
335
-     *
336
-     * @return string|null
337
-     */
338
-    public function getAttributesArrayName()
339
-    {
340
-        return $this->arrayName;
341
-    }
342
-
343
-    /**
344
-     * Get all the attribute names for the class, including relationships, embeddables and primary key.
345
-     *
346
-     * @return array
347
-     */
348
-    public function getCompiledAttributes(): array
349
-    {
350
-        $key = $this->getKeyName();
351
-
352
-        $embeddables = array_keys($this->getEmbeddables());
353
-
354
-        $relationships = $this->getRelationships();
355
-
356
-        $attributes = $this->getAttributes();
357
-
358
-        return array_merge([$key], $embeddables, $relationships, $attributes);
359
-    }
360
-
361
-    /**
362
-     * Set the date format to use with the current database connection.
363
-     *
364
-     * @param string $format
365
-     */
366
-    public function setDateFormat(string $format)
367
-    {
368
-        $this->dateFormat = $format;
369
-    }
370
-
371
-    /**
372
-     * Get the date format to use with the current database connection.
373
-     *
374
-     *  @return string
375
-     */
376
-    public function getDateFormat(): string
377
-    {
378
-        return $this->dateFormat;
379
-    }
380
-
381
-    /**
382
-     * Set the Driver for this mapping.
383
-     *
384
-     * @param string $driver
385
-     */
386
-    public function setDriver(string $driver)
387
-    {
388
-        $this->driver = $driver;
389
-    }
390
-
391
-    /**
392
-     * Get the Driver for this mapping.
393
-     *
394
-     * @return string
395
-     */
396
-    public function getDriver(): string
397
-    {
398
-        return $this->driver;
399
-    }
400
-
401
-    /**
402
-     * Set the db connection to use on the table.
403
-     *
404
-     * @param string $connection
405
-     */
406
-    public function setConnection(string $connection)
407
-    {
408
-        $this->connection = $connection;
409
-    }
410
-
411
-    /**
412
-     * Get the Database connection the Entity is stored on.
413
-     *
414
-     * @return string|null
415
-     */
416
-    public function getConnection()
417
-    {
418
-        return $this->connection;
419
-    }
420
-
421
-    /**
422
-     * Get the table associated with the entity.
423
-     *
424
-     * @return string
425
-     */
426
-    public function getTable(): string
427
-    {
428
-        if (isset($this->table)) {
429
-            return $this->table;
430
-        }
431
-
432
-        return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
433
-    }
434
-
435
-    /**
436
-     * Set the database table name.
437
-     *
438
-     * @param string $table
439
-     */
440
-    public function setTable(string $table)
441
-    {
442
-        $this->table = $table;
443
-    }
444
-
445
-    /**
446
-     * Get the custom entity class.
447
-     *
448
-     * @return string|null
449
-     */
450
-    public function getClass()
451
-    {
452
-        return $this->class ?: null;
453
-    }
454
-
455
-    /**
456
-     * Set the custom entity class.
457
-     *
458
-     * @param string $class The FQCN
459
-     */
460
-    public function setClass(string $class)
461
-    {
462
-        $this->class = $class;
463
-    }
464
-
465
-    /**
466
-     * Get the embedded Value Objects.
467
-     *
468
-     * @deprecated 5.5
469
-     *
470
-     * @return array
471
-     */
472
-    public function getEmbeddables(): array
473
-    {
474
-        return $this->embeddables;
475
-    }
476
-
477
-    /**
478
-     * Return attributes that should be mapped to class properties.
479
-     *
480
-     * @return array
481
-     */
482
-    public function getProperties(): array
483
-    {
484
-        return get_parent_class(__CLASS__) !== false
485
-            ? array_unique(array_merge($this->properties, parent::getProperties()))
486
-            : $this->properties;
487
-    }
488
-
489
-    /**
490
-     * Return event map for the Entity.
491
-     *
492
-     * @return array
493
-     */
494
-    public function getEvents() : array
495
-    {
496
-        return $this->events;
497
-    }
498
-
499
-    /**
500
-     * Return the array property in which will be mapped all attributes
501
-     * that are not mapped to class properties.
502
-     *
503
-     * @return string
504
-     */
505
-    public function getAttributesPropertyName() : string
506
-    {
507
-    }
508
-
509
-    /**
510
-     * Set the embedded Value Objects.
511
-     *
512
-     * @deprecated 5.5
513
-     *
514
-     * @param array $embeddables
515
-     */
516
-    public function setEmbeddables(array $embeddables)
517
-    {
518
-        $this->embeddables = $embeddables;
519
-    }
520
-
521
-    /**
522
-     * Get the relationships to map on a custom domain
523
-     * class.
524
-     *
525
-     * @return array
526
-     */
527
-    public function getRelationships(): array
528
-    {
529
-        return $this->relationships;
530
-    }
531
-
532
-    /**
533
-     * Return all relationships that are not embedded objects.
534
-     *
535
-     * @return array
536
-     */
537
-    public function getNonEmbeddedRelationships(): array
538
-    {
539
-        return array_diff($this->relationships, $this->embeddedRelations);
540
-    }
541
-
542
-    /**
543
-     * Get the relationships that will not have a proxy
544
-     * set on them.
545
-     *
546
-     * @return array
547
-     */
548
-    public function getRelationshipsWithoutProxy(): array
549
-    {
550
-        return $this->nonProxyRelationships;
551
-    }
552
-
553
-    /**
554
-     * Relationships of the Entity type.
555
-     *
556
-     * @return array
557
-     */
558
-    public function getSingleRelationships(): array
559
-    {
560
-        return $this->singleRelations;
561
-    }
562
-
563
-    /**
564
-     * Return true if relationship is single.
565
-     *
566
-     * @param string $relation
567
-     *
568
-     * @return bool
569
-     */
570
-    public function isSingleRelationship(string $relation): bool
571
-    {
572
-        return in_array($relation, $this->singleRelations);
573
-    }
574
-
575
-    /**
576
-     * Relationships of type Collection.
577
-     *
578
-     * @return array
579
-     */
580
-    public function getManyRelationships(): array
581
-    {
582
-        return $this->manyRelations;
583
-    }
584
-
585
-    /**
586
-     * Return true if relationship is single.
587
-     *
588
-     * @param string $relation
589
-     *
590
-     * @return bool
591
-     */
592
-    public function isManyRelationship(string $relation): bool
593
-    {
594
-        return in_array($relation, $this->manyRelations);
595
-    }
596
-
597
-    /**
598
-     * Return empty value for a given relationship.
599
-     *
600
-     * @param string $relation
601
-     *
602
-     * @throws MappingException
603
-     *
604
-     * @return mixed
605
-     */
606
-    public function getEmptyValueForRelationship(string $relation)
607
-    {
608
-        if ($this->isSingleRelationship($relation)) {
609
-            return;
610
-        }
611
-
612
-        if ($this->isManyRelationship($relation)) {
613
-            return new Collection();
614
-        }
615
-
616
-        throw new MappingException("Cannot determine default value of $relation");
617
-    }
618
-
619
-    /**
620
-     * Return empty value for a local foreign key.
621
-     *
622
-     * @param string $relation
623
-     *
624
-     * @return mixed
625
-     */
626
-    public function getEmptyValueForLocalKey(string $relation)
627
-    {
628
-        if ($this->isPolymorphic($relation)) {
629
-            $key = $this->localForeignKeys[$relation];
630
-
631
-            return [
632
-                $key['type'] => null,
633
-                $key['id']   => null,
634
-            ];
635
-        }
636
-
637
-        if ($this->isManyRelationship($relation)) {
638
-            return [];
639
-        }
640
-    }
641
-
642
-    /**
643
-     * Relationships with foreign key in the mapped entity record.
644
-     *
645
-     * @return array
646
-     */
647
-    public function getLocalRelationships(): array
648
-    {
649
-        return $this->localRelations;
650
-    }
651
-
652
-    /**
653
-     * Return the local keys associated to the relationship.
654
-     *
655
-     * @param string $relation
656
-     *
657
-     * @return string|array|null
658
-     */
659
-    public function getLocalKeys($relation)
660
-    {
661
-        return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
662
-    }
663
-
664
-    /**
665
-     * Relationships with foreign key in the related Entity record.
666
-     *
667
-     * @return array
668
-     */
669
-    public function getForeignRelationships(): array
670
-    {
671
-        return $this->foreignRelations;
672
-    }
673
-
674
-    /**
675
-     * Relationships which keys are stored in a pivot record.
676
-     *
677
-     * @return array
678
-     */
679
-    public function getPivotRelationships(): array
680
-    {
681
-        return $this->pivotRelations;
682
-    }
683
-
684
-    /**
685
-     * Return an array containing all embedded relationships.
686
-     *
687
-     * @return array
688
-     */
689
-    public function getEmbeddedRelationships(): array
690
-    {
691
-        return $this->embeddedRelations;
692
-    }
693
-
694
-    /**
695
-     * Return true if the relationship method is polymorphic.
696
-     *
697
-     * @param string $relation
698
-     *
699
-     * @return bool
700
-     */
701
-    public function isPolymorphic($relation): bool
702
-    {
703
-        return in_array($relation, $this->polymorphicRelations);
704
-    }
705
-
706
-    /**
707
-     * Get the targeted type for a relationship. Return null if polymorphic.
708
-     *
709
-     * @param string $relation
710
-     *
711
-     * @return string|null
712
-     */
713
-    public function getTargettedClass($relation)
714
-    {
715
-        if (array_key_exists($relation, $this->relatedClasses)) {
716
-            return $this->relatedClasses[$relation];
717
-        }
718
-    }
719
-
720
-    /**
721
-     * Add a Dynamic Relationship method at runtime. This has to be done
722
-     * by hooking the 'initializing' event, before entityMap is initialized.
723
-     *
724
-     * @param string   $name         Relation name
725
-     * @param \Closure $relationship
726
-     *
727
-     * @return void
728
-     */
729
-    public function addRelationshipMethod($name, \Closure $relationship)
730
-    {
731
-        $this->dynamicRelationships[$name] = $relationship;
732
-    }
733
-
734
-    /**
735
-     * Get the dynamic relationship method names.
736
-     *
737
-     * @return array
738
-     */
739
-    public function getDynamicRelationships(): array
740
-    {
741
-        return array_keys($this->dynamicRelationships);
742
-    }
743
-
744
-    /**
745
-     * Get the relationships that have to be eager loaded
746
-     * on each request.
747
-     *
748
-     * @return array
749
-     */
750
-    public function getEagerloadedRelationships(): array
751
-    {
752
-        return $this->with;
753
-    }
754
-
755
-    /**
756
-     * Get the primary key attribute for the entity.
757
-     *
758
-     * @return string|null
759
-     */
760
-    public function getKeyName()
761
-    {
762
-        return $this->primaryKey;
763
-    }
764
-
765
-    /**
766
-     * Set the primary key for the entity.
767
-     *
768
-     * @param string $key
769
-     *
770
-     * @return void
771
-     */
772
-    public function setKeyName(string $key)
773
-    {
774
-        $this->primaryKey = $key;
775
-    }
776
-
777
-    /**
778
-     * Get the table qualified key name.
779
-     *
780
-     * @return string
781
-     */
782
-    public function getQualifiedKeyName(): string
783
-    {
784
-        return $this->getTable().'.'.$this->getKeyName();
785
-    }
786
-
787
-    /**
788
-     * Get the number of models to return per page.
789
-     *
790
-     * @return int
791
-     */
792
-    public function getPerPage(): int
793
-    {
794
-        return $this->perPage;
795
-    }
796
-
797
-    /**
798
-     * Set the number of models to return per page.
799
-     *
800
-     * @param int $perPage
801
-     *
802
-     * @return void
803
-     */
804
-    public function setPerPage(int $perPage)
805
-    {
806
-        $this->perPage = $perPage;
807
-    }
808
-
809
-    /**
810
-     * Determine if the entity uses get.
811
-     *
812
-     * @return bool
813
-     */
814
-    public function usesTimestamps(): bool
815
-    {
816
-        return $this->timestamps;
817
-    }
818
-
819
-    /**
820
-     * Determine if the entity uses soft deletes.
821
-     *
822
-     * @return bool
823
-     */
824
-    public function usesSoftDeletes(): bool
825
-    {
826
-        return $this->softDeletes;
827
-    }
828
-
829
-    /**
830
-     * Get the 'created_at' column name.
831
-     *
832
-     * @return string
833
-     */
834
-    public function getCreatedAtColumn(): string
835
-    {
836
-        return $this->createdAtColumn;
837
-    }
838
-
839
-    /**
840
-     * Get the 'updated_at' column name.
841
-     *
842
-     * @return string
843
-     */
844
-    public function getUpdatedAtColumn(): string
845
-    {
846
-        return $this->updatedAtColumn;
847
-    }
848
-
849
-    /**
850
-     * Get the deleted_at column.
851
-     *
852
-     * @return string
853
-     */
854
-    public function getQualifiedDeletedAtColumn(): string
855
-    {
856
-        return $this->deletedAtColumn;
857
-    }
858
-
859
-    /**
860
-     * Get the default foreign key name for the model.
861
-     *
862
-     * @return string
863
-     */
864
-    public function getForeignKey(): string
865
-    {
866
-        return snake_case(class_basename($this->getClass())).'_id';
867
-    }
868
-
869
-    /**
870
-     * Return the inheritance type used by the entity.
871
-     *
872
-     * @return string|null
873
-     */
874
-    public function getInheritanceType()
875
-    {
876
-        return $this->inheritanceType;
877
-    }
878
-
879
-    /**
880
-     * Return the discriminator column name on the entity that's
881
-     * used for table inheritance.
882
-     *
883
-     * @return string
884
-     */
885
-    public function getDiscriminatorColumn(): string
886
-    {
887
-        return $this->discriminatorColumn;
888
-    }
889
-
890
-    /**
891
-     * Return the mapping of discriminator column values to
892
-     * entity class names that are used for table inheritance.
893
-     *
894
-     * @return array
895
-     */
896
-    public function getDiscriminatorColumnMap(): array
897
-    {
898
-        return $this->discriminatorColumnMap;
899
-    }
900
-
901
-    /**
902
-     * Return true if the entity should be instantiated using
903
-     * the IoC Container.
904
-     *
905
-     * @return bool
906
-     */
907
-    public function useDependencyInjection(): bool
908
-    {
909
-        return $this->dependencyInjection;
910
-    }
911
-
912
-    /**
913
-     * Add a single relation method name once.
914
-     *
915
-     * @param string $relation
916
-     */
917
-    protected function addSingleRelation(string $relation)
918
-    {
919
-        if (!in_array($relation, $this->singleRelations)) {
920
-            $this->singleRelations[] = $relation;
921
-        }
922
-    }
923
-
924
-    /**
925
-     * Add a foreign relation method name once.
926
-     *
927
-     * @param string $relation
928
-     */
929
-    protected function addForeignRelation(string $relation)
930
-    {
931
-        if (!in_array($relation, $this->foreignRelations)) {
932
-            $this->foreignRelations[] = $relation;
933
-        }
934
-    }
935
-
936
-    /**
937
-     * Add a polymorphic relation method name once.
938
-     *
939
-     * @param string $relation
940
-     */
941
-    protected function addPolymorphicRelation(string $relation)
942
-    {
943
-        if (!in_array($relation, $this->polymorphicRelations)) {
944
-            $this->polymorphicRelations[] = $relation;
945
-        }
946
-    }
947
-
948
-    /**
949
-     * Add a non proxy relation method name once.
950
-     *
951
-     * @param string $relation
952
-     */
953
-    protected function addNonProxyRelation(string $relation)
954
-    {
955
-        if (!in_array($relation, $this->nonProxyRelationships)) {
956
-            $this->nonProxyRelationships[] = $relation;
957
-        }
958
-    }
959
-
960
-    /**
961
-     * Add a local relation method name once.
962
-     *
963
-     * @param string $relation
964
-     */
965
-    protected function addLocalRelation(string $relation)
966
-    {
967
-        if (!in_array($relation, $this->localRelations)) {
968
-            $this->localRelations[] = $relation;
969
-        }
970
-    }
971
-
972
-    /**
973
-     * Add a many relation method name once.
974
-     *
975
-     * @param string $relation
976
-     */
977
-    protected function addManyRelation(string $relation)
978
-    {
979
-        if (!in_array($relation, $this->manyRelations)) {
980
-            $this->manyRelations[] = $relation;
981
-        }
982
-    }
983
-
984
-    /**
985
-     * Add a pivot relation method name once.
986
-     *
987
-     * @param string $relation
988
-     */
989
-    protected function addPivotRelation(string $relation)
990
-    {
991
-        if (!in_array($relation, $this->pivotRelations)) {
992
-            $this->pivotRelations[] = $relation;
993
-        }
994
-    }
995
-
996
-    /**
997
-     * Add an embedded relation.
998
-     *
999
-     * @param string $relation
1000
-     */
1001
-    protected function addEmbeddedRelation(string $relation)
1002
-    {
1003
-        if (!in_array($relation, $this->embeddedRelations)) {
1004
-            $this->embeddedRelations[] = $relation;
1005
-        }
1006
-    }
1007
-
1008
-    /**
1009
-     * Define an Embedded Object.
1010
-     *
1011
-     * @param mixed  $parent
1012
-     * @param string $relatedClass
1013
-     * @param string $relation
1014
-     *
1015
-     * @return EmbedsOne
1016
-     */
1017
-    public function embedsOne($parent, string $relatedClass, string $relation = null): EmbedsOne
1018
-    {
1019
-        if (is_null($relation)) {
1020
-            list(, $caller) = debug_backtrace(false);
1021
-            $relation = $caller['function'];
1022
-        }
1023
-
1024
-        $this->addEmbeddedRelation($relation);
1025
-        $this->addNonProxyRelation($relation);
1026
-
1027
-        return new EmbedsOne($parent, $relatedClass, $relation);
1028
-    }
1029
-
1030
-    /**
1031
-     * Define an Embedded Collection.
1032
-     *
1033
-     * @param mixed  $parent
1034
-     * @param string $relatedClass
1035
-     * @param string $relation
1036
-     *
1037
-     * @return EmbedsMany
1038
-     */
1039
-    public function embedsMany($parent, string $relatedClass, string $relation = null): EmbedsMany
1040
-    {
1041
-        if (is_null($relation)) {
1042
-            list(, $caller) = debug_backtrace(false);
1043
-            $relation = $caller['function'];
1044
-        }
1045
-
1046
-        $this->addEmbeddedRelation($relation);
1047
-        $this->addNonProxyRelation($relation);
1048
-
1049
-        return new EmbedsMany($parent, $relatedClass, $relation);
1050
-    }
1051
-
1052
-    /**
1053
-     * Define a one-to-one relationship.
1054
-     *
1055
-     * @param mixed  $entity
1056
-     * @param string $related    entity class
1057
-     * @param string $foreignKey
1058
-     * @param string $localKey
1059
-     *
1060
-     * @throws MappingException
1061
-     *
1062
-     * @return \Analogue\ORM\Relationships\HasOne
1063
-     */
1064
-    public function hasOne($entity, string $related, string $foreignKey = null, string $localKey = null): HasOne
1065
-    {
1066
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1067
-
1068
-        $relatedMapper = Manager::getInstance()->mapper($related);
1069
-
1070
-        $relatedMap = $relatedMapper->getEntityMap();
1071
-
1072
-        $localKey = $localKey ?: $this->getKeyName();
1073
-
1074
-        // Add the relation to the definition in map
1075
-        list(, $caller) = debug_backtrace(false);
1076
-        $relation = $caller['function'];
1077
-        $this->relatedClasses[$relation] = $related;
1078
-
1079
-        $this->addSingleRelation($relation);
1080
-        $this->addForeignRelation($relation);
1081
-        $this->addNonProxyRelation($relation);
1082
-
1083
-        // This relationship will always be eager loaded, as proxying it would
1084
-        // mean having an object that doesn't actually exists.
1085
-        if (!in_array($relation, $this->with)) {
1086
-            $this->with[] = $relation;
1087
-        }
1088
-
1089
-        return new HasOne($relatedMapper, $entity, /*$relatedMap->getTable().'.'*/$foreignKey, $localKey);
1090
-    }
1091
-
1092
-    /**
1093
-     * Define a polymorphic one-to-one relationship.
1094
-     *
1095
-     * @param mixed       $entity
1096
-     * @param string      $related
1097
-     * @param string      $name
1098
-     * @param string|null $type
1099
-     * @param string|null $id
1100
-     * @param string|null $localKey
1101
-     *
1102
-     * @throws MappingException
1103
-     *
1104
-     * @return \Analogue\ORM\Relationships\MorphOne
1105
-     */
1106
-    public function morphOne(
1107
-        $entity,
1108
-        string $related,
1109
-        string $name,
1110
-        string $type = null,
1111
-        string $id = null,
1112
-        string $localKey = null
1113
-    ): MorphOne {
1114
-        list($type, $id) = $this->getMorphs($name, $type, $id);
1115
-
1116
-        $localKey = $localKey ?: $this->getKeyName();
1117
-
1118
-        $relatedMapper = Manager::getInstance()->mapper($related);
1119
-
1120
-        //$table = $relatedMapper->getEntityMap()->getTable();
1121
-
1122
-        // Add the relation to the definition in map
1123
-        list(, $caller) = debug_backtrace(false);
1124
-        $relation = $caller['function'];
1125
-        $this->relatedClasses[$relation] = $related;
1126
-
1127
-        $this->addSingleRelation($relation);
1128
-        $this->addForeignRelation($relation);
1129
-        $this->addNonProxyRelation($relation);
1130
-
1131
-        // This relationship will always be eager loaded, as proxying it would
1132
-        // mean having an object that doesn't actually exists.
1133
-        if (!in_array($relation, $this->with)) {
1134
-            $this->with[] = $relation;
1135
-        }
1136
-
1137
-        return new MorphOne($relatedMapper, $entity, /*$table.'.'.*/$type, /*$table.'.'.*/$id, $localKey);
1138
-    }
1139
-
1140
-    /**
1141
-     * Define an inverse one-to-one or many relationship.
1142
-     *
1143
-     * @param mixed       $entity
1144
-     * @param string      $related
1145
-     * @param string|null $foreignKey
1146
-     * @param string|null $otherKey
1147
-     *
1148
-     * @throws MappingException
1149
-     *
1150
-     * @return \Analogue\ORM\Relationships\BelongsTo
1151
-     */
1152
-    public function belongsTo($entity, string $related, string $foreignKey = null, string $otherKey = null): BelongsTo
1153
-    {
1154
-        // Add the relation to the definition in map
1155
-        list(, $caller) = debug_backtrace(false);
1156
-        $relation = $caller['function'];
1157
-        $this->relatedClasses[$relation] = $related;
1158
-
1159
-        $this->addSingleRelation($relation);
1160
-        $this->addLocalRelation($relation);
1161
-
1162
-        // If no foreign key was supplied, we can use a backtrace to guess the proper
1163
-        // foreign key name by using the name of the relationship function, which
1164
-        // when combined with an "_id" should conventionally match the columns.
1165
-        if (is_null($foreignKey)) {
1166
-            $foreignKey = snake_case($relation).'_id';
1167
-        }
1168
-
1169
-        $this->localForeignKeys[$relation] = $foreignKey;
1170
-
1171
-        $relatedMapper = Manager::getInstance()->mapper($related);
1172
-
1173
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
1174
-
1175
-        return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
1176
-    }
1177
-
1178
-    /**
1179
-     * Define a polymorphic, inverse one-to-one or many relationship.
1180
-     *
1181
-     * @param mixed       $entity
1182
-     * @param string|null $name
1183
-     * @param string|null $type
1184
-     * @param string|null $id
1185
-     *
1186
-     * @throws MappingException
1187
-     *
1188
-     * @return \Analogue\ORM\Relationships\MorphTo
1189
-     */
1190
-    public function morphTo($entity, string $name = null, string $type = null, string $id = null): MorphTo
1191
-    {
1192
-        // If no name is provided, we will use the backtrace to get the function name
1193
-        // since that is most likely the name of the polymorphic interface. We can
1194
-        // use that to get both the class and foreign key that will be utilized.
1195
-        if (is_null($name)) {
1196
-            list(, $caller) = debug_backtrace(false);
1197
-
1198
-            $name = snake_case($caller['function']);
1199
-        }
1200
-        $this->addSingleRelation($name);
1201
-        $this->addLocalRelation($name);
1202
-        $this->addPolymorphicRelation($name);
1203
-
1204
-        $this->relatedClass[$name] = null;
1205
-
1206
-        list($type, $id) = $this->getMorphs($name, $type, $id);
1207
-
1208
-        // Store the foreign key in the entity map.
1209
-        // We might want to store the (key, type) as we might need it
1210
-        // to build a MorphTo proxy
1211
-        $this->localForeignKeys[$name] = [
1212
-            'id'   => $id,
1213
-            'type' => $type,
1214
-        ];
1215
-
1216
-        $mapper = Manager::getInstance()->mapper(get_class($entity));
1217
-
1218
-        // If the type value is null it is probably safe to assume we're eager loading
1219
-        // the relationship. When that is the case we will pass in a dummy query as
1220
-        // there are multiple types in the morph and we can't use single queries.
1221
-        $factory = new Factory();
1222
-        $wrapper = $factory->make($entity);
1223
-
1224
-        if (is_null($class = $wrapper->getEntityAttribute($type))) {
1225
-            return new MorphTo(
1226
-                $mapper, $entity, $id, null, $type, $name
1227
-            );
1228
-        }
1229
-
1230
-        // If we are not eager loading the relationship we will essentially treat this
1231
-        // as a belongs-to style relationship since morph-to extends that class and
1232
-        // we will pass in the appropriate values so that it behaves as expected.
1233
-        else {
1234
-            $class = Manager::getInstance()->getInverseMorphMap($class);
1235
-            $relatedMapper = Manager::getInstance()->mapper($class);
1236
-
1237
-            $foreignKey = $relatedMapper->getEntityMap()->getKeyName();
1238
-
1239
-            return new MorphTo(
1240
-                $relatedMapper, $entity, $id, $foreignKey, $type, $name
1241
-            );
1242
-        }
1243
-    }
1244
-
1245
-    /**
1246
-     * Define a one-to-many relationship.
1247
-     *
1248
-     * @param mixed       $entity
1249
-     * @param string      $related
1250
-     * @param string|null $foreignKey
1251
-     * @param string|null $localKey
1252
-     *
1253
-     * @throws MappingException
1254
-     *
1255
-     * @return \Analogue\ORM\Relationships\HasMany
1256
-     */
1257
-    public function hasMany($entity, string $related, string $foreignKey = null, string $localKey = null): HasMany
1258
-    {
1259
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1260
-
1261
-        $relatedMapper = Manager::getInstance()->mapper($related);
1262
-
1263
-        $localKey = $localKey ?: $this->getKeyName();
1264
-
1265
-        // Add the relation to the definition in map
1266
-        list(, $caller) = debug_backtrace(false);
1267
-        $relation = $caller['function'];
1268
-        $this->relatedClasses[$relation] = $related;
1269
-
1270
-        $this->addManyRelation($relation);
1271
-        $this->addForeignRelation($relation);
1272
-
1273
-        return new HasMany($relatedMapper, $entity, $foreignKey, $localKey);
1274
-    }
1275
-
1276
-    /**
1277
-     * Define a has-many-through relationship.
1278
-     *
1279
-     * @param mixed       $entity
1280
-     * @param string      $related
1281
-     * @param string      $through
1282
-     * @param string|null $firstKey
1283
-     * @param string|null $secondKey
1284
-     *
1285
-     * @throws MappingException
1286
-     *
1287
-     * @return \Analogue\ORM\Relationships\HasManyThrough
1288
-     */
1289
-    public function hasManyThrough(
1290
-        $entity,
1291
-        string $related,
1292
-        string $through,
1293
-        string $firstKey = null,
1294
-        string $secondKey = null
1295
-    ): HasManyThrough {
1296
-        $relatedMapper = Manager::getInstance()->mapper($related);
1297
-        $throughMapper = Manager::getInstance()->mapper($through);
1298
-
1299
-        $firstKey = $firstKey ?: $this->getForeignKey();
1300
-
1301
-        $throughMap = $throughMapper->getEntityMap();
1302
-
1303
-        $secondKey = $secondKey ?: $throughMap->getForeignKey();
1304
-
1305
-        // Add the relation to the definition in map
1306
-        list(, $caller) = debug_backtrace(false);
1307
-        $relation = $caller['function'];
1308
-        $this->relatedClasses[$relation] = $related;
1309
-
1310
-        $this->addManyRelation($relation);
1311
-        $this->addForeignRelation($relation);
1312
-
1313
-        return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
1314
-    }
1315
-
1316
-    /**
1317
-     * Define a polymorphic one-to-many relationship.
1318
-     *
1319
-     * @param mixed       $entity
1320
-     * @param string      $related
1321
-     * @param string      $name
1322
-     * @param string|null $type
1323
-     * @param string|null $id
1324
-     * @param string|null $localKey
1325
-     *
1326
-     * @return \Analogue\ORM\Relationships\MorphMany
1327
-     */
1328
-    public function morphMany(
1329
-        $entity,
1330
-        string $related,
1331
-        string $name,
1332
-        string $type = null,
1333
-        string $id = null,
1334
-        string $localKey = null
1335
-    ): MorphMany {
1336
-        // Here we will gather up the morph type and ID for the relationship so that we
1337
-        // can properly query the intermediate table of a relation. Finally, we will
1338
-        // get the table and create the relationship instances for the developers.
1339
-        list($type, $id) = $this->getMorphs($name, $type, $id);
1340
-
1341
-        $relatedMapper = Manager::getInstance()->mapper($related);
1342
-
1343
-        $table = $relatedMapper->getEntityMap()->getTable();
1344
-
1345
-        $localKey = $localKey ?: $this->getKeyName();
1346
-
1347
-        // Add the relation to the definition in map
1348
-        list(, $caller) = debug_backtrace(false);
1349
-        $relation = $caller['function'];
1350
-        $this->relatedClasses[$relation] = $related;
1351
-
1352
-        $this->addManyRelation($relation);
1353
-        $this->addForeignRelation($relation);
1354
-
1355
-        return new MorphMany($relatedMapper, $entity, /*$table.'.'.*/$type, /*$table.'.'.*/$id, $localKey);
1356
-    }
1357
-
1358
-    /**
1359
-     * Define a many-to-many relationship.
1360
-     *
1361
-     * @param mixed       $entity
1362
-     * @param string      $related
1363
-     * @param string|null $table
1364
-     * @param string|null $foreignKey
1365
-     * @param string|null $otherKey
1366
-     *
1367
-     * @throws MappingException
1368
-     *
1369
-     * @return \Analogue\ORM\Relationships\BelongsToMany
1370
-     */
1371
-    public function belongsToMany(
1372
-        $entity,
1373
-        string $related,
1374
-        string $table = null,
1375
-        string $foreignKey = null,
1376
-        string $otherKey = null
1377
-    ): BelongsToMany {
1378
-        // Add the relation to the definition in map
1379
-        list(, $caller) = debug_backtrace(false);
1380
-        $relation = $caller['function'];
1381
-        $this->relatedClasses[$relation] = $related;
1382
-
1383
-        $this->addManyRelation($relation);
1384
-        $this->addForeignRelation($relation);
1385
-        $this->addPivotRelation($relation);
1386
-
1387
-        // First, we'll need to determine the foreign key and "other key" for the
1388
-        // relationship. Once we have determined the keys we'll make the query
1389
-        // instances as well as the relationship instances we need for this.
1390
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1391
-
1392
-        $relatedMapper = Manager::getInstance()->mapper($related);
1393
-
1394
-        $relatedMap = $relatedMapper->getEntityMap();
1395
-
1396
-        $otherKey = $otherKey ?: $relatedMap->getForeignKey();
1397
-
1398
-        // If no table name was provided, we can guess it by concatenating the two
1399
-        // models using underscores in alphabetical order. The two model names
1400
-        // are transformed to snake case from their default CamelCase also.
1401
-        if (is_null($table)) {
1402
-            $table = $this->joiningTable($relatedMap);
1403
-        }
1404
-
1405
-        return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1406
-    }
1407
-
1408
-    /**
1409
-     * Define a polymorphic many-to-many relationship.
1410
-     *
1411
-     * @param mixed       $entity
1412
-     * @param string      $related
1413
-     * @param string      $name
1414
-     * @param string|null $table
1415
-     * @param string|null $foreignKey
1416
-     * @param string|null $otherKey
1417
-     * @param bool        $inverse
1418
-     *
1419
-     * @throws MappingException
1420
-     *
1421
-     * @return \Analogue\ORM\Relationships\MorphToMany
1422
-     */
1423
-    public function morphToMany(
1424
-        $entity,
1425
-        string $related,
1426
-        string $name,
1427
-        string $table = null,
1428
-        string $foreignKey = null,
1429
-        string $otherKey = null,
1430
-        bool $inverse = false
1431
-    ): MorphToMany {
1432
-        // Add the relation to the definition in map
1433
-        list(, $caller) = debug_backtrace(false);
1434
-        $relation = $caller['function'];
1435
-        $this->relatedClasses[$relation] = $related;
1436
-
1437
-        $this->addManyRelation($relation);
1438
-        $this->addForeignRelation($relation);
1439
-        $this->addPivotRelation($relation);
1440
-
1441
-        // First, we will need to determine the foreign key and "other key" for the
1442
-        // relationship. Once we have determined the keys we will make the query
1443
-        // instances, as well as the relationship instances we need for these.
1444
-        $foreignKey = $foreignKey ?: $name.'_id';
1445
-
1446
-        $relatedMapper = Manager::getInstance()->mapper($related);
1447
-
1448
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1449
-
1450
-        $table = $table ?: str_plural($name);
1451
-
1452
-        return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1453
-    }
1454
-
1455
-    /**
1456
-     * Define a polymorphic, inverse many-to-many relationship.
1457
-     *
1458
-     * @param mixed       $entity
1459
-     * @param string      $related
1460
-     * @param string      $name
1461
-     * @param string|null $table
1462
-     * @param string|null $foreignKey
1463
-     * @param string|null $otherKey
1464
-     *
1465
-     * @throws MappingException
1466
-     *
1467
-     * @return \Analogue\ORM\Relationships\MorphToMany
1468
-     */
1469
-    public function morphedByMany(
1470
-        $entity,
1471
-        string $related,
1472
-        string $name,
1473
-        string $table = null,
1474
-        string $foreignKey = null,
1475
-        string $otherKey = null
1476
-    ): MorphToMany {
1477
-        // Add the relation to the definition in map
1478
-        list(, $caller) = debug_backtrace(false);
1479
-        $relation = $caller['function'];
1480
-        $this->relatedClasses[$relation] = $related;
1481
-
1482
-        $this->addManyRelation($relation);
1483
-        $this->addForeignRelation($relation);
1484
-
1485
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1486
-
1487
-        // For the inverse of the polymorphic many-to-many relations, we will change
1488
-        // the way we determine the foreign and other keys, as it is the opposite
1489
-        // of the morph-to-many method since we're figuring out these inverses.
1490
-        $otherKey = $otherKey ?: $name.'_id';
1491
-
1492
-        return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1493
-    }
1494
-
1495
-    /**
1496
-     * Get the joining table name for a many-to-many relation.
1497
-     *
1498
-     * @param EntityMap $relatedMap
1499
-     *
1500
-     * @return string
1501
-     */
1502
-    public function joiningTable(self $relatedMap): string
1503
-    {
1504
-        // The joining table name, by convention, is simply the snake cased models
1505
-        // sorted alphabetically and concatenated with an underscore, so we can
1506
-        // just sort the models and join them together to get the table name.
1507
-        $base = $this->getTable();
1508
-
1509
-        $related = $relatedMap->getTable();
1510
-
1511
-        $tables = [$related, $base];
1512
-
1513
-        // Now that we have the model names in an array we can just sort them and
1514
-        // use the implode function to join them together with an underscores,
1515
-        // which is typically used by convention within the database system.
1516
-        sort($tables);
1517
-
1518
-        return strtolower(implode('_', $tables));
1519
-    }
1520
-
1521
-    /**
1522
-     * Get the polymorphic relationship columns.
1523
-     *
1524
-     * @param string $name
1525
-     * @param string $type
1526
-     * @param string $id
1527
-     *
1528
-     * @return string[]
1529
-     */
1530
-    protected function getMorphs(string $name, string $type = null, string $id = null): array
1531
-    {
1532
-        return [
1533
-            $type ?: $name.'_type',
1534
-            $id ?: $name.'_id',
1535
-        ];
1536
-    }
1537
-
1538
-    /**
1539
-     * Get the class name for polymorphic relations.
1540
-     *
1541
-     * @return string
1542
-     */
1543
-    public function getMorphClass(): string
1544
-    {
1545
-        $morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1546
-
1547
-        return $this->morphClass ?: $morphClass;
1548
-    }
1549
-
1550
-    /**
1551
-     * Create a new Entity Collection instance.
1552
-     *
1553
-     * @param array $entities
1554
-     *
1555
-     * @return \Analogue\ORM\EntityCollection
1556
-     */
1557
-    public function newCollection(array $entities = []): EntityCollection
1558
-    {
1559
-        return new EntityCollection($entities);
1560
-    }
1561
-
1562
-    /**
1563
-     * Process EntityMap parsing at initialization time.
1564
-     *
1565
-     * @return void
1566
-     */
1567
-    public function initialize()
1568
-    {
1569
-        $userMethods = $this->getCustomMethods();
1570
-
1571
-        // Parse EntityMap for method based relationship
1572
-        if (count($userMethods) > 0) {
1573
-            $this->relationships = $this->parseMethodsForRelationship($userMethods);
1574
-        }
1575
-
1576
-        // Parse EntityMap for dynamic relationships
1577
-        if (count($this->dynamicRelationships) > 0) {
1578
-            $this->relationships = $this->relationships + $this->getDynamicRelationships();
1579
-        }
1580
-    }
1581
-
1582
-    /**
1583
-     * Parse every relationships on the EntityMap and sort
1584
-     * them by type.
1585
-     *
1586
-     * @return void
1587
-     */
1588
-    public function boot()
1589
-    {
1590
-        if (count($this->relationships) > 0) {
1591
-            $this->sortRelationshipsByType();
1592
-        }
1593
-
1594
-        $this->isBooted = true;
1595
-    }
1596
-
1597
-    /**
1598
-     * Return true if entity map has been booted.
1599
-     *
1600
-     * @return bool
1601
-     */
1602
-    public function isBooted(): bool
1603
-    {
1604
-        return $this->isBooted;
1605
-    }
1606
-
1607
-    /**
1608
-     * Get Methods that has been added in the child class.
1609
-     *
1610
-     * @return array
1611
-     */
1612
-    protected function getCustomMethods(): array
1613
-    {
1614
-        $mapMethods = get_class_methods($this);
1615
-
1616
-        $parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1617
-
1618
-        return array_diff($mapMethods, $parentsMethods);
1619
-    }
1620
-
1621
-    /**
1622
-     * Parse user's class methods for relationships.
1623
-     *
1624
-     * @param array $customMethods
1625
-     *
1626
-     * @return array
1627
-     */
1628
-    protected function parseMethodsForRelationship(array $customMethods): array
1629
-    {
1630
-        $relationships = [];
1631
-
1632
-        $class = new ReflectionClass(get_class($this));
1633
-
1634
-        // Get the mapped Entity class, as we will detect relationships
1635
-        // methods by testing that the first argument is type-hinted to
1636
-        // the same class as the mapped Entity.
1637
-        $entityClass = $this->getClass();
1638
-
1639
-        foreach ($customMethods as $methodName) {
1640
-            $method = $class->getMethod($methodName);
1641
-
1642
-            if ($method->getNumberOfParameters() > 0) {
1643
-                $params = $method->getParameters();
1644
-
1645
-                if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1646
-                    $relationships[] = $methodName;
1647
-                }
1648
-            }
1649
-        }
1650
-
1651
-        return $relationships;
1652
-    }
1653
-
1654
-    /**
1655
-     * Sort Relationships methods by type.
1656
-     *
1657
-     * TODO : replace this by directly setting these value
1658
-     * in the corresponding methods, so we won't need
1659
-     * the correspondency table
1660
-     *
1661
-     * @return void
1662
-     */
1663
-    protected function sortRelationshipsByType()
1664
-    {
1665
-        $entityClass = $this->getClass();
1666
-
1667
-        // Instantiate a dummy entity which we will pass to relationship methods.
1668
-        $entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1669
-
1670
-        foreach ($this->relationships as $relation) {
1671
-            $this->$relation($entity);
1672
-        }
1673
-    }
1674
-
1675
-    /**
1676
-     * Override this method for custom entity instantiation.
1677
-     *
1678
-     * @return null
1679
-     *
1680
-     * @deprecated 5.5
1681
-     */
1682
-    public function activator()
1683
-    {
1684
-    }
1685
-
1686
-    /**
1687
-     * Magic call to dynamic relationships.
1688
-     *
1689
-     * @param string $method
1690
-     * @param array  $parameters
1691
-     *
1692
-     * @throws Exception
1693
-     *
1694
-     * @return mixed
1695
-     */
1696
-    public function __call(string $method, array $parameters)
1697
-    {
1698
-        if (!array_key_exists($method, $this->dynamicRelationships)) {
1699
-            throw new Exception(get_class($this)." has no method $method");
1700
-        }
1701
-
1702
-        // Add $this to parameters so the closure can call relationship method on the map.
1703
-        $parameters[] = $this;
1704
-
1705
-        return  call_user_func_array($this->dynamicRelationships[$method], $parameters);
1706
-    }
29
+	/**
30
+	 * The mapping driver to use with this entity.
31
+	 *
32
+	 * @var string
33
+	 */
34
+	protected $driver = 'illuminate';
35
+
36
+	/**
37
+	 * The Database Connection name for the model.
38
+	 *
39
+	 * @var string
40
+	 */
41
+	protected $connection;
42
+
43
+	/**
44
+	 * The table associated with the entity.
45
+	 *
46
+	 * @var string
47
+	 */
48
+	protected $table;
49
+
50
+	/**
51
+	 * The primary key for the model. If the model is an Embedded Value object
52
+	 * primary key is set to null.
53
+	 *
54
+	 * @var string|null
55
+	 */
56
+	protected $primaryKey = 'id';
57
+
58
+	/**
59
+	 * Name of the entity's array property that should
60
+	 * contain the attributes.
61
+	 * If set to null, analogue will only hydrate object's properties.
62
+	 *
63
+	 * @var string|null
64
+	 */
65
+	protected $arrayName = 'attributes';
66
+
67
+	/**
68
+	 * Array containing the list of database columns to be mapped
69
+	 * in the attributes array of the entity.
70
+	 *
71
+	 * @var array
72
+	 */
73
+	protected $attributes = [];
74
+
75
+	/**
76
+	 * Array containing the list of database columns to be mapped
77
+	 * to the entity's class properties.
78
+	 *
79
+	 * @var array
80
+	 */
81
+	protected $properties = [];
82
+
83
+	/**
84
+	 * The Custom Domain Class to use with this mapping.
85
+	 *
86
+	 * @var string
87
+	 */
88
+	protected $class;
89
+
90
+	/**
91
+	 * The event map for the entity.
92
+	 *
93
+	 * @var array
94
+	 */
95
+	protected $events = [];
96
+
97
+	/**
98
+	 * Embedded Value Objects.
99
+	 *
100
+	 * @deprecated 5.5 (use embedsOne() or embedsMany() relationships instead)
101
+	 *
102
+	 * @var array
103
+	 */
104
+	protected $embeddables = [];
105
+
106
+	/**
107
+	 * Determine the relationships method used on the entity.
108
+	 * If not set, mapper will autodetect them.
109
+	 *
110
+	 * @var array
111
+	 */
112
+	protected $relationships = [];
113
+
114
+	/**
115
+	 * Relationships that should be treated as collection.
116
+	 *
117
+	 * @var array
118
+	 */
119
+	protected $manyRelations = [];
120
+
121
+	/**
122
+	 * Relationships that should be treated as single entity.
123
+	 *
124
+	 * @var array
125
+	 */
126
+	protected $singleRelations = [];
127
+
128
+	/**
129
+	 * Relationships for which the key is stored in the Entity itself.
130
+	 *
131
+	 * @var array
132
+	 */
133
+	protected $localRelations = [];
134
+
135
+	/**
136
+	 * List of local keys associated to local relation methods.
137
+	 *
138
+	 * @var array
139
+	 */
140
+	protected $localForeignKeys = [];
141
+
142
+	/**
143
+	 * Relationships for which the key is stored in the Related Entity.
144
+	 *
145
+	 * @var array
146
+	 */
147
+	protected $foreignRelations = [];
148
+
149
+	/**
150
+	 * Relationships which use a pivot record.
151
+	 *
152
+	 * @var array
153
+	 */
154
+	protected $pivotRelations = [];
155
+
156
+	/**
157
+	 * Polymorphic relationships.
158
+	 *
159
+	 * @var array
160
+	 */
161
+	protected $polymorphicRelations = [];
162
+
163
+	/**
164
+	 * Dynamic relationships.
165
+	 *
166
+	 * @var array
167
+	 */
168
+	protected $dynamicRelationships = [];
169
+
170
+	/**
171
+	 * Targeted class for the relationship method. value is set to `null` for
172
+	 * polymorphic relations.
173
+	 *
174
+	 * @var array
175
+	 */
176
+	protected $relatedClasses = [];
177
+
178
+	/**
179
+	 * Some relation methods like embedded objects, or HasOne and MorphOne,
180
+	 * will never have a proxy loaded on them.
181
+	 *
182
+	 * @var array
183
+	 */
184
+	protected $nonProxyRelationships = [];
185
+
186
+	/**
187
+	 * Relation methods that are embedded objects.
188
+	 *
189
+	 * @var array
190
+	 */
191
+	protected $embeddedRelations = [];
192
+
193
+	/**
194
+	 * The number of models to return for pagination.
195
+	 *
196
+	 * @var int
197
+	 */
198
+	protected $perPage = 15;
199
+
200
+	/**
201
+	 * The relations to eager load on every query.
202
+	 *
203
+	 * @var array
204
+	 */
205
+	protected $with = [];
206
+
207
+	/**
208
+	 * The class name to be used in polymorphic relations.
209
+	 *
210
+	 * @var string
211
+	 */
212
+	protected $morphClass;
213
+
214
+	/**
215
+	 * Indicates if the entity should be timestamped.
216
+	 *
217
+	 * @var bool
218
+	 */
219
+	public $timestamps = false;
220
+
221
+	/**
222
+	 * The name of the "created at" attribute.
223
+	 *
224
+	 * @var string
225
+	 */
226
+	protected $createdAtColumn = 'created_at';
227
+
228
+	/**
229
+	 * The name of the "updated at" attribute.
230
+	 *
231
+	 * @var string
232
+	 */
233
+	protected $updatedAtColumn = 'updated_at';
234
+
235
+	/**
236
+	 * Indicates if the entity uses softdeletes.
237
+	 *
238
+	 * @var bool
239
+	 */
240
+	public $softDeletes = false;
241
+
242
+	/**
243
+	 * The name of the "deleted at" attribute.
244
+	 *
245
+	 * @var string
246
+	 */
247
+	protected $deletedAtColumn = 'deleted_at';
248
+
249
+	/**
250
+	 * The date format to use with the current database connection.
251
+	 *
252
+	 * @var string
253
+	 */
254
+	protected $dateFormat;
255
+
256
+	/**
257
+	 * Set this property to true if the entity should be instantiated
258
+	 * using the IoC Container.
259
+	 *
260
+	 * @var bool
261
+	 */
262
+	protected $dependencyInjection = false;
263
+
264
+	/**
265
+	 * Set the usage of inheritance, possible values are :
266
+	 * "single_table"
267
+	 * null.
268
+	 *
269
+	 * @var string
270
+	 */
271
+	protected $inheritanceType;
272
+
273
+	/**
274
+	 * Discriminator column name.
275
+	 *
276
+	 * @var string
277
+	 */
278
+	protected $discriminatorColumn = 'type';
279
+
280
+	/**
281
+	 * Allow using a string to define which entity type should be instantiated.
282
+	 * If not set, analogue will uses entity's FQCN.
283
+	 *
284
+	 * @var array
285
+	 */
286
+	protected $discriminatorColumnMap = [];
287
+
288
+	/**
289
+	 * Indicate if the entity map has been booted.
290
+	 *
291
+	 * @var bool
292
+	 */
293
+	private $isBooted = false;
294
+
295
+	/**
296
+	 * Return Domain class attributes, useful when mapping to a Plain PHP Object.
297
+	 *
298
+	 * @return array
299
+	 */
300
+	public function getAttributes(): array
301
+	{
302
+		return $this->attributes;
303
+	}
304
+
305
+	/**
306
+	 * Set the domain class attributes.
307
+	 *
308
+	 * @param array $attributeNames
309
+	 */
310
+	public function setAttributes(array $attributeNames)
311
+	{
312
+		$this->attributes = $attributeNames;
313
+	}
314
+
315
+	/**
316
+	 * Return true if the Entity has an 'attributes' array property.
317
+	 *
318
+	 * @return bool
319
+	 */
320
+	public function usesAttributesArray(): bool
321
+	{
322
+		if ($this->arrayName === null) {
323
+			return false;
324
+		}
325
+
326
+		if ($this->attributes === null) {
327
+			return false;
328
+		}
329
+
330
+		return true;
331
+	}
332
+
333
+	/**
334
+	 * Return the name of the Entity's attributes property.
335
+	 *
336
+	 * @return string|null
337
+	 */
338
+	public function getAttributesArrayName()
339
+	{
340
+		return $this->arrayName;
341
+	}
342
+
343
+	/**
344
+	 * Get all the attribute names for the class, including relationships, embeddables and primary key.
345
+	 *
346
+	 * @return array
347
+	 */
348
+	public function getCompiledAttributes(): array
349
+	{
350
+		$key = $this->getKeyName();
351
+
352
+		$embeddables = array_keys($this->getEmbeddables());
353
+
354
+		$relationships = $this->getRelationships();
355
+
356
+		$attributes = $this->getAttributes();
357
+
358
+		return array_merge([$key], $embeddables, $relationships, $attributes);
359
+	}
360
+
361
+	/**
362
+	 * Set the date format to use with the current database connection.
363
+	 *
364
+	 * @param string $format
365
+	 */
366
+	public function setDateFormat(string $format)
367
+	{
368
+		$this->dateFormat = $format;
369
+	}
370
+
371
+	/**
372
+	 * Get the date format to use with the current database connection.
373
+	 *
374
+	 *  @return string
375
+	 */
376
+	public function getDateFormat(): string
377
+	{
378
+		return $this->dateFormat;
379
+	}
380
+
381
+	/**
382
+	 * Set the Driver for this mapping.
383
+	 *
384
+	 * @param string $driver
385
+	 */
386
+	public function setDriver(string $driver)
387
+	{
388
+		$this->driver = $driver;
389
+	}
390
+
391
+	/**
392
+	 * Get the Driver for this mapping.
393
+	 *
394
+	 * @return string
395
+	 */
396
+	public function getDriver(): string
397
+	{
398
+		return $this->driver;
399
+	}
400
+
401
+	/**
402
+	 * Set the db connection to use on the table.
403
+	 *
404
+	 * @param string $connection
405
+	 */
406
+	public function setConnection(string $connection)
407
+	{
408
+		$this->connection = $connection;
409
+	}
410
+
411
+	/**
412
+	 * Get the Database connection the Entity is stored on.
413
+	 *
414
+	 * @return string|null
415
+	 */
416
+	public function getConnection()
417
+	{
418
+		return $this->connection;
419
+	}
420
+
421
+	/**
422
+	 * Get the table associated with the entity.
423
+	 *
424
+	 * @return string
425
+	 */
426
+	public function getTable(): string
427
+	{
428
+		if (isset($this->table)) {
429
+			return $this->table;
430
+		}
431
+
432
+		return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
433
+	}
434
+
435
+	/**
436
+	 * Set the database table name.
437
+	 *
438
+	 * @param string $table
439
+	 */
440
+	public function setTable(string $table)
441
+	{
442
+		$this->table = $table;
443
+	}
444
+
445
+	/**
446
+	 * Get the custom entity class.
447
+	 *
448
+	 * @return string|null
449
+	 */
450
+	public function getClass()
451
+	{
452
+		return $this->class ?: null;
453
+	}
454
+
455
+	/**
456
+	 * Set the custom entity class.
457
+	 *
458
+	 * @param string $class The FQCN
459
+	 */
460
+	public function setClass(string $class)
461
+	{
462
+		$this->class = $class;
463
+	}
464
+
465
+	/**
466
+	 * Get the embedded Value Objects.
467
+	 *
468
+	 * @deprecated 5.5
469
+	 *
470
+	 * @return array
471
+	 */
472
+	public function getEmbeddables(): array
473
+	{
474
+		return $this->embeddables;
475
+	}
476
+
477
+	/**
478
+	 * Return attributes that should be mapped to class properties.
479
+	 *
480
+	 * @return array
481
+	 */
482
+	public function getProperties(): array
483
+	{
484
+		return get_parent_class(__CLASS__) !== false
485
+			? array_unique(array_merge($this->properties, parent::getProperties()))
486
+			: $this->properties;
487
+	}
488
+
489
+	/**
490
+	 * Return event map for the Entity.
491
+	 *
492
+	 * @return array
493
+	 */
494
+	public function getEvents() : array
495
+	{
496
+		return $this->events;
497
+	}
498
+
499
+	/**
500
+	 * Return the array property in which will be mapped all attributes
501
+	 * that are not mapped to class properties.
502
+	 *
503
+	 * @return string
504
+	 */
505
+	public function getAttributesPropertyName() : string
506
+	{
507
+	}
508
+
509
+	/**
510
+	 * Set the embedded Value Objects.
511
+	 *
512
+	 * @deprecated 5.5
513
+	 *
514
+	 * @param array $embeddables
515
+	 */
516
+	public function setEmbeddables(array $embeddables)
517
+	{
518
+		$this->embeddables = $embeddables;
519
+	}
520
+
521
+	/**
522
+	 * Get the relationships to map on a custom domain
523
+	 * class.
524
+	 *
525
+	 * @return array
526
+	 */
527
+	public function getRelationships(): array
528
+	{
529
+		return $this->relationships;
530
+	}
531
+
532
+	/**
533
+	 * Return all relationships that are not embedded objects.
534
+	 *
535
+	 * @return array
536
+	 */
537
+	public function getNonEmbeddedRelationships(): array
538
+	{
539
+		return array_diff($this->relationships, $this->embeddedRelations);
540
+	}
541
+
542
+	/**
543
+	 * Get the relationships that will not have a proxy
544
+	 * set on them.
545
+	 *
546
+	 * @return array
547
+	 */
548
+	public function getRelationshipsWithoutProxy(): array
549
+	{
550
+		return $this->nonProxyRelationships;
551
+	}
552
+
553
+	/**
554
+	 * Relationships of the Entity type.
555
+	 *
556
+	 * @return array
557
+	 */
558
+	public function getSingleRelationships(): array
559
+	{
560
+		return $this->singleRelations;
561
+	}
562
+
563
+	/**
564
+	 * Return true if relationship is single.
565
+	 *
566
+	 * @param string $relation
567
+	 *
568
+	 * @return bool
569
+	 */
570
+	public function isSingleRelationship(string $relation): bool
571
+	{
572
+		return in_array($relation, $this->singleRelations);
573
+	}
574
+
575
+	/**
576
+	 * Relationships of type Collection.
577
+	 *
578
+	 * @return array
579
+	 */
580
+	public function getManyRelationships(): array
581
+	{
582
+		return $this->manyRelations;
583
+	}
584
+
585
+	/**
586
+	 * Return true if relationship is single.
587
+	 *
588
+	 * @param string $relation
589
+	 *
590
+	 * @return bool
591
+	 */
592
+	public function isManyRelationship(string $relation): bool
593
+	{
594
+		return in_array($relation, $this->manyRelations);
595
+	}
596
+
597
+	/**
598
+	 * Return empty value for a given relationship.
599
+	 *
600
+	 * @param string $relation
601
+	 *
602
+	 * @throws MappingException
603
+	 *
604
+	 * @return mixed
605
+	 */
606
+	public function getEmptyValueForRelationship(string $relation)
607
+	{
608
+		if ($this->isSingleRelationship($relation)) {
609
+			return;
610
+		}
611
+
612
+		if ($this->isManyRelationship($relation)) {
613
+			return new Collection();
614
+		}
615
+
616
+		throw new MappingException("Cannot determine default value of $relation");
617
+	}
618
+
619
+	/**
620
+	 * Return empty value for a local foreign key.
621
+	 *
622
+	 * @param string $relation
623
+	 *
624
+	 * @return mixed
625
+	 */
626
+	public function getEmptyValueForLocalKey(string $relation)
627
+	{
628
+		if ($this->isPolymorphic($relation)) {
629
+			$key = $this->localForeignKeys[$relation];
630
+
631
+			return [
632
+				$key['type'] => null,
633
+				$key['id']   => null,
634
+			];
635
+		}
636
+
637
+		if ($this->isManyRelationship($relation)) {
638
+			return [];
639
+		}
640
+	}
641
+
642
+	/**
643
+	 * Relationships with foreign key in the mapped entity record.
644
+	 *
645
+	 * @return array
646
+	 */
647
+	public function getLocalRelationships(): array
648
+	{
649
+		return $this->localRelations;
650
+	}
651
+
652
+	/**
653
+	 * Return the local keys associated to the relationship.
654
+	 *
655
+	 * @param string $relation
656
+	 *
657
+	 * @return string|array|null
658
+	 */
659
+	public function getLocalKeys($relation)
660
+	{
661
+		return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
662
+	}
663
+
664
+	/**
665
+	 * Relationships with foreign key in the related Entity record.
666
+	 *
667
+	 * @return array
668
+	 */
669
+	public function getForeignRelationships(): array
670
+	{
671
+		return $this->foreignRelations;
672
+	}
673
+
674
+	/**
675
+	 * Relationships which keys are stored in a pivot record.
676
+	 *
677
+	 * @return array
678
+	 */
679
+	public function getPivotRelationships(): array
680
+	{
681
+		return $this->pivotRelations;
682
+	}
683
+
684
+	/**
685
+	 * Return an array containing all embedded relationships.
686
+	 *
687
+	 * @return array
688
+	 */
689
+	public function getEmbeddedRelationships(): array
690
+	{
691
+		return $this->embeddedRelations;
692
+	}
693
+
694
+	/**
695
+	 * Return true if the relationship method is polymorphic.
696
+	 *
697
+	 * @param string $relation
698
+	 *
699
+	 * @return bool
700
+	 */
701
+	public function isPolymorphic($relation): bool
702
+	{
703
+		return in_array($relation, $this->polymorphicRelations);
704
+	}
705
+
706
+	/**
707
+	 * Get the targeted type for a relationship. Return null if polymorphic.
708
+	 *
709
+	 * @param string $relation
710
+	 *
711
+	 * @return string|null
712
+	 */
713
+	public function getTargettedClass($relation)
714
+	{
715
+		if (array_key_exists($relation, $this->relatedClasses)) {
716
+			return $this->relatedClasses[$relation];
717
+		}
718
+	}
719
+
720
+	/**
721
+	 * Add a Dynamic Relationship method at runtime. This has to be done
722
+	 * by hooking the 'initializing' event, before entityMap is initialized.
723
+	 *
724
+	 * @param string   $name         Relation name
725
+	 * @param \Closure $relationship
726
+	 *
727
+	 * @return void
728
+	 */
729
+	public function addRelationshipMethod($name, \Closure $relationship)
730
+	{
731
+		$this->dynamicRelationships[$name] = $relationship;
732
+	}
733
+
734
+	/**
735
+	 * Get the dynamic relationship method names.
736
+	 *
737
+	 * @return array
738
+	 */
739
+	public function getDynamicRelationships(): array
740
+	{
741
+		return array_keys($this->dynamicRelationships);
742
+	}
743
+
744
+	/**
745
+	 * Get the relationships that have to be eager loaded
746
+	 * on each request.
747
+	 *
748
+	 * @return array
749
+	 */
750
+	public function getEagerloadedRelationships(): array
751
+	{
752
+		return $this->with;
753
+	}
754
+
755
+	/**
756
+	 * Get the primary key attribute for the entity.
757
+	 *
758
+	 * @return string|null
759
+	 */
760
+	public function getKeyName()
761
+	{
762
+		return $this->primaryKey;
763
+	}
764
+
765
+	/**
766
+	 * Set the primary key for the entity.
767
+	 *
768
+	 * @param string $key
769
+	 *
770
+	 * @return void
771
+	 */
772
+	public function setKeyName(string $key)
773
+	{
774
+		$this->primaryKey = $key;
775
+	}
776
+
777
+	/**
778
+	 * Get the table qualified key name.
779
+	 *
780
+	 * @return string
781
+	 */
782
+	public function getQualifiedKeyName(): string
783
+	{
784
+		return $this->getTable().'.'.$this->getKeyName();
785
+	}
786
+
787
+	/**
788
+	 * Get the number of models to return per page.
789
+	 *
790
+	 * @return int
791
+	 */
792
+	public function getPerPage(): int
793
+	{
794
+		return $this->perPage;
795
+	}
796
+
797
+	/**
798
+	 * Set the number of models to return per page.
799
+	 *
800
+	 * @param int $perPage
801
+	 *
802
+	 * @return void
803
+	 */
804
+	public function setPerPage(int $perPage)
805
+	{
806
+		$this->perPage = $perPage;
807
+	}
808
+
809
+	/**
810
+	 * Determine if the entity uses get.
811
+	 *
812
+	 * @return bool
813
+	 */
814
+	public function usesTimestamps(): bool
815
+	{
816
+		return $this->timestamps;
817
+	}
818
+
819
+	/**
820
+	 * Determine if the entity uses soft deletes.
821
+	 *
822
+	 * @return bool
823
+	 */
824
+	public function usesSoftDeletes(): bool
825
+	{
826
+		return $this->softDeletes;
827
+	}
828
+
829
+	/**
830
+	 * Get the 'created_at' column name.
831
+	 *
832
+	 * @return string
833
+	 */
834
+	public function getCreatedAtColumn(): string
835
+	{
836
+		return $this->createdAtColumn;
837
+	}
838
+
839
+	/**
840
+	 * Get the 'updated_at' column name.
841
+	 *
842
+	 * @return string
843
+	 */
844
+	public function getUpdatedAtColumn(): string
845
+	{
846
+		return $this->updatedAtColumn;
847
+	}
848
+
849
+	/**
850
+	 * Get the deleted_at column.
851
+	 *
852
+	 * @return string
853
+	 */
854
+	public function getQualifiedDeletedAtColumn(): string
855
+	{
856
+		return $this->deletedAtColumn;
857
+	}
858
+
859
+	/**
860
+	 * Get the default foreign key name for the model.
861
+	 *
862
+	 * @return string
863
+	 */
864
+	public function getForeignKey(): string
865
+	{
866
+		return snake_case(class_basename($this->getClass())).'_id';
867
+	}
868
+
869
+	/**
870
+	 * Return the inheritance type used by the entity.
871
+	 *
872
+	 * @return string|null
873
+	 */
874
+	public function getInheritanceType()
875
+	{
876
+		return $this->inheritanceType;
877
+	}
878
+
879
+	/**
880
+	 * Return the discriminator column name on the entity that's
881
+	 * used for table inheritance.
882
+	 *
883
+	 * @return string
884
+	 */
885
+	public function getDiscriminatorColumn(): string
886
+	{
887
+		return $this->discriminatorColumn;
888
+	}
889
+
890
+	/**
891
+	 * Return the mapping of discriminator column values to
892
+	 * entity class names that are used for table inheritance.
893
+	 *
894
+	 * @return array
895
+	 */
896
+	public function getDiscriminatorColumnMap(): array
897
+	{
898
+		return $this->discriminatorColumnMap;
899
+	}
900
+
901
+	/**
902
+	 * Return true if the entity should be instantiated using
903
+	 * the IoC Container.
904
+	 *
905
+	 * @return bool
906
+	 */
907
+	public function useDependencyInjection(): bool
908
+	{
909
+		return $this->dependencyInjection;
910
+	}
911
+
912
+	/**
913
+	 * Add a single relation method name once.
914
+	 *
915
+	 * @param string $relation
916
+	 */
917
+	protected function addSingleRelation(string $relation)
918
+	{
919
+		if (!in_array($relation, $this->singleRelations)) {
920
+			$this->singleRelations[] = $relation;
921
+		}
922
+	}
923
+
924
+	/**
925
+	 * Add a foreign relation method name once.
926
+	 *
927
+	 * @param string $relation
928
+	 */
929
+	protected function addForeignRelation(string $relation)
930
+	{
931
+		if (!in_array($relation, $this->foreignRelations)) {
932
+			$this->foreignRelations[] = $relation;
933
+		}
934
+	}
935
+
936
+	/**
937
+	 * Add a polymorphic relation method name once.
938
+	 *
939
+	 * @param string $relation
940
+	 */
941
+	protected function addPolymorphicRelation(string $relation)
942
+	{
943
+		if (!in_array($relation, $this->polymorphicRelations)) {
944
+			$this->polymorphicRelations[] = $relation;
945
+		}
946
+	}
947
+
948
+	/**
949
+	 * Add a non proxy relation method name once.
950
+	 *
951
+	 * @param string $relation
952
+	 */
953
+	protected function addNonProxyRelation(string $relation)
954
+	{
955
+		if (!in_array($relation, $this->nonProxyRelationships)) {
956
+			$this->nonProxyRelationships[] = $relation;
957
+		}
958
+	}
959
+
960
+	/**
961
+	 * Add a local relation method name once.
962
+	 *
963
+	 * @param string $relation
964
+	 */
965
+	protected function addLocalRelation(string $relation)
966
+	{
967
+		if (!in_array($relation, $this->localRelations)) {
968
+			$this->localRelations[] = $relation;
969
+		}
970
+	}
971
+
972
+	/**
973
+	 * Add a many relation method name once.
974
+	 *
975
+	 * @param string $relation
976
+	 */
977
+	protected function addManyRelation(string $relation)
978
+	{
979
+		if (!in_array($relation, $this->manyRelations)) {
980
+			$this->manyRelations[] = $relation;
981
+		}
982
+	}
983
+
984
+	/**
985
+	 * Add a pivot relation method name once.
986
+	 *
987
+	 * @param string $relation
988
+	 */
989
+	protected function addPivotRelation(string $relation)
990
+	{
991
+		if (!in_array($relation, $this->pivotRelations)) {
992
+			$this->pivotRelations[] = $relation;
993
+		}
994
+	}
995
+
996
+	/**
997
+	 * Add an embedded relation.
998
+	 *
999
+	 * @param string $relation
1000
+	 */
1001
+	protected function addEmbeddedRelation(string $relation)
1002
+	{
1003
+		if (!in_array($relation, $this->embeddedRelations)) {
1004
+			$this->embeddedRelations[] = $relation;
1005
+		}
1006
+	}
1007
+
1008
+	/**
1009
+	 * Define an Embedded Object.
1010
+	 *
1011
+	 * @param mixed  $parent
1012
+	 * @param string $relatedClass
1013
+	 * @param string $relation
1014
+	 *
1015
+	 * @return EmbedsOne
1016
+	 */
1017
+	public function embedsOne($parent, string $relatedClass, string $relation = null): EmbedsOne
1018
+	{
1019
+		if (is_null($relation)) {
1020
+			list(, $caller) = debug_backtrace(false);
1021
+			$relation = $caller['function'];
1022
+		}
1023
+
1024
+		$this->addEmbeddedRelation($relation);
1025
+		$this->addNonProxyRelation($relation);
1026
+
1027
+		return new EmbedsOne($parent, $relatedClass, $relation);
1028
+	}
1029
+
1030
+	/**
1031
+	 * Define an Embedded Collection.
1032
+	 *
1033
+	 * @param mixed  $parent
1034
+	 * @param string $relatedClass
1035
+	 * @param string $relation
1036
+	 *
1037
+	 * @return EmbedsMany
1038
+	 */
1039
+	public function embedsMany($parent, string $relatedClass, string $relation = null): EmbedsMany
1040
+	{
1041
+		if (is_null($relation)) {
1042
+			list(, $caller) = debug_backtrace(false);
1043
+			$relation = $caller['function'];
1044
+		}
1045
+
1046
+		$this->addEmbeddedRelation($relation);
1047
+		$this->addNonProxyRelation($relation);
1048
+
1049
+		return new EmbedsMany($parent, $relatedClass, $relation);
1050
+	}
1051
+
1052
+	/**
1053
+	 * Define a one-to-one relationship.
1054
+	 *
1055
+	 * @param mixed  $entity
1056
+	 * @param string $related    entity class
1057
+	 * @param string $foreignKey
1058
+	 * @param string $localKey
1059
+	 *
1060
+	 * @throws MappingException
1061
+	 *
1062
+	 * @return \Analogue\ORM\Relationships\HasOne
1063
+	 */
1064
+	public function hasOne($entity, string $related, string $foreignKey = null, string $localKey = null): HasOne
1065
+	{
1066
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1067
+
1068
+		$relatedMapper = Manager::getInstance()->mapper($related);
1069
+
1070
+		$relatedMap = $relatedMapper->getEntityMap();
1071
+
1072
+		$localKey = $localKey ?: $this->getKeyName();
1073
+
1074
+		// Add the relation to the definition in map
1075
+		list(, $caller) = debug_backtrace(false);
1076
+		$relation = $caller['function'];
1077
+		$this->relatedClasses[$relation] = $related;
1078
+
1079
+		$this->addSingleRelation($relation);
1080
+		$this->addForeignRelation($relation);
1081
+		$this->addNonProxyRelation($relation);
1082
+
1083
+		// This relationship will always be eager loaded, as proxying it would
1084
+		// mean having an object that doesn't actually exists.
1085
+		if (!in_array($relation, $this->with)) {
1086
+			$this->with[] = $relation;
1087
+		}
1088
+
1089
+		return new HasOne($relatedMapper, $entity, /*$relatedMap->getTable().'.'*/$foreignKey, $localKey);
1090
+	}
1091
+
1092
+	/**
1093
+	 * Define a polymorphic one-to-one relationship.
1094
+	 *
1095
+	 * @param mixed       $entity
1096
+	 * @param string      $related
1097
+	 * @param string      $name
1098
+	 * @param string|null $type
1099
+	 * @param string|null $id
1100
+	 * @param string|null $localKey
1101
+	 *
1102
+	 * @throws MappingException
1103
+	 *
1104
+	 * @return \Analogue\ORM\Relationships\MorphOne
1105
+	 */
1106
+	public function morphOne(
1107
+		$entity,
1108
+		string $related,
1109
+		string $name,
1110
+		string $type = null,
1111
+		string $id = null,
1112
+		string $localKey = null
1113
+	): MorphOne {
1114
+		list($type, $id) = $this->getMorphs($name, $type, $id);
1115
+
1116
+		$localKey = $localKey ?: $this->getKeyName();
1117
+
1118
+		$relatedMapper = Manager::getInstance()->mapper($related);
1119
+
1120
+		//$table = $relatedMapper->getEntityMap()->getTable();
1121
+
1122
+		// Add the relation to the definition in map
1123
+		list(, $caller) = debug_backtrace(false);
1124
+		$relation = $caller['function'];
1125
+		$this->relatedClasses[$relation] = $related;
1126
+
1127
+		$this->addSingleRelation($relation);
1128
+		$this->addForeignRelation($relation);
1129
+		$this->addNonProxyRelation($relation);
1130
+
1131
+		// This relationship will always be eager loaded, as proxying it would
1132
+		// mean having an object that doesn't actually exists.
1133
+		if (!in_array($relation, $this->with)) {
1134
+			$this->with[] = $relation;
1135
+		}
1136
+
1137
+		return new MorphOne($relatedMapper, $entity, /*$table.'.'.*/$type, /*$table.'.'.*/$id, $localKey);
1138
+	}
1139
+
1140
+	/**
1141
+	 * Define an inverse one-to-one or many relationship.
1142
+	 *
1143
+	 * @param mixed       $entity
1144
+	 * @param string      $related
1145
+	 * @param string|null $foreignKey
1146
+	 * @param string|null $otherKey
1147
+	 *
1148
+	 * @throws MappingException
1149
+	 *
1150
+	 * @return \Analogue\ORM\Relationships\BelongsTo
1151
+	 */
1152
+	public function belongsTo($entity, string $related, string $foreignKey = null, string $otherKey = null): BelongsTo
1153
+	{
1154
+		// Add the relation to the definition in map
1155
+		list(, $caller) = debug_backtrace(false);
1156
+		$relation = $caller['function'];
1157
+		$this->relatedClasses[$relation] = $related;
1158
+
1159
+		$this->addSingleRelation($relation);
1160
+		$this->addLocalRelation($relation);
1161
+
1162
+		// If no foreign key was supplied, we can use a backtrace to guess the proper
1163
+		// foreign key name by using the name of the relationship function, which
1164
+		// when combined with an "_id" should conventionally match the columns.
1165
+		if (is_null($foreignKey)) {
1166
+			$foreignKey = snake_case($relation).'_id';
1167
+		}
1168
+
1169
+		$this->localForeignKeys[$relation] = $foreignKey;
1170
+
1171
+		$relatedMapper = Manager::getInstance()->mapper($related);
1172
+
1173
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
1174
+
1175
+		return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
1176
+	}
1177
+
1178
+	/**
1179
+	 * Define a polymorphic, inverse one-to-one or many relationship.
1180
+	 *
1181
+	 * @param mixed       $entity
1182
+	 * @param string|null $name
1183
+	 * @param string|null $type
1184
+	 * @param string|null $id
1185
+	 *
1186
+	 * @throws MappingException
1187
+	 *
1188
+	 * @return \Analogue\ORM\Relationships\MorphTo
1189
+	 */
1190
+	public function morphTo($entity, string $name = null, string $type = null, string $id = null): MorphTo
1191
+	{
1192
+		// If no name is provided, we will use the backtrace to get the function name
1193
+		// since that is most likely the name of the polymorphic interface. We can
1194
+		// use that to get both the class and foreign key that will be utilized.
1195
+		if (is_null($name)) {
1196
+			list(, $caller) = debug_backtrace(false);
1197
+
1198
+			$name = snake_case($caller['function']);
1199
+		}
1200
+		$this->addSingleRelation($name);
1201
+		$this->addLocalRelation($name);
1202
+		$this->addPolymorphicRelation($name);
1203
+
1204
+		$this->relatedClass[$name] = null;
1205
+
1206
+		list($type, $id) = $this->getMorphs($name, $type, $id);
1207
+
1208
+		// Store the foreign key in the entity map.
1209
+		// We might want to store the (key, type) as we might need it
1210
+		// to build a MorphTo proxy
1211
+		$this->localForeignKeys[$name] = [
1212
+			'id'   => $id,
1213
+			'type' => $type,
1214
+		];
1215
+
1216
+		$mapper = Manager::getInstance()->mapper(get_class($entity));
1217
+
1218
+		// If the type value is null it is probably safe to assume we're eager loading
1219
+		// the relationship. When that is the case we will pass in a dummy query as
1220
+		// there are multiple types in the morph and we can't use single queries.
1221
+		$factory = new Factory();
1222
+		$wrapper = $factory->make($entity);
1223
+
1224
+		if (is_null($class = $wrapper->getEntityAttribute($type))) {
1225
+			return new MorphTo(
1226
+				$mapper, $entity, $id, null, $type, $name
1227
+			);
1228
+		}
1229
+
1230
+		// If we are not eager loading the relationship we will essentially treat this
1231
+		// as a belongs-to style relationship since morph-to extends that class and
1232
+		// we will pass in the appropriate values so that it behaves as expected.
1233
+		else {
1234
+			$class = Manager::getInstance()->getInverseMorphMap($class);
1235
+			$relatedMapper = Manager::getInstance()->mapper($class);
1236
+
1237
+			$foreignKey = $relatedMapper->getEntityMap()->getKeyName();
1238
+
1239
+			return new MorphTo(
1240
+				$relatedMapper, $entity, $id, $foreignKey, $type, $name
1241
+			);
1242
+		}
1243
+	}
1244
+
1245
+	/**
1246
+	 * Define a one-to-many relationship.
1247
+	 *
1248
+	 * @param mixed       $entity
1249
+	 * @param string      $related
1250
+	 * @param string|null $foreignKey
1251
+	 * @param string|null $localKey
1252
+	 *
1253
+	 * @throws MappingException
1254
+	 *
1255
+	 * @return \Analogue\ORM\Relationships\HasMany
1256
+	 */
1257
+	public function hasMany($entity, string $related, string $foreignKey = null, string $localKey = null): HasMany
1258
+	{
1259
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1260
+
1261
+		$relatedMapper = Manager::getInstance()->mapper($related);
1262
+
1263
+		$localKey = $localKey ?: $this->getKeyName();
1264
+
1265
+		// Add the relation to the definition in map
1266
+		list(, $caller) = debug_backtrace(false);
1267
+		$relation = $caller['function'];
1268
+		$this->relatedClasses[$relation] = $related;
1269
+
1270
+		$this->addManyRelation($relation);
1271
+		$this->addForeignRelation($relation);
1272
+
1273
+		return new HasMany($relatedMapper, $entity, $foreignKey, $localKey);
1274
+	}
1275
+
1276
+	/**
1277
+	 * Define a has-many-through relationship.
1278
+	 *
1279
+	 * @param mixed       $entity
1280
+	 * @param string      $related
1281
+	 * @param string      $through
1282
+	 * @param string|null $firstKey
1283
+	 * @param string|null $secondKey
1284
+	 *
1285
+	 * @throws MappingException
1286
+	 *
1287
+	 * @return \Analogue\ORM\Relationships\HasManyThrough
1288
+	 */
1289
+	public function hasManyThrough(
1290
+		$entity,
1291
+		string $related,
1292
+		string $through,
1293
+		string $firstKey = null,
1294
+		string $secondKey = null
1295
+	): HasManyThrough {
1296
+		$relatedMapper = Manager::getInstance()->mapper($related);
1297
+		$throughMapper = Manager::getInstance()->mapper($through);
1298
+
1299
+		$firstKey = $firstKey ?: $this->getForeignKey();
1300
+
1301
+		$throughMap = $throughMapper->getEntityMap();
1302
+
1303
+		$secondKey = $secondKey ?: $throughMap->getForeignKey();
1304
+
1305
+		// Add the relation to the definition in map
1306
+		list(, $caller) = debug_backtrace(false);
1307
+		$relation = $caller['function'];
1308
+		$this->relatedClasses[$relation] = $related;
1309
+
1310
+		$this->addManyRelation($relation);
1311
+		$this->addForeignRelation($relation);
1312
+
1313
+		return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
1314
+	}
1315
+
1316
+	/**
1317
+	 * Define a polymorphic one-to-many relationship.
1318
+	 *
1319
+	 * @param mixed       $entity
1320
+	 * @param string      $related
1321
+	 * @param string      $name
1322
+	 * @param string|null $type
1323
+	 * @param string|null $id
1324
+	 * @param string|null $localKey
1325
+	 *
1326
+	 * @return \Analogue\ORM\Relationships\MorphMany
1327
+	 */
1328
+	public function morphMany(
1329
+		$entity,
1330
+		string $related,
1331
+		string $name,
1332
+		string $type = null,
1333
+		string $id = null,
1334
+		string $localKey = null
1335
+	): MorphMany {
1336
+		// Here we will gather up the morph type and ID for the relationship so that we
1337
+		// can properly query the intermediate table of a relation. Finally, we will
1338
+		// get the table and create the relationship instances for the developers.
1339
+		list($type, $id) = $this->getMorphs($name, $type, $id);
1340
+
1341
+		$relatedMapper = Manager::getInstance()->mapper($related);
1342
+
1343
+		$table = $relatedMapper->getEntityMap()->getTable();
1344
+
1345
+		$localKey = $localKey ?: $this->getKeyName();
1346
+
1347
+		// Add the relation to the definition in map
1348
+		list(, $caller) = debug_backtrace(false);
1349
+		$relation = $caller['function'];
1350
+		$this->relatedClasses[$relation] = $related;
1351
+
1352
+		$this->addManyRelation($relation);
1353
+		$this->addForeignRelation($relation);
1354
+
1355
+		return new MorphMany($relatedMapper, $entity, /*$table.'.'.*/$type, /*$table.'.'.*/$id, $localKey);
1356
+	}
1357
+
1358
+	/**
1359
+	 * Define a many-to-many relationship.
1360
+	 *
1361
+	 * @param mixed       $entity
1362
+	 * @param string      $related
1363
+	 * @param string|null $table
1364
+	 * @param string|null $foreignKey
1365
+	 * @param string|null $otherKey
1366
+	 *
1367
+	 * @throws MappingException
1368
+	 *
1369
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
1370
+	 */
1371
+	public function belongsToMany(
1372
+		$entity,
1373
+		string $related,
1374
+		string $table = null,
1375
+		string $foreignKey = null,
1376
+		string $otherKey = null
1377
+	): BelongsToMany {
1378
+		// Add the relation to the definition in map
1379
+		list(, $caller) = debug_backtrace(false);
1380
+		$relation = $caller['function'];
1381
+		$this->relatedClasses[$relation] = $related;
1382
+
1383
+		$this->addManyRelation($relation);
1384
+		$this->addForeignRelation($relation);
1385
+		$this->addPivotRelation($relation);
1386
+
1387
+		// First, we'll need to determine the foreign key and "other key" for the
1388
+		// relationship. Once we have determined the keys we'll make the query
1389
+		// instances as well as the relationship instances we need for this.
1390
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1391
+
1392
+		$relatedMapper = Manager::getInstance()->mapper($related);
1393
+
1394
+		$relatedMap = $relatedMapper->getEntityMap();
1395
+
1396
+		$otherKey = $otherKey ?: $relatedMap->getForeignKey();
1397
+
1398
+		// If no table name was provided, we can guess it by concatenating the two
1399
+		// models using underscores in alphabetical order. The two model names
1400
+		// are transformed to snake case from their default CamelCase also.
1401
+		if (is_null($table)) {
1402
+			$table = $this->joiningTable($relatedMap);
1403
+		}
1404
+
1405
+		return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1406
+	}
1407
+
1408
+	/**
1409
+	 * Define a polymorphic many-to-many relationship.
1410
+	 *
1411
+	 * @param mixed       $entity
1412
+	 * @param string      $related
1413
+	 * @param string      $name
1414
+	 * @param string|null $table
1415
+	 * @param string|null $foreignKey
1416
+	 * @param string|null $otherKey
1417
+	 * @param bool        $inverse
1418
+	 *
1419
+	 * @throws MappingException
1420
+	 *
1421
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1422
+	 */
1423
+	public function morphToMany(
1424
+		$entity,
1425
+		string $related,
1426
+		string $name,
1427
+		string $table = null,
1428
+		string $foreignKey = null,
1429
+		string $otherKey = null,
1430
+		bool $inverse = false
1431
+	): MorphToMany {
1432
+		// Add the relation to the definition in map
1433
+		list(, $caller) = debug_backtrace(false);
1434
+		$relation = $caller['function'];
1435
+		$this->relatedClasses[$relation] = $related;
1436
+
1437
+		$this->addManyRelation($relation);
1438
+		$this->addForeignRelation($relation);
1439
+		$this->addPivotRelation($relation);
1440
+
1441
+		// First, we will need to determine the foreign key and "other key" for the
1442
+		// relationship. Once we have determined the keys we will make the query
1443
+		// instances, as well as the relationship instances we need for these.
1444
+		$foreignKey = $foreignKey ?: $name.'_id';
1445
+
1446
+		$relatedMapper = Manager::getInstance()->mapper($related);
1447
+
1448
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1449
+
1450
+		$table = $table ?: str_plural($name);
1451
+
1452
+		return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1453
+	}
1454
+
1455
+	/**
1456
+	 * Define a polymorphic, inverse many-to-many relationship.
1457
+	 *
1458
+	 * @param mixed       $entity
1459
+	 * @param string      $related
1460
+	 * @param string      $name
1461
+	 * @param string|null $table
1462
+	 * @param string|null $foreignKey
1463
+	 * @param string|null $otherKey
1464
+	 *
1465
+	 * @throws MappingException
1466
+	 *
1467
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1468
+	 */
1469
+	public function morphedByMany(
1470
+		$entity,
1471
+		string $related,
1472
+		string $name,
1473
+		string $table = null,
1474
+		string $foreignKey = null,
1475
+		string $otherKey = null
1476
+	): MorphToMany {
1477
+		// Add the relation to the definition in map
1478
+		list(, $caller) = debug_backtrace(false);
1479
+		$relation = $caller['function'];
1480
+		$this->relatedClasses[$relation] = $related;
1481
+
1482
+		$this->addManyRelation($relation);
1483
+		$this->addForeignRelation($relation);
1484
+
1485
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1486
+
1487
+		// For the inverse of the polymorphic many-to-many relations, we will change
1488
+		// the way we determine the foreign and other keys, as it is the opposite
1489
+		// of the morph-to-many method since we're figuring out these inverses.
1490
+		$otherKey = $otherKey ?: $name.'_id';
1491
+
1492
+		return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1493
+	}
1494
+
1495
+	/**
1496
+	 * Get the joining table name for a many-to-many relation.
1497
+	 *
1498
+	 * @param EntityMap $relatedMap
1499
+	 *
1500
+	 * @return string
1501
+	 */
1502
+	public function joiningTable(self $relatedMap): string
1503
+	{
1504
+		// The joining table name, by convention, is simply the snake cased models
1505
+		// sorted alphabetically and concatenated with an underscore, so we can
1506
+		// just sort the models and join them together to get the table name.
1507
+		$base = $this->getTable();
1508
+
1509
+		$related = $relatedMap->getTable();
1510
+
1511
+		$tables = [$related, $base];
1512
+
1513
+		// Now that we have the model names in an array we can just sort them and
1514
+		// use the implode function to join them together with an underscores,
1515
+		// which is typically used by convention within the database system.
1516
+		sort($tables);
1517
+
1518
+		return strtolower(implode('_', $tables));
1519
+	}
1520
+
1521
+	/**
1522
+	 * Get the polymorphic relationship columns.
1523
+	 *
1524
+	 * @param string $name
1525
+	 * @param string $type
1526
+	 * @param string $id
1527
+	 *
1528
+	 * @return string[]
1529
+	 */
1530
+	protected function getMorphs(string $name, string $type = null, string $id = null): array
1531
+	{
1532
+		return [
1533
+			$type ?: $name.'_type',
1534
+			$id ?: $name.'_id',
1535
+		];
1536
+	}
1537
+
1538
+	/**
1539
+	 * Get the class name for polymorphic relations.
1540
+	 *
1541
+	 * @return string
1542
+	 */
1543
+	public function getMorphClass(): string
1544
+	{
1545
+		$morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1546
+
1547
+		return $this->morphClass ?: $morphClass;
1548
+	}
1549
+
1550
+	/**
1551
+	 * Create a new Entity Collection instance.
1552
+	 *
1553
+	 * @param array $entities
1554
+	 *
1555
+	 * @return \Analogue\ORM\EntityCollection
1556
+	 */
1557
+	public function newCollection(array $entities = []): EntityCollection
1558
+	{
1559
+		return new EntityCollection($entities);
1560
+	}
1561
+
1562
+	/**
1563
+	 * Process EntityMap parsing at initialization time.
1564
+	 *
1565
+	 * @return void
1566
+	 */
1567
+	public function initialize()
1568
+	{
1569
+		$userMethods = $this->getCustomMethods();
1570
+
1571
+		// Parse EntityMap for method based relationship
1572
+		if (count($userMethods) > 0) {
1573
+			$this->relationships = $this->parseMethodsForRelationship($userMethods);
1574
+		}
1575
+
1576
+		// Parse EntityMap for dynamic relationships
1577
+		if (count($this->dynamicRelationships) > 0) {
1578
+			$this->relationships = $this->relationships + $this->getDynamicRelationships();
1579
+		}
1580
+	}
1581
+
1582
+	/**
1583
+	 * Parse every relationships on the EntityMap and sort
1584
+	 * them by type.
1585
+	 *
1586
+	 * @return void
1587
+	 */
1588
+	public function boot()
1589
+	{
1590
+		if (count($this->relationships) > 0) {
1591
+			$this->sortRelationshipsByType();
1592
+		}
1593
+
1594
+		$this->isBooted = true;
1595
+	}
1596
+
1597
+	/**
1598
+	 * Return true if entity map has been booted.
1599
+	 *
1600
+	 * @return bool
1601
+	 */
1602
+	public function isBooted(): bool
1603
+	{
1604
+		return $this->isBooted;
1605
+	}
1606
+
1607
+	/**
1608
+	 * Get Methods that has been added in the child class.
1609
+	 *
1610
+	 * @return array
1611
+	 */
1612
+	protected function getCustomMethods(): array
1613
+	{
1614
+		$mapMethods = get_class_methods($this);
1615
+
1616
+		$parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1617
+
1618
+		return array_diff($mapMethods, $parentsMethods);
1619
+	}
1620
+
1621
+	/**
1622
+	 * Parse user's class methods for relationships.
1623
+	 *
1624
+	 * @param array $customMethods
1625
+	 *
1626
+	 * @return array
1627
+	 */
1628
+	protected function parseMethodsForRelationship(array $customMethods): array
1629
+	{
1630
+		$relationships = [];
1631
+
1632
+		$class = new ReflectionClass(get_class($this));
1633
+
1634
+		// Get the mapped Entity class, as we will detect relationships
1635
+		// methods by testing that the first argument is type-hinted to
1636
+		// the same class as the mapped Entity.
1637
+		$entityClass = $this->getClass();
1638
+
1639
+		foreach ($customMethods as $methodName) {
1640
+			$method = $class->getMethod($methodName);
1641
+
1642
+			if ($method->getNumberOfParameters() > 0) {
1643
+				$params = $method->getParameters();
1644
+
1645
+				if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1646
+					$relationships[] = $methodName;
1647
+				}
1648
+			}
1649
+		}
1650
+
1651
+		return $relationships;
1652
+	}
1653
+
1654
+	/**
1655
+	 * Sort Relationships methods by type.
1656
+	 *
1657
+	 * TODO : replace this by directly setting these value
1658
+	 * in the corresponding methods, so we won't need
1659
+	 * the correspondency table
1660
+	 *
1661
+	 * @return void
1662
+	 */
1663
+	protected function sortRelationshipsByType()
1664
+	{
1665
+		$entityClass = $this->getClass();
1666
+
1667
+		// Instantiate a dummy entity which we will pass to relationship methods.
1668
+		$entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1669
+
1670
+		foreach ($this->relationships as $relation) {
1671
+			$this->$relation($entity);
1672
+		}
1673
+	}
1674
+
1675
+	/**
1676
+	 * Override this method for custom entity instantiation.
1677
+	 *
1678
+	 * @return null
1679
+	 *
1680
+	 * @deprecated 5.5
1681
+	 */
1682
+	public function activator()
1683
+	{
1684
+	}
1685
+
1686
+	/**
1687
+	 * Magic call to dynamic relationships.
1688
+	 *
1689
+	 * @param string $method
1690
+	 * @param array  $parameters
1691
+	 *
1692
+	 * @throws Exception
1693
+	 *
1694
+	 * @return mixed
1695
+	 */
1696
+	public function __call(string $method, array $parameters)
1697
+	{
1698
+		if (!array_key_exists($method, $this->dynamicRelationships)) {
1699
+			throw new Exception(get_class($this)." has no method $method");
1700
+		}
1701
+
1702
+		// Add $this to parameters so the closure can call relationship method on the map.
1703
+		$parameters[] = $this;
1704
+
1705
+		return  call_user_func_array($this->dynamicRelationships[$method], $parameters);
1706
+	}
1707 1707
 }
Please login to merge, or discard this patch.
src/System/Wrappers/Factory.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -14,7 +14,7 @@
 block discarded – undo
14 14
      *
15 15
      * @throws \Analogue\ORM\Exceptions\MappingException
16 16
      *
17
-     * @return Wrapper
17
+     * @return ObjectWrapper
18 18
      */
19 19
     public function make($object)
20 20
     {
Please login to merge, or discard this patch.
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -7,34 +7,34 @@
 block discarded – undo
7 7
 
8 8
 class Factory
9 9
 {
10
-    /**
11
-     * Build the wrapper corresponding to the object's type.
12
-     *
13
-     * @param mixed $object
14
-     *
15
-     * @throws \Analogue\ORM\Exceptions\MappingException
16
-     *
17
-     * @return Wrapper
18
-     */
19
-    public function make($object)
20
-    {
21
-        $manager = Manager::getInstance();
10
+	/**
11
+	 * Build the wrapper corresponding to the object's type.
12
+	 *
13
+	 * @param mixed $object
14
+	 *
15
+	 * @throws \Analogue\ORM\Exceptions\MappingException
16
+	 *
17
+	 * @return Wrapper
18
+	 */
19
+	public function make($object)
20
+	{
21
+		$manager = Manager::getInstance();
22 22
 
23
-        // Instantiate hydrator. We'll need to optimize this and allow pre-generation
24
-        // of these hydrator, and get it, ideally, from the entityMap or the Mapper class,
25
-        // so it's only instantiated once
26
-        $config = new Configuration(get_class($object));
23
+		// Instantiate hydrator. We'll need to optimize this and allow pre-generation
24
+		// of these hydrator, and get it, ideally, from the entityMap or the Mapper class,
25
+		// so it's only instantiated once
26
+		$config = new Configuration(get_class($object));
27 27
 
28
-        $hydratorClass = $config->createFactory()->getHydratorClass();
29
-        $hydrator = new $hydratorClass();
28
+		$hydratorClass = $config->createFactory()->getHydratorClass();
29
+		$hydrator = new $hydratorClass();
30 30
 
31
-        if ($manager->isValueObject($object)) {
32
-            $entityMap = $manager->getValueMap($object);
33
-        } else {
34
-            $entityMap = $manager->mapper($object)->getEntityMap();
35
-        }
31
+		if ($manager->isValueObject($object)) {
32
+			$entityMap = $manager->getValueMap($object);
33
+		} else {
34
+			$entityMap = $manager->mapper($object)->getEntityMap();
35
+		}
36 36
 
37
-        // Build Wrapper
38
-        return new ObjectWrapper($object, $entityMap, $hydrator);
39
-    }
37
+		// Build Wrapper
38
+		return new ObjectWrapper($object, $entityMap, $hydrator);
39
+	}
40 40
 }
Please login to merge, or discard this patch.
src/System/Wrappers/ObjectWrapper.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -120,7 +120,7 @@
 block discarded – undo
120 120
     /**
121 121
      * Returns the wrapped entity's map.
122 122
      *
123
-     * @return mixed
123
+     * @return EntityMap
124 124
      */
125 125
     public function getMap()
126 126
     {
Please login to merge, or discard this patch.
Indentation   +416 added lines, -416 removed lines patch added patch discarded remove patch
@@ -13,420 +13,420 @@
 block discarded – undo
13 13
  */
14 14
 class ObjectWrapper implements InternallyMappable
15 15
 {
16
-    /**
17
-     * Original Entity Object.
18
-     *
19
-     * @var mixed
20
-     */
21
-    protected $entity;
22
-
23
-    /**
24
-     * Corresponding EntityMap.
25
-     *
26
-     * @var \Analogue\ORM\EntityMap
27
-     */
28
-    protected $entityMap;
29
-
30
-    /**
31
-     * @var \Analogue\ORM\System\Proxies\ProxyFactory
32
-     */
33
-    protected $proxyFactory;
34
-
35
-    /**
36
-     * Internal Representation of analogue's entity attributes.
37
-     *
38
-     * @var array
39
-     */
40
-    protected $attributes = [];
41
-
42
-    /**
43
-     * All properties on the original object.
44
-     *
45
-     * @var array
46
-     */
47
-    protected $properties = [];
48
-
49
-    /**
50
-     * Object properties that are not a part of the entity attributes,
51
-     * but which are needed to correctly hydrate the Object.
52
-     *
53
-     * @var array
54
-     */
55
-    protected $unmanagedProperties = [];
56
-
57
-    /**
58
-     * The hydrator for the wrapped object.
59
-     *
60
-     * @var HydratorInterface
61
-     */
62
-    protected $hydrator;
63
-
64
-    /**
65
-     * Set to true if the object's attributes have been modified since last
66
-     * hydration.
67
-     *
68
-     * @var bool
69
-     */
70
-    protected $touched = false;
71
-
72
-    /**
73
-     * Object Wrapper constructor.
74
-     *
75
-     * @param mixed                   $entity
76
-     * @param \Analogue\ORM\EntityMap $entityMap
77
-     * @param HydratorInterface       $hydrator
78
-     */
79
-    public function __construct($entity, $entityMap, HydratorInterface $hydrator)
80
-    {
81
-        $this->hydrator = $hydrator;
82
-        $this->entity = $entity;
83
-        $this->entityMap = $entityMap;
84
-        $this->proxyFactory = new ProxyFactory();
85
-        $this->attributes = $this->dehydrate($entity);
86
-    }
87
-
88
-    /**
89
-     * {@inheritdoc}
90
-     */
91
-    public function getEntityClass(): string
92
-    {
93
-        return get_class($this->entity);
94
-    }
95
-
96
-    /**
97
-     * {@inheritdoc}
98
-     */
99
-    public function getEntityKeyName(): string
100
-    {
101
-        return $this->entityMap->getKeyName();
102
-    }
103
-
104
-    /**
105
-     * {@inheritdoc}
106
-     */
107
-    public function getEntityKeyValue()
108
-    {
109
-        return $this->getEntityAttribute($this->entityMap->getKeyName());
110
-    }
111
-
112
-    /**
113
-     * {@inheritdoc}
114
-     */
115
-    public function getEntityHash(): string
116
-    {
117
-        return $this->getEntityClass().'.'.$this->getEntityKeyValue();
118
-    }
119
-
120
-    /**
121
-     * Returns the wrapped entity's map.
122
-     *
123
-     * @return mixed
124
-     */
125
-    public function getMap()
126
-    {
127
-        return $this->entityMap;
128
-    }
129
-
130
-    /**
131
-     * Get hydrated object.
132
-     *
133
-     * @return mixed
134
-     */
135
-    public function unwrap()
136
-    {
137
-        if ($this->touched) {
138
-            $this->hydrate();
139
-        }
140
-
141
-        return $this->entity;
142
-    }
143
-
144
-    /**
145
-     * Returns the wrapped entity.
146
-     *
147
-     * @return mixed
148
-     */
149
-    public function getObject()
150
-    {
151
-        return $this->entity;
152
-    }
153
-
154
-    /**
155
-     * Extract entity attributes / properties to an array of attributes.
156
-     *
157
-     * @param mixed $entity
158
-     *
159
-     * @return array
160
-     */
161
-    protected function dehydrate($entity): array
162
-    {
163
-        $properties = $this->hydrator->extract($entity);
164
-
165
-        $this->properties = $properties;
166
-
167
-        $this->unmanagedProperties = array_except($properties, $this->getManagedProperties());
168
-
169
-        return $this->attributesFromProperties($properties);
170
-    }
171
-
172
-    /**
173
-     * Hydrate object's properties/attribute from the internal array representation.
174
-     *
175
-     * @return void
176
-     */
177
-    protected function hydrate()
178
-    {
179
-        $properties = $this->propertiesFromAttributes() + $this->unmanagedProperties;
180
-
181
-        // In some case, attributes will miss some properties, so we'll just complete the hydration
182
-        // set with the original object properties
183
-        $missingProperties = array_diff_key($this->properties, $properties);
184
-
185
-        foreach (array_keys($missingProperties) as $missingKey) {
186
-            $properties[$missingKey] = $this->properties[$missingKey];
187
-        }
188
-
189
-        $this->hydrator->hydrate($properties, $this->entity);
190
-
191
-        $this->touched = false;
192
-    }
193
-
194
-    /**
195
-     * Return properties that will be extracted from the entity.
196
-     *
197
-     * @return array
198
-     */
199
-    protected function getManagedProperties(): array
200
-    {
201
-        $properties = $this->entityMap->getProperties();
202
-
203
-        $attributesName = $this->entityMap->getAttributesArrayName();
204
-
205
-        return $attributesName == null ? $properties : array_merge($properties, [$attributesName]);
206
-    }
207
-
208
-    /**
209
-     * Convert object's properties to analogue's internal attributes representation.
210
-     *
211
-     * @param array $properties
212
-     *
213
-     * @throws MappingException
214
-     *
215
-     * @return array
216
-     */
217
-    protected function attributesFromProperties(array $properties): array
218
-    {
219
-        // First, we'll only keep the entities that are part of the Entity's
220
-        // attributes
221
-        $managedProperties = $this->getManagedProperties();
222
-
223
-        $properties = array_only($properties, $managedProperties);
224
-
225
-        // If the entity does not uses the attributes array to store
226
-        // part of its attributes, we'll directly return the properties
227
-        if (!$this->entityMap->usesAttributesArray()) {
228
-            return $properties;
229
-        }
230
-
231
-        $arrayName = $this->entityMap->getAttributesArrayName();
232
-
233
-        if (!array_key_exists($arrayName, $properties)) {
234
-            throw new MappingException("Property $arrayName not set on object of type ".$this->getEntityClass());
235
-        }
236
-
237
-        if (!is_array($properties[$arrayName])) {
238
-            throw new MappingException("Property $arrayName should be an array.");
239
-        }
240
-
241
-        $attributes = $properties[$arrayName];
242
-
243
-        unset($properties[$arrayName]);
244
-
245
-        return $properties + $attributes;
246
-    }
247
-
248
-    /**
249
-     * Convert internal representation of attributes to an array of properties
250
-     * that can hydrate the actual object.
251
-     *
252
-     * @return array
253
-     */
254
-    protected function propertiesFromAttributes(): array
255
-    {
256
-        // Get all managed properties
257
-        $propertyNames = $this->entityMap->getProperties();
258
-
259
-        $propertyAttributes = array_only($this->attributes, $propertyNames);
260
-        $attributesArray = array_except($this->attributes, $propertyNames);
261
-
262
-        $attributesArrayName = $this->entityMap->getAttributesArrayName();
263
-
264
-        if ($attributesArrayName) {
265
-            $propertyAttributes[$attributesArrayName] = $attributesArray;
266
-        }
267
-
268
-        return $propertyAttributes;
269
-    }
270
-
271
-    /**
272
-     * {@inheritdoc}
273
-     */
274
-    public function setEntityAttributes(array $attributes)
275
-    {
276
-        $this->attributes = $attributes;
277
-        $this->touched = true;
278
-    }
279
-
280
-    /**
281
-     * {@inheritdoc}
282
-     */
283
-    public function getEntityAttributes(): array
284
-    {
285
-        return $this->attributes;
286
-    }
287
-
288
-    /**
289
-     * {@inheritdoc}
290
-     */
291
-    public function setEntityAttribute(string $key, $value)
292
-    {
293
-        $this->attributes[$key] = $value;
294
-
295
-        $this->touched = true;
296
-
297
-        $this->hydrate();
298
-    }
299
-
300
-    /**
301
-     * {@inheritdoc}
302
-     */
303
-    public function getEntityAttribute(string $key)
304
-    {
305
-        if ($this->hasAttribute($key)) {
306
-            return $this->attributes[$key];
307
-        }
308
-    }
309
-
310
-    /**
311
-     * {@inheritdoc}
312
-     */
313
-    public function hasAttribute(string $key): bool
314
-    {
315
-        return array_key_exists($key, $this->attributes);
316
-    }
317
-
318
-    /**
319
-     * Set the lazy loading proxies on the wrapped entity objet.
320
-     *
321
-     * @param array $relations list of relations to be lazy loaded
322
-     *
323
-     * @return void
324
-     */
325
-    public function setProxies(array $relations = null)
326
-    {
327
-        $attributes = $this->getEntityAttributes();
328
-        $proxies = [];
329
-
330
-        $relations = $this->getRelationsToProxy();
331
-
332
-        // Before calling the relationship methods, we'll set the relationship
333
-        // method to null, to avoid hydration error on class properties
334
-        foreach ($relations as $relation) {
335
-            $this->setEntityAttribute($relation, null);
336
-        }
337
-
338
-        foreach ($relations as $relation) {
339
-
340
-            // First, we check that the relation has not been already
341
-            // set, in which case, we'll just pass.
342
-            if (array_key_exists($relation, $attributes) && !is_null($attributes[$relation])) {
343
-                continue;
344
-            }
345
-
346
-            // If the key is handled locally and we know it not to be set,
347
-            // we'll set the relationship to null value
348
-            if (!$this->relationNeedsProxy($relation, $attributes)) {
349
-                $proxies[$relation] = $this->entityMap->getEmptyValueForRelationship($relation);
350
-            } else {
351
-                $targetClass = $this->getClassToProxy($relation, $attributes);
352
-                $proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
353
-            }
354
-        }
355
-
356
-        foreach ($proxies as $key => $value) {
357
-            $this->setEntityAttribute($key, $value);
358
-        }
359
-    }
360
-
361
-    /**
362
-     * Get Target class to proxy for a one to one.
363
-     *
364
-     * @param string $relation
365
-     * @param array  $attributes
366
-     *
367
-     * @return string
368
-     */
369
-    protected function getClassToProxy($relation, array $attributes)
370
-    {
371
-        if ($this->entityMap->isPolymorphic($relation)) {
372
-            $localTypeAttribute = $this->entityMap->getLocalKeys($relation)['type'];
373
-
374
-            return $attributes[$localTypeAttribute];
375
-        }
376
-
377
-        return $this->entityMap->getTargettedClass($relation);
378
-    }
379
-
380
-    /**
381
-     * Determine which relations we have to build proxy for, by parsing
382
-     * attributes and finding methods that aren't set.
383
-     *
384
-     * @return array
385
-     */
386
-    protected function getRelationsToProxy()
387
-    {
388
-        $proxies = [];
389
-        $attributes = $this->getEntityAttributes();
390
-
391
-        foreach ($this->entityMap->getNonEmbeddedRelationships() as $relation) {
392
-            //foreach ($this->entityMap->getRelationships() as $relation) {
393
-
394
-            if (!array_key_exists($relation, $attributes) || is_null($attributes[$relation])) {
395
-                $proxies[] = $relation;
396
-            }
397
-        }
398
-
399
-        return $proxies;
400
-    }
401
-
402
-    /**
403
-     * Determine if the relation needs a proxy or not.
404
-     *
405
-     * @param string $relation
406
-     * @param array  $attributes
407
-     *
408
-     * @return bool
409
-     */
410
-    protected function relationNeedsProxy($relation, $attributes)
411
-    {
412
-        if (in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
413
-            return false;
414
-        }
415
-
416
-        $localKey = $this->entityMap->getLocalKeys($relation);
417
-
418
-        if (is_null($localKey)) {
419
-            return true;
420
-        }
421
-
422
-        if (is_array($localKey)) {
423
-            $localKey = $localKey['id'];
424
-        }
425
-
426
-        if (!isset($attributes[$localKey]) || is_null($attributes[$localKey])) {
427
-            return false;
428
-        }
429
-
430
-        return true;
431
-    }
16
+	/**
17
+	 * Original Entity Object.
18
+	 *
19
+	 * @var mixed
20
+	 */
21
+	protected $entity;
22
+
23
+	/**
24
+	 * Corresponding EntityMap.
25
+	 *
26
+	 * @var \Analogue\ORM\EntityMap
27
+	 */
28
+	protected $entityMap;
29
+
30
+	/**
31
+	 * @var \Analogue\ORM\System\Proxies\ProxyFactory
32
+	 */
33
+	protected $proxyFactory;
34
+
35
+	/**
36
+	 * Internal Representation of analogue's entity attributes.
37
+	 *
38
+	 * @var array
39
+	 */
40
+	protected $attributes = [];
41
+
42
+	/**
43
+	 * All properties on the original object.
44
+	 *
45
+	 * @var array
46
+	 */
47
+	protected $properties = [];
48
+
49
+	/**
50
+	 * Object properties that are not a part of the entity attributes,
51
+	 * but which are needed to correctly hydrate the Object.
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $unmanagedProperties = [];
56
+
57
+	/**
58
+	 * The hydrator for the wrapped object.
59
+	 *
60
+	 * @var HydratorInterface
61
+	 */
62
+	protected $hydrator;
63
+
64
+	/**
65
+	 * Set to true if the object's attributes have been modified since last
66
+	 * hydration.
67
+	 *
68
+	 * @var bool
69
+	 */
70
+	protected $touched = false;
71
+
72
+	/**
73
+	 * Object Wrapper constructor.
74
+	 *
75
+	 * @param mixed                   $entity
76
+	 * @param \Analogue\ORM\EntityMap $entityMap
77
+	 * @param HydratorInterface       $hydrator
78
+	 */
79
+	public function __construct($entity, $entityMap, HydratorInterface $hydrator)
80
+	{
81
+		$this->hydrator = $hydrator;
82
+		$this->entity = $entity;
83
+		$this->entityMap = $entityMap;
84
+		$this->proxyFactory = new ProxyFactory();
85
+		$this->attributes = $this->dehydrate($entity);
86
+	}
87
+
88
+	/**
89
+	 * {@inheritdoc}
90
+	 */
91
+	public function getEntityClass(): string
92
+	{
93
+		return get_class($this->entity);
94
+	}
95
+
96
+	/**
97
+	 * {@inheritdoc}
98
+	 */
99
+	public function getEntityKeyName(): string
100
+	{
101
+		return $this->entityMap->getKeyName();
102
+	}
103
+
104
+	/**
105
+	 * {@inheritdoc}
106
+	 */
107
+	public function getEntityKeyValue()
108
+	{
109
+		return $this->getEntityAttribute($this->entityMap->getKeyName());
110
+	}
111
+
112
+	/**
113
+	 * {@inheritdoc}
114
+	 */
115
+	public function getEntityHash(): string
116
+	{
117
+		return $this->getEntityClass().'.'.$this->getEntityKeyValue();
118
+	}
119
+
120
+	/**
121
+	 * Returns the wrapped entity's map.
122
+	 *
123
+	 * @return mixed
124
+	 */
125
+	public function getMap()
126
+	{
127
+		return $this->entityMap;
128
+	}
129
+
130
+	/**
131
+	 * Get hydrated object.
132
+	 *
133
+	 * @return mixed
134
+	 */
135
+	public function unwrap()
136
+	{
137
+		if ($this->touched) {
138
+			$this->hydrate();
139
+		}
140
+
141
+		return $this->entity;
142
+	}
143
+
144
+	/**
145
+	 * Returns the wrapped entity.
146
+	 *
147
+	 * @return mixed
148
+	 */
149
+	public function getObject()
150
+	{
151
+		return $this->entity;
152
+	}
153
+
154
+	/**
155
+	 * Extract entity attributes / properties to an array of attributes.
156
+	 *
157
+	 * @param mixed $entity
158
+	 *
159
+	 * @return array
160
+	 */
161
+	protected function dehydrate($entity): array
162
+	{
163
+		$properties = $this->hydrator->extract($entity);
164
+
165
+		$this->properties = $properties;
166
+
167
+		$this->unmanagedProperties = array_except($properties, $this->getManagedProperties());
168
+
169
+		return $this->attributesFromProperties($properties);
170
+	}
171
+
172
+	/**
173
+	 * Hydrate object's properties/attribute from the internal array representation.
174
+	 *
175
+	 * @return void
176
+	 */
177
+	protected function hydrate()
178
+	{
179
+		$properties = $this->propertiesFromAttributes() + $this->unmanagedProperties;
180
+
181
+		// In some case, attributes will miss some properties, so we'll just complete the hydration
182
+		// set with the original object properties
183
+		$missingProperties = array_diff_key($this->properties, $properties);
184
+
185
+		foreach (array_keys($missingProperties) as $missingKey) {
186
+			$properties[$missingKey] = $this->properties[$missingKey];
187
+		}
188
+
189
+		$this->hydrator->hydrate($properties, $this->entity);
190
+
191
+		$this->touched = false;
192
+	}
193
+
194
+	/**
195
+	 * Return properties that will be extracted from the entity.
196
+	 *
197
+	 * @return array
198
+	 */
199
+	protected function getManagedProperties(): array
200
+	{
201
+		$properties = $this->entityMap->getProperties();
202
+
203
+		$attributesName = $this->entityMap->getAttributesArrayName();
204
+
205
+		return $attributesName == null ? $properties : array_merge($properties, [$attributesName]);
206
+	}
207
+
208
+	/**
209
+	 * Convert object's properties to analogue's internal attributes representation.
210
+	 *
211
+	 * @param array $properties
212
+	 *
213
+	 * @throws MappingException
214
+	 *
215
+	 * @return array
216
+	 */
217
+	protected function attributesFromProperties(array $properties): array
218
+	{
219
+		// First, we'll only keep the entities that are part of the Entity's
220
+		// attributes
221
+		$managedProperties = $this->getManagedProperties();
222
+
223
+		$properties = array_only($properties, $managedProperties);
224
+
225
+		// If the entity does not uses the attributes array to store
226
+		// part of its attributes, we'll directly return the properties
227
+		if (!$this->entityMap->usesAttributesArray()) {
228
+			return $properties;
229
+		}
230
+
231
+		$arrayName = $this->entityMap->getAttributesArrayName();
232
+
233
+		if (!array_key_exists($arrayName, $properties)) {
234
+			throw new MappingException("Property $arrayName not set on object of type ".$this->getEntityClass());
235
+		}
236
+
237
+		if (!is_array($properties[$arrayName])) {
238
+			throw new MappingException("Property $arrayName should be an array.");
239
+		}
240
+
241
+		$attributes = $properties[$arrayName];
242
+
243
+		unset($properties[$arrayName]);
244
+
245
+		return $properties + $attributes;
246
+	}
247
+
248
+	/**
249
+	 * Convert internal representation of attributes to an array of properties
250
+	 * that can hydrate the actual object.
251
+	 *
252
+	 * @return array
253
+	 */
254
+	protected function propertiesFromAttributes(): array
255
+	{
256
+		// Get all managed properties
257
+		$propertyNames = $this->entityMap->getProperties();
258
+
259
+		$propertyAttributes = array_only($this->attributes, $propertyNames);
260
+		$attributesArray = array_except($this->attributes, $propertyNames);
261
+
262
+		$attributesArrayName = $this->entityMap->getAttributesArrayName();
263
+
264
+		if ($attributesArrayName) {
265
+			$propertyAttributes[$attributesArrayName] = $attributesArray;
266
+		}
267
+
268
+		return $propertyAttributes;
269
+	}
270
+
271
+	/**
272
+	 * {@inheritdoc}
273
+	 */
274
+	public function setEntityAttributes(array $attributes)
275
+	{
276
+		$this->attributes = $attributes;
277
+		$this->touched = true;
278
+	}
279
+
280
+	/**
281
+	 * {@inheritdoc}
282
+	 */
283
+	public function getEntityAttributes(): array
284
+	{
285
+		return $this->attributes;
286
+	}
287
+
288
+	/**
289
+	 * {@inheritdoc}
290
+	 */
291
+	public function setEntityAttribute(string $key, $value)
292
+	{
293
+		$this->attributes[$key] = $value;
294
+
295
+		$this->touched = true;
296
+
297
+		$this->hydrate();
298
+	}
299
+
300
+	/**
301
+	 * {@inheritdoc}
302
+	 */
303
+	public function getEntityAttribute(string $key)
304
+	{
305
+		if ($this->hasAttribute($key)) {
306
+			return $this->attributes[$key];
307
+		}
308
+	}
309
+
310
+	/**
311
+	 * {@inheritdoc}
312
+	 */
313
+	public function hasAttribute(string $key): bool
314
+	{
315
+		return array_key_exists($key, $this->attributes);
316
+	}
317
+
318
+	/**
319
+	 * Set the lazy loading proxies on the wrapped entity objet.
320
+	 *
321
+	 * @param array $relations list of relations to be lazy loaded
322
+	 *
323
+	 * @return void
324
+	 */
325
+	public function setProxies(array $relations = null)
326
+	{
327
+		$attributes = $this->getEntityAttributes();
328
+		$proxies = [];
329
+
330
+		$relations = $this->getRelationsToProxy();
331
+
332
+		// Before calling the relationship methods, we'll set the relationship
333
+		// method to null, to avoid hydration error on class properties
334
+		foreach ($relations as $relation) {
335
+			$this->setEntityAttribute($relation, null);
336
+		}
337
+
338
+		foreach ($relations as $relation) {
339
+
340
+			// First, we check that the relation has not been already
341
+			// set, in which case, we'll just pass.
342
+			if (array_key_exists($relation, $attributes) && !is_null($attributes[$relation])) {
343
+				continue;
344
+			}
345
+
346
+			// If the key is handled locally and we know it not to be set,
347
+			// we'll set the relationship to null value
348
+			if (!$this->relationNeedsProxy($relation, $attributes)) {
349
+				$proxies[$relation] = $this->entityMap->getEmptyValueForRelationship($relation);
350
+			} else {
351
+				$targetClass = $this->getClassToProxy($relation, $attributes);
352
+				$proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
353
+			}
354
+		}
355
+
356
+		foreach ($proxies as $key => $value) {
357
+			$this->setEntityAttribute($key, $value);
358
+		}
359
+	}
360
+
361
+	/**
362
+	 * Get Target class to proxy for a one to one.
363
+	 *
364
+	 * @param string $relation
365
+	 * @param array  $attributes
366
+	 *
367
+	 * @return string
368
+	 */
369
+	protected function getClassToProxy($relation, array $attributes)
370
+	{
371
+		if ($this->entityMap->isPolymorphic($relation)) {
372
+			$localTypeAttribute = $this->entityMap->getLocalKeys($relation)['type'];
373
+
374
+			return $attributes[$localTypeAttribute];
375
+		}
376
+
377
+		return $this->entityMap->getTargettedClass($relation);
378
+	}
379
+
380
+	/**
381
+	 * Determine which relations we have to build proxy for, by parsing
382
+	 * attributes and finding methods that aren't set.
383
+	 *
384
+	 * @return array
385
+	 */
386
+	protected function getRelationsToProxy()
387
+	{
388
+		$proxies = [];
389
+		$attributes = $this->getEntityAttributes();
390
+
391
+		foreach ($this->entityMap->getNonEmbeddedRelationships() as $relation) {
392
+			//foreach ($this->entityMap->getRelationships() as $relation) {
393
+
394
+			if (!array_key_exists($relation, $attributes) || is_null($attributes[$relation])) {
395
+				$proxies[] = $relation;
396
+			}
397
+		}
398
+
399
+		return $proxies;
400
+	}
401
+
402
+	/**
403
+	 * Determine if the relation needs a proxy or not.
404
+	 *
405
+	 * @param string $relation
406
+	 * @param array  $attributes
407
+	 *
408
+	 * @return bool
409
+	 */
410
+	protected function relationNeedsProxy($relation, $attributes)
411
+	{
412
+		if (in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
413
+			return false;
414
+		}
415
+
416
+		$localKey = $this->entityMap->getLocalKeys($relation);
417
+
418
+		if (is_null($localKey)) {
419
+			return true;
420
+		}
421
+
422
+		if (is_array($localKey)) {
423
+			$localKey = $localKey['id'];
424
+		}
425
+
426
+		if (!isset($attributes[$localKey]) || is_null($attributes[$localKey])) {
427
+			return false;
428
+		}
429
+
430
+		return true;
431
+	}
432 432
 }
Please login to merge, or discard this patch.
src/Events/Event.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -4,20 +4,20 @@
 block discarded – undo
4 4
 
5 5
 abstract class Event
6 6
 {
7
-    /**
8
-     * Entity.
9
-     *
10
-     * @var mixed
11
-     */
12
-    public $entity;
7
+	/**
8
+	 * Entity.
9
+	 *
10
+	 * @var mixed
11
+	 */
12
+	public $entity;
13 13
 
14
-    /**
15
-     * Event constructor.
16
-     *
17
-     * @param mixed $entity
18
-     */
19
-    public function __construct($entity)
20
-    {
21
-        $this->entity = $entity;
22
-    }
14
+	/**
15
+	 * Event constructor.
16
+	 *
17
+	 * @param mixed $entity
18
+	 */
19
+	public function __construct($entity)
20
+	{
21
+		$this->entity = $entity;
22
+	}
23 23
 }
Please login to merge, or discard this patch.
src/Events/Initialized.php 1 patch
Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -6,20 +6,20 @@
 block discarded – undo
6 6
 
7 7
 class Initialized
8 8
 {
9
-    /**
10
-     * Mapper instance.
11
-     *
12
-     * @var Mapper
13
-     */
14
-    public $mapper;
9
+	/**
10
+	 * Mapper instance.
11
+	 *
12
+	 * @var Mapper
13
+	 */
14
+	public $mapper;
15 15
 
16
-    /**
17
-     * Initialized constructor.
18
-     *
19
-     * @param Mapper $mapper
20
-     */
21
-    public function __construct(Mapper $mapper)
22
-    {
23
-        $this->mapper = $mapper;
24
-    }
16
+	/**
17
+	 * Initialized constructor.
18
+	 *
19
+	 * @param Mapper $mapper
20
+	 */
21
+	public function __construct(Mapper $mapper)
22
+	{
23
+		$this->mapper = $mapper;
24
+	}
25 25
 }
Please login to merge, or discard this patch.
src/Mappable.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -7,17 +7,17 @@
 block discarded – undo
7 7
  */
8 8
 interface Mappable
9 9
 {
10
-    /**
11
-     * Set the object attribute raw values (hydration).
12
-     *
13
-     * @param array $attributes
14
-     */
15
-    public function setEntityAttributes(array $attributes);
10
+	/**
11
+	 * Set the object attribute raw values (hydration).
12
+	 *
13
+	 * @param array $attributes
14
+	 */
15
+	public function setEntityAttributes(array $attributes);
16 16
 
17
-    /**
18
-     * Get the raw object's values.
19
-     *
20
-     * @return array
21
-     */
22
-    public function getEntityAttributes();
17
+	/**
18
+	 * Get the raw object's values.
19
+	 *
20
+	 * @return array
21
+	 */
22
+	public function getEntityAttributes();
23 23
 }
Please login to merge, or discard this patch.
src/Drivers/CapsuleConnectionProvider.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -7,32 +7,32 @@
 block discarded – undo
7 7
 
8 8
 class CapsuleConnectionProvider
9 9
 {
10
-    /**
11
-     * Capsule manager.
12
-     *
13
-     * @var Capsule
14
-     */
15
-    protected $capsule;
10
+	/**
11
+	 * Capsule manager.
12
+	 *
13
+	 * @var Capsule
14
+	 */
15
+	protected $capsule;
16 16
 
17
-    /**
18
-     * CapsuleConnectionProvider constructor.
19
-     *
20
-     * @param Capsule $capsule
21
-     */
22
-    public function __construct(Capsule $capsule)
23
-    {
24
-        $this->capsule = $capsule;
25
-    }
17
+	/**
18
+	 * CapsuleConnectionProvider constructor.
19
+	 *
20
+	 * @param Capsule $capsule
21
+	 */
22
+	public function __construct(Capsule $capsule)
23
+	{
24
+		$this->capsule = $capsule;
25
+	}
26 26
 
27
-    /**
28
-     * Get a Database connection object.
29
-     *
30
-     * @param string $name
31
-     *
32
-     * @return \Illuminate\Database\Connection
33
-     */
34
-    public function connection(string $name = null): Connection
35
-    {
36
-        return $this->capsule->getConnection($name);
37
-    }
27
+	/**
28
+	 * Get a Database connection object.
29
+	 *
30
+	 * @param string $name
31
+	 *
32
+	 * @return \Illuminate\Database\Connection
33
+	 */
34
+	public function connection(string $name = null): Connection
35
+	{
36
+		return $this->capsule->getConnection($name);
37
+	}
38 38
 }
Please login to merge, or discard this patch.
src/Drivers/IlluminateConnectionProvider.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -7,32 +7,32 @@
 block discarded – undo
7 7
 
8 8
 class IlluminateConnectionProvider
9 9
 {
10
-    /**
11
-     * Database manager.
12
-     *
13
-     * @var DatabaseManager
14
-     */
15
-    protected $db;
10
+	/**
11
+	 * Database manager.
12
+	 *
13
+	 * @var DatabaseManager
14
+	 */
15
+	protected $db;
16 16
 
17
-    /**
18
-     * IlluminateConnectionProvider constructor.
19
-     *
20
-     * @param DatabaseManager $db
21
-     */
22
-    public function __construct(DatabaseManager $db)
23
-    {
24
-        $this->db = $db;
25
-    }
17
+	/**
18
+	 * IlluminateConnectionProvider constructor.
19
+	 *
20
+	 * @param DatabaseManager $db
21
+	 */
22
+	public function __construct(DatabaseManager $db)
23
+	{
24
+		$this->db = $db;
25
+	}
26 26
 
27
-    /**
28
-     * Get a Database connection object.
29
-     *
30
-     * @param string $name
31
-     *
32
-     * @return \Illuminate\Database\Connection
33
-     */
34
-    public function connection(string $name = null): Connection
35
-    {
36
-        return $this->db->connection($name);
37
-    }
27
+	/**
28
+	 * Get a Database connection object.
29
+	 *
30
+	 * @param string $name
31
+	 *
32
+	 * @return \Illuminate\Database\Connection
33
+	 */
34
+	public function connection(string $name = null): Connection
35
+	{
36
+		return $this->db->connection($name);
37
+	}
38 38
 }
Please login to merge, or discard this patch.
src/Drivers/IlluminateDBAdapter.php 1 patch
Indentation   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -10,72 +10,72 @@
 block discarded – undo
10 10
  */
11 11
 class IlluminateDBAdapter implements DBAdapter
12 12
 {
13
-    /**
14
-     * Database connection.
15
-     *
16
-     * @var Connection
17
-     */
18
-    protected $connection;
13
+	/**
14
+	 * Database connection.
15
+	 *
16
+	 * @var Connection
17
+	 */
18
+	protected $connection;
19 19
 
20
-    /**
21
-     * IlluminateDBAdapter constructor.
22
-     *
23
-     * @param Connection $connection
24
-     */
25
-    public function __construct(Connection $connection)
26
-    {
27
-        $this->connection = $connection;
28
-    }
20
+	/**
21
+	 * IlluminateDBAdapter constructor.
22
+	 *
23
+	 * @param Connection $connection
24
+	 */
25
+	public function __construct(Connection $connection)
26
+	{
27
+		$this->connection = $connection;
28
+	}
29 29
 
30
-    /**
31
-     * {@inheritdoc}
32
-     */
33
-    public function getQuery()
34
-    {
35
-        $connection = $this->connection;
30
+	/**
31
+	 * {@inheritdoc}
32
+	 */
33
+	public function getQuery()
34
+	{
35
+		$connection = $this->connection;
36 36
 
37
-        $grammar = $connection->getQueryGrammar();
37
+		$grammar = $connection->getQueryGrammar();
38 38
 
39
-        return new IlluminateQueryBuilder($connection, $grammar, $connection->getPostProcessor());
40
-    }
39
+		return new IlluminateQueryBuilder($connection, $grammar, $connection->getPostProcessor());
40
+	}
41 41
 
42
-    /**
43
-     * {@inheritdoc}
44
-     */
45
-    public function getDateFormat(): string
46
-    {
47
-        return $this->connection->getQueryGrammar()->getDateFormat();
48
-    }
42
+	/**
43
+	 * {@inheritdoc}
44
+	 */
45
+	public function getDateFormat(): string
46
+	{
47
+		return $this->connection->getQueryGrammar()->getDateFormat();
48
+	}
49 49
 
50
-    /**
51
-     * {@inheritdoc}
52
-     */
53
-    public function beginTransaction()
54
-    {
55
-        $this->connection->beginTransaction();
56
-    }
50
+	/**
51
+	 * {@inheritdoc}
52
+	 */
53
+	public function beginTransaction()
54
+	{
55
+		$this->connection->beginTransaction();
56
+	}
57 57
 
58
-    /**
59
-     * {@inheritdoc}
60
-     */
61
-    public function commit()
62
-    {
63
-        $this->connection->commit();
64
-    }
58
+	/**
59
+	 * {@inheritdoc}
60
+	 */
61
+	public function commit()
62
+	{
63
+		$this->connection->commit();
64
+	}
65 65
 
66
-    /**
67
-     * {@inheritdoc}
68
-     */
69
-    public function rollback()
70
-    {
71
-        $this->connection->rollBack();
72
-    }
66
+	/**
67
+	 * {@inheritdoc}
68
+	 */
69
+	public function rollback()
70
+	{
71
+		$this->connection->rollBack();
72
+	}
73 73
 
74
-    /**
75
-     * {@inheritdoc}
76
-     */
77
-    public function fromDatabase(array $rows): array
78
-    {
79
-        return $rows;
80
-    }
74
+	/**
75
+	 * {@inheritdoc}
76
+	 */
77
+	public function fromDatabase(array $rows): array
78
+	{
79
+		return $rows;
80
+	}
81 81
 }
Please login to merge, or discard this patch.