Completed
Branch master (cbd196)
by Rémi
08:54
created

EntityMap::getNonEmbeddedRelationships()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Analogue\ORM;
4
5
use Analogue\ORM\Exceptions\MappingException;
6
use Analogue\ORM\Relationships\BelongsTo;
7
use Analogue\ORM\Relationships\BelongsToMany;
8
use Analogue\ORM\Relationships\EmbedsMany;
9
use Analogue\ORM\Relationships\EmbedsOne;
10
use Analogue\ORM\Relationships\HasMany;
11
use Analogue\ORM\Relationships\HasManyThrough;
12
use Analogue\ORM\Relationships\HasOne;
13
use Analogue\ORM\Relationships\MorphMany;
14
use Analogue\ORM\Relationships\MorphOne;
15
use Analogue\ORM\Relationships\MorphTo;
16
use Analogue\ORM\Relationships\MorphToMany;
17
use Analogue\ORM\System\Manager;
18
use Analogue\ORM\System\Wrappers\Factory;
19
use Exception;
20
use ReflectionClass;
21
22
/**
23
 * The Entity Map defines the Mapping behaviour of an Entity,
24
 * including relationships.
25
 */
26
class EntityMap
27
{
28
    /**
29
     * The mapping driver to use with this entity.
30
     *
31
     * @var string
32
     */
33
    protected $driver = 'illuminate';
34
35
    /**
36
     * The Database Connection name for the model.
37
     *
38
     * @var string
39
     */
40
    protected $connection;
41
42
    /**
43
     * The table associated with the entity.
44
     *
45
     * @var string|null
46
     */
47
    protected $table = null;
48
49
    /**
50
     * The primary key for the model.
51
     *
52
     * @var string
53
     */
54
    protected $primaryKey = 'id';
55
56
    /**
57
     * Name of the entity's array property that should
58
     * contain the attributes.
59
     * If set to null, analogue will only hydrate object's properties.
60
     *
61
     * @var string|null
62
     */
63
    protected $arrayName = 'attributes';
64
65
    /**
66
     * Array containing the list of database columns to be mapped
67
     * in the attributes array of the entity.
68
     *
69
     * @var array
70
     */
71
    protected $attributes = [];
72
73
    /**
74
     * Array containing the list of database columns to be mapped
75
     * to the entity's class properties.
76
     *
77
     * @var array
78
     */
79
    protected $properties = [];
80
81
    /**
82
     * The Custom Domain Class to use with this mapping.
83
     *
84
     * @var string|null
85
     */
86
    protected $class = null;
87
88
    /**
89
     * Embedded Value Objects.
90
     *
91
     * @var array
92
     */
93
    protected $embeddables = [];
94
95
    /**
96
     * Determine the relationships method used on the entity.
97
     * If not set, mapper will autodetect them.
98
     *
99
     * @var array
100
     */
101
    private $relationships = [];
102
103
    /**
104
     * Relationships that should be treated as collection.
105
     *
106
     * @var array
107
     */
108
    private $manyRelations = [];
109
110
    /**
111
     * Relationships that should be treated as single entity.
112
     *
113
     * @var array
114
     */
115
    private $singleRelations = [];
116
117
    /**
118
     * Relationships for which the key is stored in the Entity itself.
119
     *
120
     * @var array
121
     */
122
    private $localRelations = [];
123
124
    /**
125
     * List of local keys associated to local relation methods.
126
     *
127
     * @var array
128
     */
129
    private $localForeignKeys = [];
130
131
    /**
132
     * Relationships for which the key is stored in the Related Entity.
133
     *
134
     * @var array
135
     */
136
    private $foreignRelations = [];
137
138
    /**
139
     * Relationships which use a pivot record.
140
     *
141
     * @var array
142
     */
143
    private $pivotRelations = [];
144
145
    /**
146
     * Polymorphic relationships.
147
     *
148
     * @var array
149
     */
150
    private $polymorphicRelations = [];
151
152
    /**
153
     * Dynamic relationships.
154
     *
155
     * @var array
156
     */
157
    private $dynamicRelationships = [];
158
159
    /**
160
     * Targetted class for the relationship method. value is set to `null` for
161
     * polymorphic relations.
162
     *
163
     * @var array
164
     */
165
    private $relatedClasses = [];
166
167
    /**
168
     * Some relation methods like embedded objects, or HasOne and MorphOne,
169
     * will never have a proxy loaded on them.
170
     *
171
     * @var array
172
     */
173
    private $nonProxyRelationships = [];
174
175
    /**
176
     * Relation methods that are embedded objects.
177
     *
178
     * @var array
179
     */
180
    private $embeddedRelations = [];
181
182
    /**
183
     * The number of models to return for pagination.
184
     *
185
     * @var int
186
     */
187
    protected $perPage = 15;
188
189
    /**
190
     * The relations to eager load on every query.
191
     *
192
     * @var array
193
     */
194
    protected $with = [];
195
196
    /**
197
     * The class name to be used in polymorphic relations.
198
     *
199
     * @var string
200
     */
201
    protected $morphClass;
202
203
    /**
204
     * Sequence name, to be used with postgreSql
205
     * defaults to %table_name%_id_seq.
206
     *
207
     * @var string|null
208
     */
209
    protected $sequence = null;
210
211
    /**
212
     * Indicates if the entity should be timestamped.
213
     *
214
     * @var bool
215
     */
216
    public $timestamps = false;
217
218
    /**
219
     * The name of the "created at" column.
220
     *
221
     * @var string
222
     */
223
    protected $createdAtColumn = 'created_at';
224
225
    /**
226
     * The name of the "updated at" column.
227
     *
228
     * @var string
229
     */
230
    protected $updatedAtColumn = 'updated_at';
231
232
    /**
233
     * Indicates if the entity uses softdeletes.
234
     *
235
     * @var bool
236
     */
237
    public $softDeletes = false;
238
239
    /**
240
     * The name of the "deleted at" column.
241
     *
242
     * @var string
243
     */
244
    protected $deletedAtColumn = 'deleted_at';
245
246
    /**
247
     * The date format to use with the current database connection.
248
     *
249
     * @var string
250
     */
251
    protected $dateFormat;
252
253
    /**
254
     * Set this property to true if the entity should be instantiated
255
     * using the IoC Container.
256
     *
257
     * @var bool
258
     */
259
    protected $dependencyInjection = false;
260
261
    /**
262
     * Set the usage of inheritance, possible values are :
263
     * "single_table"
264
     * null.
265
     *
266
     * @var string | null
267
     */
268
    protected $inheritanceType = null;
269
270
    /**
271
     * Discriminator column name.
272
     *
273
     * @var string
274
     */
275
    protected $discriminatorColumn = 'type';
276
277
    /**
278
     * Allow using a string to define which entity type should be instantiated.
279
     * If not set, analogue will uses entity's FQDN.
280
     *
281
     * @var array
282
     */
283
    protected $discriminatorColumnMap = [];
284
285
    /**
286
     * Return Domain class attributes, useful when mapping to a Plain PHP Object.
287
     *
288
     * @return array
289
     */
290
    public function getAttributes() : array
291
    {
292
        return $this->attributes;
293
    }
294
295
    /**
296
     * Set the domain class attributes.
297
     *
298
     * @param array $attributeNames
299
     */
300
    public function setAttributes(array $attributeNames)
301
    {
302
        $this->attributes = $attributeNames;
303
    }
304
305
    /**
306
     * Return true if the Entity has an 'attributes' array property.
307
     *
308
     * @return bool
309
     */
310
    public function usesAttributesArray() : bool
311
    {
312
        if ($this->arrayName === null) {
313
            return false;
314
        }
315
        if ($this->attributes === null) {
316
            return false;
317
        }
318
319
        return true;
320
    }
321
322
    /**
323
     * Return the name of the Entity's attributes property.
324
     *
325
     * @return string|null
326
     */
327
    public function getAttributesArrayName()
328
    {
329
        return $this->arrayName;
330
    }
331
332
    /**
333
     * Get all the attribute names for the class, including relationships, embeddables and primary key.
334
     *
335
     * @return array
336
     */
337
    public function getCompiledAttributes() : array
338
    {
339
        $key = $this->getKeyName();
340
341
        $embeddables = array_keys($this->getEmbeddables());
342
343
        $relationships = $this->getRelationships();
344
345
        $attributes = $this->getAttributes();
346
347
        return array_merge([$key], $embeddables, $relationships, $attributes);
348
    }
349
350
    /**
351
     * Set the date format to use with the current database connection.
352
     *
353
     * @param string $format
354
     */
355
    public function setDateFormat($format)
356
    {
357
        $this->dateFormat = $format;
358
    }
359
360
    /**
361
     * Get the date format to use with the current database connection.
362
     *
363
     *  @return string
364
     */
365
    public function getDateFormat() : string
366
    {
367
        return $this->dateFormat;
368
    }
369
370
    /**
371
     * Set the Driver for this mapping.
372
     *
373
     * @param string $driver
374
     */
375
    public function setDriver($driver)
376
    {
377
        $this->driver = $driver;
378
    }
379
380
    /**
381
     * Get the Driver for this mapping.
382
     *
383
     * @return string
384
     */
385
    public function getDriver() : string
386
    {
387
        return $this->driver;
388
    }
389
390
    /**
391
     * Set the db connection to use on the table.
392
     *
393
     * @param $connection
394
     */
395
    public function setConnection($connection)
396
    {
397
        $this->connection = $connection;
398
    }
399
400
    /**
401
     * Get the Database connection the Entity is stored on.
402
     *
403
     * @return string | null
404
     */
405
    public function getConnection()
406
    {
407
        return $this->connection;
408
    }
409
410
    /**
411
     * Get the table associated with the entity.
412
     *
413
     * @return string
414
     */
415
    public function getTable() : string
416
    {
417
        if (!is_null($this->table)) {
418
            return $this->table;
419
        }
420
421
        return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
422
    }
423
424
    /**
425
     * Set the database table name.
426
     *
427
     * @param string $table
428
     */
429
    public function setTable($table)
430
    {
431
        $this->table = $table;
432
    }
433
434
    /**
435
     * Get the pgSql sequence name.
436
     *
437
     * @return string
438
     */
439
    public function getSequence()
440
    {
441
        if (!is_null($this->sequence)) {
442
            return $this->sequence;
443
        } else {
444
            return $this->getTable().'_id_seq';
445
        }
446
    }
447
448
    /**
449
     * Get the custom entity class.
450
     *
451
     * @return string namespaced class name
452
     */
453
    public function getClass() : string
454
    {
455
        return isset($this->class) ? $this->class : null;
456
    }
457
458
    /**
459
     * Set the custom entity class.
460
     *
461
     * @param string $class namespaced class name
462
     */
463
    public function setClass($class)
464
    {
465
        $this->class = $class;
466
    }
467
468
    /**
469
     * Get the embedded Value Objects.
470
     *
471
     * @return array
472
     */
473
    public function getEmbeddables() : array
474
    {
475
        return $this->embeddables;
476
    }
477
478
    /**
479
     * Return attributes that should be mapped to class properties.
480
     *
481
     * @return array
482
     */
483
    public function getProperties() : array
484
    {
485
        return $this->properties;
486
    }
487
488
    /**
489
     * Return the array property in which will be mapped all attributes
490
     * that are not mapped to class properties.
491
     *
492
     * @return string
493
     */
494
    public function getAttributesPropertyName() : string
495
    {
496
    }
497
498
    /**
499
     * Set the embedded Value Objects.
500
     *
501
     * @param array $embeddables
502
     */
503
    public function setEmbeddables(array $embeddables)
504
    {
505
        $this->embeddables = $embeddables;
506
    }
507
508
    /**
509
     * Get the relationships to map on a custom domain
510
     * class.
511
     *
512
     * @return array
513
     */
514
    public function getRelationships() : array
515
    {
516
        return $this->relationships;
517
    }
518
519
    /**
520
     * Return all relationships that are not embedded objects.
521
     *
522
     * @return array
523
     */
524
    public function getNonEmbeddedRelationships() : array
525
    {
526
        return array_diff($this->relationships, $this->embeddedRelations);
527
    }
528
529
    /**
530
     * Get the relationships that will not have a proxy
531
     * set on them.
532
     *
533
     * @return array
534
     */
535
    public function getRelationshipsWithoutProxy() : array
536
    {
537
        return $this->nonProxyRelationships;
538
    }
539
540
    /**
541
     * Relationships of the Entity type.
542
     *
543
     * @return array
544
     */
545
    public function getSingleRelationships() : array
546
    {
547
        return $this->singleRelations;
548
    }
549
550
    /**
551
     * Relationships of type Collection.
552
     *
553
     * @return array
554
     */
555
    public function getManyRelationships() : array
556
    {
557
        return $this->manyRelations;
558
    }
559
560
    /**
561
     * Relationships with foreign key in the mapped entity record.
562
     *
563
     * @return array
564
     */
565
    public function getLocalRelationships() : array
566
    {
567
        return $this->localRelations;
568
    }
569
570
    /**
571
     * Return the local keys associated to the relationship.
572
     *
573
     * @param string $relation
574
     *
575
     * @return string | array | null
576
     */
577
    public function getLocalKeys($relation)
578
    {
579
        return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
580
    }
581
582
    /**
583
     * Relationships with foreign key in the related Entity record.
584
     *
585
     * @return array
586
     */
587
    public function getForeignRelationships() : array
588
    {
589
        return $this->foreignRelations;
590
    }
591
592
    /**
593
     * Relationships which keys are stored in a pivot record.
594
     *
595
     * @return array
596
     */
597
    public function getPivotRelationships() : array
598
    {
599
        return $this->pivotRelations;
600
    }
601
602
    /**
603
     * Return an array containing all embedded relationships.
604
     *
605
     * @return array
606
     */
607
    public function getEmbeddedRelationships() : array
608
    {
609
        return $this->embeddedRelations;
610
    }
611
612
    /**
613
     * Return true if the relationship method is polymorphic.
614
     *
615
     * @param string $relation
616
     *
617
     * @return bool
618
     */
619
    public function isPolymorphic($relation) : bool
620
    {
621
        return in_array($relation, $this->polymorphicRelations);
622
    }
623
624
    /**
625
     * Get the targetted type for a relationship. Return null if polymorphic.
626
     *
627
     * @param string $relation
628
     *
629
     * @return string | null
630
     */
631
    public function getTargettedClass($relation)
632
    {
633
        if (!array_key_exists($relation, $this->relatedClasses)) {
634
            return;
635
        }
636
637
        return $this->relatedClasses[$relation];
638
    }
639
640
    /**
641
     * Add a Dynamic Relationship method at runtime. This has to be done
642
     * by hooking the 'initializing' event, before entityMap is initialized.
643
     *
644
     * @param string   $name         Relation name
645
     * @param \Closure $relationship
646
     *
647
     * @return void
648
     */
649
    public function addRelationshipMethod($name, \Closure $relationship)
650
    {
651
        $this->dynamicRelationships[$name] = $relationship;
652
    }
653
654
    /**
655
     * Get the dynamic relationship method names.
656
     *
657
     * @return array
658
     */
659
    public function getDynamicRelationships() : array
660
    {
661
        return array_keys($this->dynamicRelationships);
662
    }
663
664
    /**
665
     * Get the relationships that have to be eager loaded
666
     * on each request.
667
     *
668
     * @return array
669
     */
670
    public function getEagerloadedRelationships() : array
671
    {
672
        return $this->with;
673
    }
674
675
    /**
676
     * Get the primary key attribute for the entity.
677
     *
678
     * @return string
679
     */
680
    public function getKeyName() : string
681
    {
682
        return $this->primaryKey;
683
    }
684
685
    /**
686
     * Set the primary key for the entity.
687
     *
688
     * @param $key
689
     *
690
     * @return void
691
     */
692
    public function setKeyName($key)
693
    {
694
        $this->primaryKey = $key;
695
    }
696
697
    /**
698
     * Get the table qualified key name.
699
     *
700
     * @return string
701
     */
702
    public function getQualifiedKeyName() : string
703
    {
704
        return $this->getTable().'.'.$this->getKeyName();
705
    }
706
707
    /**
708
     * Get the number of models to return per page.
709
     *
710
     * @return int
711
     */
712
    public function getPerPage() : int
713
    {
714
        return $this->perPage;
715
    }
716
717
    /**
718
     * Set the number of models to return per page.
719
     *
720
     * @param int $perPage
721
     *
722
     * @return void
723
     */
724
    public function setPerPage($perPage)
725
    {
726
        $this->perPage = $perPage;
727
    }
728
729
    /**
730
     * Determine if the entity uses get.
731
     *
732
     * @return bool
733
     */
734
    public function usesTimestamps() : bool
735
    {
736
        return $this->timestamps;
737
    }
738
739
    /**
740
     * Determine if the entity uses soft deletes.
741
     *
742
     * @return bool
743
     */
744
    public function usesSoftDeletes() : bool
745
    {
746
        return $this->softDeletes;
747
    }
748
749
    /**
750
     * Get the 'created_at' column name.
751
     *
752
     * @return string
753
     */
754
    public function getCreatedAtColumn() : string
755
    {
756
        return $this->createdAtColumn;
757
    }
758
759
    /**
760
     * Get the 'updated_at' column name.
761
     *
762
     * @return string
763
     */
764
    public function getUpdatedAtColumn() : string
765
    {
766
        return $this->updatedAtColumn;
767
    }
768
769
    /**
770
     * Get the deleted_at column.
771
     *
772
     * @return string
773
     */
774
    public function getQualifiedDeletedAtColumn() : string
775
    {
776
        return $this->deletedAtColumn;
777
    }
778
779
    /**
780
     * Get the default foreign key name for the model.
781
     *
782
     * @return string
783
     */
784
    public function getForeignKey() : string
785
    {
786
        return snake_case(class_basename($this->getClass())).'_id';
787
    }
788
789
    /**
790
     * Return the inheritance type used by the entity.
791
     *
792
     * @return string|null
793
     */
794
    public function getInheritanceType()
795
    {
796
        return $this->inheritanceType;
797
    }
798
799
    /**
800
     * Return the discriminator column name on the entity that's
801
     * used for table inheritance.
802
     *
803
     * @return string
804
     */
805
    public function getDiscriminatorColumn() : string
806
    {
807
        return $this->discriminatorColumn;
808
    }
809
810
    /**
811
     * Return the mapping of discriminator column values to
812
     * entity class names that are used for table inheritance.
813
     *
814
     * @return array
815
     */
816
    public function getDiscriminatorColumnMap() : array
817
    {
818
        return $this->discriminatorColumnMap;
819
    }
820
821
    /**
822
     * Return true if the entity should be instanciated using
823
     * the IoC Container.
824
     *
825
     * @return bool
826
     */
827
    public function useDependencyInjection() : bool
828
    {
829
        return $this->dependencyInjection;
830
    }
831
832
    /**
833
     * Add a single relation method name once.
834
     *
835
     * @param string $relation
836
     */
837
    protected function addSingleRelation($relation)
838
    {
839
        if (!in_array($relation, $this->singleRelations)) {
840
            $this->singleRelations[] = $relation;
841
        }
842
    }
843
844
    /**
845
     * Add a foreign relation method name once.
846
     *
847
     * @param string $relation
848
     */
849
    protected function addForeignRelation($relation)
850
    {
851
        if (!in_array($relation, $this->foreignRelations)) {
852
            $this->foreignRelations[] = $relation;
853
        }
854
    }
855
856
    /**
857
     * Add a polymorphic relation method name once.
858
     *
859
     * @param string $relation
860
     */
861
    protected function addPolymorphicRelation($relation)
862
    {
863
        if (!in_array($relation, $this->polymorphicRelations)) {
864
            $this->polymorphicRelations[] = $relation;
865
        }
866
    }
867
868
    /**
869
     * Add a non proxy relation method name once.
870
     *
871
     * @param string $relation
872
     */
873
    protected function addNonProxyRelation($relation)
874
    {
875
        if (!in_array($relation, $this->nonProxyRelationships)) {
876
            $this->nonProxyRelationships[] = $relation;
877
        }
878
    }
879
880
    /**
881
     * Add a local relation method name once.
882
     *
883
     * @param string $relation
884
     */
885
    protected function addLocalRelation($relation)
886
    {
887
        if (!in_array($relation, $this->localRelations)) {
888
            $this->localRelations[] = $relation;
889
        }
890
    }
891
892
    /**
893
     * Add a many relation method name once.
894
     *
895
     * @param string $relation
896
     */
897
    protected function addManyRelation($relation)
898
    {
899
        if (!in_array($relation, $this->manyRelations)) {
900
            $this->manyRelations[] = $relation;
901
        }
902
    }
903
904
    /**
905
     * Add a pivot relation method name once.
906
     *
907
     * @param string $relation
908
     */
909
    protected function addPivotRelation($relation)
910
    {
911
        if (!in_array($relation, $this->pivotRelations)) {
912
            $this->pivotRelations[] = $relation;
913
        }
914
    }
915
916
    /**
917
     * Add an embedded relation.
918
     *
919
     * @param string $relation
920
     */
921
    protected function addEmbeddedRelation($relation)
922
    {
923
        if (!in_array($relation, $this->embeddedRelations)) {
924
            $this->embeddedRelations[] = $relation;
925
        }
926
    }
927
928
    /**
929
     * Define an Embedded Object.
930
     *
931
     * @param mixed  $entity
0 ignored issues
show
Bug introduced by
There is no parameter named $entity. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
932
     * @param string $related
0 ignored issues
show
Documentation introduced by
There is no parameter named $related. Did you maybe mean $relatedClass?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
933
     *
934
     * @return EmbedsOne
935
     */
936 View Code Duplication
    public function embedsOne($parent, string $relatedClass, $relation = null) : EmbedsOne
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
937
    {
938
        if(is_null($relation)) {
939
            list(, $caller) = debug_backtrace(false);
940
            $relation = $caller['function'];
941
        }
942
943
        $this->addEmbeddedRelation($relation);
944
        $this->addNonProxyRelation($relation);
945
946
        return new EmbedsOne($parent, $relatedClass, $relation);
947
    }
948
949
    /**
950
     * Define an Embedded Collection.
951
     *
952
     * @param mixed  $entity
0 ignored issues
show
Bug introduced by
There is no parameter named $entity. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
953
     * @param string $related
0 ignored issues
show
Documentation introduced by
There is no parameter named $related. Did you maybe mean $relatedClass?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
954
     *
955
     * @return EmbedsOne
956
     */
957 View Code Duplication
    public function embedsMany($parent, string $relatedClass, $relation = null) : EmbedsMany
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
958
    {
959
        if(is_null($relation)) {
960
            list(, $caller) = debug_backtrace(false);
961
            $relation = $caller['function'];
962
        }
963
964
        $this->addEmbeddedRelation($relation);
965
        $this->addNonProxyRelation($relation);
966
967
        return new EmbedsMany($parent, $relatedClass, $relation);
968
    }
969
970
    /**
971
     * Define a one-to-one relationship.
972
     *
973
     * @param mixed  $entity
974
     * @param string $related    entity class
975
     * @param string $foreignKey
976
     * @param string $localKey
977
     *
978
     * @throws MappingException
979
     *
980
     * @return \Analogue\ORM\Relationships\HasOne
981
     */
982
    public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
983
    {
984
        $foreignKey = $foreignKey ?: $this->getForeignKey();
985
986
        $relatedMapper = Manager::getInstance()->mapper($related);
987
988
        $relatedMap = $relatedMapper->getEntityMap();
0 ignored issues
show
Unused Code introduced by
$relatedMap is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
989
990
        $localKey = $localKey ?: $this->getKeyName();
991
992
        // Add the relation to the definition in map
993
        list(, $caller) = debug_backtrace(false);
994
        $relation = $caller['function'];
995
        $this->relatedClasses[$relation] = $related;
996
997
        $this->addSingleRelation($relation);
998
        $this->addForeignRelation($relation);
999
        $this->addNonProxyRelation($relation);
1000
1001
        // This relationship will always be eager loaded, as proxying it would
1002
        // mean having an object that doesn't actually exists.
1003
        if (!in_array($relation, $this->with)) {
1004
            $this->with[] = $relation;
1005
        }
1006
1007
        return new HasOne($relatedMapper, $entity, /*$relatedMap->getTable().'.'*/$foreignKey, $localKey);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1008
    }
1009
1010
    /**
1011
     * Define a polymorphic one-to-one relationship.
1012
     *
1013
     * @param mixed       $entity
1014
     * @param string      $related
1015
     * @param string      $name
1016
     * @param string|null $type
1017
     * @param string|null $id
1018
     * @param string|null $localKey
1019
     *
1020
     * @throws MappingException
1021
     *
1022
     * @return \Analogue\ORM\Relationships\MorphOne
1023
     */
1024
    public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
1025
    {
1026
        list($type, $id) = $this->getMorphs($name, $type, $id);
1027
1028
        $localKey = $localKey ?: $this->getKeyName();
1029
1030
        $relatedMapper = Manager::getInstance()->mapper($related);
1031
1032
        //$table = $relatedMapper->getEntityMap()->getTable();
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1033
1034
        // Add the relation to the definition in map
1035
        list(, $caller) = debug_backtrace(false);
1036
        $relation = $caller['function'];
1037
        $this->relatedClasses[$relation] = $related;
1038
1039
        $this->addSingleRelation($relation);
1040
        $this->addForeignRelation($relation);
1041
        $this->addNonProxyRelation($relation);
1042
1043
        // This relationship will always be eager loaded, as proxying it would
1044
        // mean having an object that doesn't actually exists.
1045
        if (!in_array($relation, $this->with)) {
1046
            $this->with[] = $relation;
1047
        }
1048
1049
        return new MorphOne($relatedMapper, $entity, /*$table.'.'.*/$type, /*$table.'.'.*/$id, $localKey);
1050
    }
1051
1052
    /**
1053
     * Define an inverse one-to-one or many relationship.
1054
     *
1055
     * @param mixed       $entity
1056
     * @param string      $related
1057
     * @param string|null $foreignKey
1058
     * @param string|null $otherKey
1059
     * @param string|null $relation
0 ignored issues
show
Bug introduced by
There is no parameter named $relation. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1060
     *
1061
     * @throws MappingException
1062
     *
1063
     * @return \Analogue\ORM\Relationships\BelongsTo
1064
     */
1065
    public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
1066
    {
1067
        // Add the relation to the definition in map
1068
        list(, $caller) = debug_backtrace(false);
1069
        $relation = $caller['function'];
1070
        $this->relatedClasses[$relation] = $related;
1071
1072
        $this->addSingleRelation($relation);
1073
        $this->addLocalRelation($relation);
1074
1075
        // If no foreign key was supplied, we can use a backtrace to guess the proper
1076
        // foreign key name by using the name of the relationship function, which
1077
        // when combined with an "_id" should conventionally match the columns.
1078
        if (is_null($foreignKey)) {
1079
            $foreignKey = snake_case($relation).'_id';
1080
        }
1081
1082
        $this->localForeignKeys[$relation] = $foreignKey;
1083
1084
        $relatedMapper = Manager::getInstance()->mapper($related);
1085
1086
        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
1087
1088
        return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
1089
    }
1090
1091
    /**
1092
     * Define a polymorphic, inverse one-to-one or many relationship.
1093
     *
1094
     * @param mixed       $entity
1095
     * @param string|null $name
1096
     * @param string|null $type
1097
     * @param string|null $id
1098
     *
1099
     * @throws MappingException
1100
     *
1101
     * @return \Analogue\ORM\Relationships\MorphTo
1102
     */
1103
    public function morphTo($entity, $name = null, $type = null, $id = null)
1104
    {
1105
        // If no name is provided, we will use the backtrace to get the function name
1106
        // since that is most likely the name of the polymorphic interface. We can
1107
        // use that to get both the class and foreign key that will be utilized.
1108
        if (is_null($name)) {
1109
            list(, $caller) = debug_backtrace(false);
1110
1111
            $name = snake_case($caller['function']);
1112
        }
1113
        $this->addSingleRelation($name);
1114
        $this->addLocalRelation($name);
1115
        $this->addPolymorphicRelation($name);
1116
1117
        $this->relatedClass[$name] = null;
0 ignored issues
show
Bug introduced by
The property relatedClass does not seem to exist. Did you mean relatedClasses?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1118
1119
        list($type, $id) = $this->getMorphs($name, $type, $id);
1120
1121
        // Store the foreign key in the entity map.
1122
        // We might want to store the (key, type) as we might need it
1123
        // to build a MorphTo proxy
1124
        $this->localForeignKeys[$name] = [
1125
            'id'   => $id,
1126
            'type' => $type,
1127
        ];
1128
1129
        $mapper = Manager::getInstance()->mapper(get_class($entity));
1130
1131
        // If the type value is null it is probably safe to assume we're eager loading
1132
        // the relationship. When that is the case we will pass in a dummy query as
1133
        // there are multiple types in the morph and we can't use single queries.
1134
        $factory = new Factory();
1135
        $wrapper = $factory->make($entity);
1136
1137
        if (is_null($class = $wrapper->getEntityAttribute($type))) {
1138
            return new MorphTo(
1139
                $mapper, $entity, $id, null, $type, $name
1140
            );
1141
        }
1142
1143
        // If we are not eager loading the relationship we will essentially treat this
1144
        // as a belongs-to style relationship since morph-to extends that class and
1145
        // we will pass in the appropriate values so that it behaves as expected.
1146
        else {
1147
            $class = Manager::getInstance()->getInverseMorphMap($class);
1148
            $relatedMapper = Manager::getInstance()->mapper($class);
1149
1150
            $foreignKey = $relatedMapper->getEntityMap()->getKeyName();
1151
1152
            return new MorphTo(
1153
                $relatedMapper, $entity, $id, $foreignKey, $type, $name
1154
            );
1155
        }
1156
    }
1157
1158
    /**
1159
     * Define a one-to-many relationship.
1160
     *
1161
     * @param mixed       $entity
1162
     * @param string      $related
1163
     * @param string|null $foreignKey
1164
     * @param string|null $localKey
1165
     *
1166
     * @throws MappingException
1167
     *
1168
     * @return \Analogue\ORM\Relationships\HasMany
1169
     */
1170
    public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
1171
    {
1172
        $foreignKey = $foreignKey ?: $this->getForeignKey();
1173
1174
        $relatedMapper = Manager::getInstance()->mapper($related);
1175
1176
        $localKey = $localKey ?: $this->getKeyName();
1177
1178
        // Add the relation to the definition in map
1179
        list(, $caller) = debug_backtrace(false);
1180
        $relation = $caller['function'];
1181
        $this->relatedClasses[$relation] = $related;
1182
1183
        $this->addManyRelation($relation);
1184
        $this->addForeignRelation($relation);
1185
1186
        return new HasMany($relatedMapper, $entity, $foreignKey, $localKey);
1187
    }
1188
1189
    /**
1190
     * Define a has-many-through relationship.
1191
     *
1192
     * @param mixed       $entity
1193
     * @param string      $related
1194
     * @param string      $through
1195
     * @param string|null $firstKey
1196
     * @param string|null $secondKey
1197
     *
1198
     * @throws MappingException
1199
     *
1200
     * @return \Analogue\ORM\Relationships\HasManyThrough
1201
     */
1202 View Code Duplication
    public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1203
    {
1204
        $relatedMapper = Manager::getInstance()->mapper($related);
1205
        $throughMapper = Manager::getInstance()->mapper($through);
1206
1207
        $firstKey = $firstKey ?: $this->getForeignKey();
1208
1209
        $throughMap = $throughMapper->getEntityMap();
1210
1211
        $secondKey = $secondKey ?: $throughMap->getForeignKey();
1212
1213
        // Add the relation to the definition in map
1214
        list(, $caller) = debug_backtrace(false);
1215
        $relation = $caller['function'];
1216
        $this->relatedClasses[$relation] = $related;
1217
1218
        $this->addManyRelation($relation);
1219
        $this->addForeignRelation($relation);
1220
1221
        return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
1222
    }
1223
1224
    /**
1225
     * Define a polymorphic one-to-many relationship.
1226
     *
1227
     * @param mixed       $entity
1228
     * @param string      $related
1229
     * @param string      $name
1230
     * @param string|null $type
1231
     * @param string|null $id
1232
     * @param string|null $localKey
1233
     *
1234
     * @return \Analogue\ORM\Relationships\MorphMany
1235
     */
1236 View Code Duplication
    public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1237
    {
1238
        // Here we will gather up the morph type and ID for the relationship so that we
1239
        // can properly query the intermediate table of a relation. Finally, we will
1240
        // get the table and create the relationship instances for the developers.
1241
        list($type, $id) = $this->getMorphs($name, $type, $id);
1242
1243
        $relatedMapper = Manager::getInstance()->mapper($related);
1244
1245
        $table = $relatedMapper->getEntityMap()->getTable();
0 ignored issues
show
Unused Code introduced by
$table is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1246
1247
        $localKey = $localKey ?: $this->getKeyName();
1248
1249
        // Add the relation to the definition in map
1250
        list(, $caller) = debug_backtrace(false);
1251
        $relation = $caller['function'];
1252
        $this->relatedClasses[$relation] = $related;
1253
1254
        $this->addManyRelation($relation);
1255
        $this->addForeignRelation($relation);
1256
1257
        return new MorphMany($relatedMapper, $entity, /*$table.'.'.*/$type, /*$table.'.'.*/$id, $localKey);
1258
    }
1259
1260
    /**
1261
     * Define a many-to-many relationship.
1262
     *
1263
     * @param mixed       $entity
1264
     * @param string      $relatedClass
0 ignored issues
show
Documentation introduced by
There is no parameter named $relatedClass. Did you maybe mean $related?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1265
     * @param string|null $table
1266
     * @param string|null $foreignKey
1267
     * @param string|null $otherKey
1268
     * @param string|null $relation
0 ignored issues
show
Bug introduced by
There is no parameter named $relation. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1269
     *
1270
     * @throws MappingException
1271
     *
1272
     * @return \Analogue\ORM\Relationships\BelongsToMany
1273
     */
1274
    public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1275
    {
1276
        // Add the relation to the definition in map
1277
        list(, $caller) = debug_backtrace(false);
1278
        $relation = $caller['function'];
1279
        $this->relatedClasses[$relation] = $related;
1280
1281
        $this->addManyRelation($relation);
1282
        $this->addForeignRelation($relation);
1283
        $this->addPivotRelation($relation);
1284
1285
        // First, we'll need to determine the foreign key and "other key" for the
1286
        // relationship. Once we have determined the keys we'll make the query
1287
        // instances as well as the relationship instances we need for this.
1288
        $foreignKey = $foreignKey ?: $this->getForeignKey();
1289
1290
        $relatedMapper = Manager::getInstance()->mapper($related);
1291
1292
        $relatedMap = $relatedMapper->getEntityMap();
1293
1294
        $otherKey = $otherKey ?: $relatedMap->getForeignKey();
1295
1296
        // If no table name was provided, we can guess it by concatenating the two
1297
        // models using underscores in alphabetical order. The two model names
1298
        // are transformed to snake case from their default CamelCase also.
1299
        if (is_null($table)) {
1300
            $table = $this->joiningTable($relatedMap);
1301
        }
1302
1303
        return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1304
    }
1305
1306
    /**
1307
     * Define a polymorphic many-to-many relationship.
1308
     *
1309
     * @param mixed       $entity
1310
     * @param string      $related
1311
     * @param string      $name
1312
     * @param string|null $table
1313
     * @param string|null $foreignKey
1314
     * @param string|null $otherKey
1315
     * @param bool        $inverse
1316
     *
1317
     * @throws MappingException
1318
     *
1319
     * @return \Analogue\ORM\Relationships\MorphToMany
1320
     */
1321
    public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1322
    {
1323
        // Add the relation to the definition in map
1324
        list(, $caller) = debug_backtrace(false);
1325
        $relation = $caller['function'];
1326
        $this->relatedClasses[$relation] = $related;
1327
1328
        $this->addManyRelation($relation);
1329
        $this->addForeignRelation($relation);
1330
        $this->addPivotRelation($relation);
1331
1332
        // First, we will need to determine the foreign key and "other key" for the
1333
        // relationship. Once we have determined the keys we will make the query
1334
        // instances, as well as the relationship instances we need for these.
1335
        $foreignKey = $foreignKey ?: $name.'_id';
1336
1337
        $relatedMapper = Manager::getInstance()->mapper($related);
1338
1339
        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1340
1341
        $table = $table ?: str_plural($name);
1342
1343
        return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1344
    }
1345
1346
    /**
1347
     * Define a polymorphic, inverse many-to-many relationship.
1348
     *
1349
     * @param mixed       $entity
1350
     * @param string      $related
1351
     * @param string      $name
1352
     * @param string|null $table
1353
     * @param string|null $foreignKey
1354
     * @param string|null $otherKey
1355
     *
1356
     * @throws MappingException
1357
     *
1358
     * @return \Analogue\ORM\Relationships\MorphToMany
1359
     */
1360
    public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1361
    {
1362
        // Add the relation to the definition in map
1363
        list(, $caller) = debug_backtrace(false);
1364
        $relation = $caller['function'];
1365
        $this->relatedClasses[$relation] = $related;
1366
1367
        $this->addManyRelation($relation);
1368
        $this->addForeignRelation($relation);
1369
1370
        $foreignKey = $foreignKey ?: $this->getForeignKey();
1371
1372
        // For the inverse of the polymorphic many-to-many relations, we will change
1373
        // the way we determine the foreign and other keys, as it is the opposite
1374
        // of the morph-to-many method since we're figuring out these inverses.
1375
        $otherKey = $otherKey ?: $name.'_id';
1376
1377
        return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1378
    }
1379
1380
    /**
1381
     * Get the joining table name for a many-to-many relation.
1382
     *
1383
     * @param EntityMap $relatedMap
1384
     *
1385
     * @return string
1386
     */
1387
    public function joiningTable($relatedMap)
1388
    {
1389
        // The joining table name, by convention, is simply the snake cased models
1390
        // sorted alphabetically and concatenated with an underscore, so we can
1391
        // just sort the models and join them together to get the table name.
1392
        $base = $this->getTable();
1393
1394
        $related = $relatedMap->getTable();
1395
1396
        $tables = [$related, $base];
1397
1398
        // Now that we have the model names in an array we can just sort them and
1399
        // use the implode function to join them together with an underscores,
1400
        // which is typically used by convention within the database system.
1401
        sort($tables);
1402
1403
        return strtolower(implode('_', $tables));
1404
    }
1405
1406
    /**
1407
     * Get the polymorphic relationship columns.
1408
     *
1409
     * @param string $name
1410
     * @param string $type
1411
     * @param string $id
1412
     *
1413
     * @return string[]
1414
     */
1415
    protected function getMorphs($name, $type, $id)
1416
    {
1417
        $type = $type ?: $name.'_type';
1418
1419
        $id = $id ?: $name.'_id';
1420
1421
        return [$type, $id];
1422
    }
1423
1424
    /**
1425
     * Get the class name for polymorphic relations.
1426
     *
1427
     * @return string
1428
     */
1429
    public function getMorphClass()
1430
    {
1431
        $morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1432
1433
        return $this->morphClass ?: $morphClass;
1434
    }
1435
1436
    /**
1437
     * Create a new Entity Collection instance.
1438
     *
1439
     * @param array $entities
1440
     *
1441
     * @return \Analogue\ORM\EntityCollection
1442
     */
1443
    public function newCollection(array $entities = [])
1444
    {
1445
        $collection = new EntityCollection($entities, $this);
1446
1447
        return $collection->keyBy($this->getKeyName());
1448
    }
1449
1450
    /**
1451
     * Process EntityMap parsing at initialization time.
1452
     *
1453
     * @return void
1454
     */
1455
    public function initialize()
1456
    {
1457
        $userMethods = $this->getCustomMethods();
1458
1459
        // Parse EntityMap for method based relationship
1460
        if (count($userMethods) > 0) {
1461
            $this->relationships = $this->parseMethodsForRelationship($userMethods);
1462
        }
1463
1464
        // Parse EntityMap for dynamic relationships
1465
        if (count($this->dynamicRelationships) > 0) {
1466
            $this->relationships = $this->relationships + $this->getDynamicRelationships();
1467
        }
1468
    }
1469
1470
    /**
1471
     * Parse every relationships on the EntityMap and sort
1472
     * them by type.
1473
     *
1474
     * @return void
1475
     */
1476
    public function boot()
1477
    {
1478
        if (count($this->relationships > 0)) {
1479
            $this->sortRelationshipsByType();
1480
        }
1481
    }
1482
1483
    /**
1484
     * Get Methods that has been added in the child class.
1485
     *
1486
     * @return array
1487
     */
1488
    protected function getCustomMethods()
1489
    {
1490
        $mapMethods = get_class_methods($this);
1491
1492
        $parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1493
1494
        return array_diff($mapMethods, $parentsMethods);
1495
    }
1496
1497
    /**
1498
     * Parse user's class methods for relationships.
1499
     *
1500
     * @param array $customMethods
1501
     *
1502
     * @return array
1503
     */
1504
    protected function parseMethodsForRelationship(array $customMethods)
1505
    {
1506
        $relationships = [];
1507
1508
        $class = new ReflectionClass(get_class($this));
1509
1510
        // Get the mapped Entity class, as we will detect relationships
1511
        // methods by testing that the first argument is type-hinted to
1512
        // the same class as the mapped Entity.
1513
        $entityClass = $this->getClass();
1514
1515
        foreach ($customMethods as $methodName) {
1516
            $method = $class->getMethod($methodName);
1517
1518
            if ($method->getNumberOfParameters() > 0) {
1519
                $params = $method->getParameters();
1520
1521
                if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $params[0]->getClass()->name can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
1522
                    $relationships[] = $methodName;
1523
                }
1524
            }
1525
        }
1526
1527
        return $relationships;
1528
    }
1529
1530
    /**
1531
     * Sort Relationships methods by type.
1532
     *
1533
     * TODO : replace this by direclty setting these value
1534
     * in the corresponding methods, so we won't need
1535
     * the correpondancy tabble
1536
     *
1537
     * @return void
1538
     */
1539
    protected function sortRelationshipsByType()
1540
    {
1541
        $entityClass = $this->getClass();
1542
1543
        // Instantiate a dummy entity which we will pass to relationship methods.
1544
        $entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1545
1546
        foreach ($this->relationships as $relation) {
1547
            $this->$relation($entity);
1548
        }
1549
    }
1550
1551
    /**
1552
     * Override this method for custom entity instantiation.
1553
     *
1554
     * @return null
1555
     */
1556
    public function activator()
1557
    {
1558
    }
1559
1560
    /**
1561
     * Magic call to dynamic relationships.
1562
     *
1563
     * @param string $method
1564
     * @param array  $parameters
1565
     *
1566
     * @throws Exception
1567
     *
1568
     * @return mixed
1569
     */
1570
    public function __call($method, $parameters)
1571
    {
1572
        if (!array_key_exists($method, $this->dynamicRelationships)) {
1573
            throw new Exception(get_class($this)." has no method $method");
1574
        }
1575
1576
        // Add $this to parameters so the closure can call relationship method on the map.
1577
        $parameters[] = $this;
1578
1579
        return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1580
    }
1581
}
1582