Completed
Branch master (a17b64)
by Rémi
15:50
created
src/EntityMap.php 3 patches
Doc Comments   +1 added lines, -3 removed lines patch added patch discarded remove patch
@@ -808,7 +808,6 @@  discard block
 block discarded – undo
808 808
      * @param  string      $related
809 809
      * @param  string|null $foreignKey
810 810
      * @param  string|null $otherKey
811
-     * @param  string|null $relation
812 811
      * @throws MappingException
813 812
      * @return \Analogue\ORM\Relationships\BelongsTo
814 813
      */
@@ -1004,11 +1003,10 @@  discard block
 block discarded – undo
1004 1003
      * Define a many-to-many relationship.
1005 1004
      *
1006 1005
      * @param  mixed       $entity
1007
-     * @param  string      $relatedClass
1006
+     * @param  string      $related
1008 1007
      * @param  string|null $table
1009 1008
      * @param  string|null $foreignKey
1010 1009
      * @param  string|null $otherKey
1011
-     * @param  string|null $relation
1012 1010
      * @throws MappingException
1013 1011
      * @return \Analogue\ORM\Relationships\BelongsToMany
1014 1012
      */
Please login to merge, or discard this patch.
Indentation   +1279 added lines, -1279 removed lines patch added patch discarded remove patch
@@ -23,1286 +23,1286 @@
 block discarded – undo
23 23
  */
24 24
 class EntityMap
25 25
 {
26
-    /**
27
-     * The mapping driver to use with this entity
28
-     *
29
-     * @var  string
30
-     */
31
-    protected $driver = 'illuminate';
32
-
33
-    /**
34
-     * The Database Connection name for the model.
35
-     *
36
-     * @var string
37
-     */
38
-    protected $connection;
39
-
40
-    /**
41
-     * The table associated with the entity.
42
-     *
43
-     * @var string|null
44
-     */
45
-    protected $table = null;
46
-
47
-    /**
48
-     * The primary key for the model.
49
-     *
50
-     * @var string
51
-     */
52
-    protected $primaryKey = 'id';
53
-
54
-    /**
55
-     * Name of the entity's array property that should
56
-     * contain the attributes
57
-     * 
58
-     * @var string
59
-     */
60
-    protected $arrayName = 'attributes';
61
-
62
-    /**
63
-     * Array containing the list of database columns to be mapped
64
-     * in the attributes array of the entity. 
65
-     *
66
-     * @var array
67
-     */
68
-    protected $attributes = [];
69
-
70
-    /**
71
-     * Array containing the list of database columns to be mapped
72
-     * to the entity's class properties. 
73
-     *
74
-     * @var array
75
-     */
76
-    protected $properties = [];
77
-
78
-    /**
79
-     * The Custom Domain Class to use with this mapping
80
-     *
81
-     * @var string|null
82
-     */
83
-    protected $class = null;
84
-
85
-    /**
86
-     * Embedded Value Objects
87
-     * 
88
-     * @var array
89
-     */
90
-    protected $embeddables = [];
91
-
92
-    /**
93
-     * Determine the relationships method used on the entity.
94
-     * If not set, mapper will autodetect them
95
-     *
96
-     * @var array
97
-     */
98
-    private $relationships = [];
99
-
100
-    /**
101
-     * Relationships that should be treated as collection.
102
-     *
103
-     * @var array
104
-     */
105
-    private $manyRelations = [];
106
-
107
-    /**
108
-     * Relationships that should be treated as single entity.
109
-     *
110
-     * @var array
111
-     */
112
-    private $singleRelations = [];
113
-
114
-    /**
115
-     * Relationships for which the key is stored in the Entity itself
116
-     *
117
-     * @var array
118
-     */
119
-    private $localRelations = [];
120
-
121
-    /** 
122
-     * List of local keys associated to local relation methods
123
-     * 
124
-     * @var array
125
-     */
126
-    private $localForeignKeys = [];
127
-
128
-    /**
129
-     * Relationships for which the key is stored in the Related Entity
130
-     *
131
-     * @var array
132
-     */
133
-    private $foreignRelations = [];
134
-
135
-    /**
136
-     * Relationships which use a pivot record.
137
-     *
138
-     * @var array
139
-     */
140
-    private $pivotRelations = [];
141
-
142
-    /**
143
-     * Dynamic relationships
144
-     *
145
-     * @var array
146
-     */
147
-    private $dynamicRelationships = [];
148
-
149
-    /**
150
-     * Targetted class for the relationship method. value is set to `null` for
151
-     * polymorphic relations. 
152
-     * 
153
-     * @var array
154
-     */
155
-    private $relatedClasses = [];
156
-
157
-    /** 
158
-     * Some relation methods like embedded objects, or HasOne and MorphOne,
159
-     * will never have a proxy loaded on them. 
160
-     * 
161
-     * @var  array
162
-     */
163
-    private $nonProxyRelationships = [];
164
-
165
-    /**
166
-     * The number of models to return for pagination.
167
-     *
168
-     * @var int
169
-     */
170
-    protected $perPage = 15;
171
-
172
-    /**
173
-     * The relations to eager load on every query.
174
-     *
175
-     * @var array
176
-     */
177
-    protected $with = [];
178
-
179
-    /**
180
-     * The class name to be used in polymorphic relations.
181
-     *
182
-     * @var string
183
-     */
184
-    protected $morphClass;
185
-
186
-    /**
187
-     * Sequence name, to be used with postgreSql
188
-     * defaults to %table_name%_id_seq
189
-     *
190
-     * @var string|null
191
-     */
192
-    protected $sequence = null;
193
-
194
-    /**
195
-     * Indicates if the entity should be timestamped.
196
-     *
197
-     * @var bool
198
-     */
199
-    public $timestamps = false;
200
-
201
-    /**
202
-     * The name of the "created at" column.
203
-     *
204
-     * @var string
205
-     */
206
-    protected $createdAtColumn = 'created_at';
207
-
208
-    /**
209
-     * The name of the "updated at" column.
210
-     *
211
-     * @var string
212
-     */
213
-    protected $updatedAtColumn = 'updated_at';
214
-
215
-    /**
216
-     * Indicates if the entity uses softdeletes
217
-     *
218
-     * @var boolean
219
-     */
220
-    public $softDeletes = false;
221
-
222
-    /**
223
-     * The name of the "deleted at" column.
224
-     *
225
-     * @var string
226
-     */
227
-    protected $deletedAtColumn = 'deleted_at';
228
-
229
-    /**
230
-     * The date format to use with the current database connection
231
-     *
232
-     * @var string
233
-     */
234
-    protected $dateFormat;
235
-
236
-    /**
237
-     * Set this property to true if the entity should be instantiated
238
-     * using the IoC Container
239
-     * 
240
-     * @var boolean
241
-     */
242
-    protected $dependencyInjection = false;
243
-
244
-    /**
245
-     * Set the usage of inheritance, possible values are :
246
-     * "single_table"
247
-     * null
248
-     * 
249
-     * @var string | null
250
-     */
251
-    protected $inheritanceType = null;
252
-
253
-    /**
254
-     * Discriminator column name
255
-     * 
256
-     * @var string
257
-     */
258
-    protected $discriminatorColumn = "type";
259
-
260
-    /**
261
-     * Allow using a string to define which entity type should be instantiated.
262
-     * If not set, analogue will uses entity's FQDN
263
-     * 
264
-     * @var array
265
-     */
266
-    protected $discriminatorColumnMap = [];
267
-
268
-    /**
269
-     * Return Domain class attributes, useful when mapping to a Plain PHP Object
270
-     *
271
-     * @return array
272
-     */
273
-    public function getAttributes()
274
-    {
275
-        return $this->attributes;
276
-    }
277
-
278
-    /**
279
-     * Set the domain class attributes
280
-     *
281
-     * @param array $attributeNames
282
-     */
283
-    public function setAttributes(array $attributeNames)
284
-    {
285
-        $this->attributes = $attributeNames;
286
-    }
287
-
288
-    /**
289
-     * Get all the attribute names for the class, including relationships, embeddables and primary key.
290
-     *
291
-     * @return array
292
-     */
293
-    public function getCompiledAttributes()
294
-    {
295
-        $key = $this->getKeyName();
296
-
297
-        $embeddables = array_keys($this->getEmbeddables());
298
-
299
-        $relationships = $this->getRelationships();
300
-
301
-        $attributes = $this->getAttributes();
302
-
303
-        return array_merge([$key], $embeddables, $relationships, $attributes);
304
-    }
305
-
306
-    /**
307
-     * Set the date format to use with the current database connection
308
-     *
309
-     * @param string $format
310
-     */
311
-    public function setDateFormat($format)
312
-    {
313
-        $this->dateFormat = $format;
314
-    }
315
-
316
-    /**
317
-     * Get the date format to use with the current database connection
318
-     *
319
-     *  @return string
320
-     */
321
-    public function getDateFormat()
322
-    {
323
-        return $this->dateFormat;
324
-    }
325
-
326
-    /**
327
-     * Set the Driver for this mapping
328
-     *
329
-     * @param string $driver
330
-     */
331
-    public function setDriver($driver)
332
-    {
333
-        $this->driver = $driver;
334
-    }
335
-
336
-    /**
337
-     * Get the Driver for this mapping.
338
-     *
339
-     * @return string
340
-     */
341
-    public function getDriver()
342
-    {
343
-        return $this->driver;
344
-    }
345
-
346
-    /**
347
-     * Set the db connection to use on the table
348
-     *
349
-     * @param $connection
350
-     */
351
-    public function setConnection($connection)
352
-    {
353
-        $this->connection = $connection;
354
-    }
355
-
356
-    /**
357
-     * Get the Database connection the Entity is stored on.
358
-     *
359
-     * @return string
360
-     */
361
-    public function getConnection()
362
-    {
363
-        return $this->connection;
364
-    }
365
-
366
-    /**
367
-     * Get the table associated with the entity.
368
-     *
369
-     * @return string
370
-     */
371
-    public function getTable()
372
-    {
373
-        if (!is_null($this->table)) {
374
-            return $this->table;
375
-        }
376
-
377
-        return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
378
-    }
379
-
380
-    /**
381
-     * Set the database table name
382
-     *
383
-     * @param  string $table
384
-     */
385
-    public function setTable($table)
386
-    {
387
-        $this->table = $table;
388
-    }
389
-
390
-    /**
391
-     * Get the pgSql sequence name
392
-     *
393
-     * @return string
394
-     */
395
-    public function getSequence()
396
-    {
397
-        if (!is_null($this->sequence)) {
398
-            return $this->sequence;
399
-        } else {
400
-            return $this->getTable() . '_id_seq';
401
-        }
402
-    }
403
-
404
-    /**
405
-     * Get the custom entity class
406
-     *
407
-     * @return string namespaced class name
408
-     */
409
-    public function getClass()
410
-    {
411
-        return isset($this->class) ? $this->class : null;
412
-    }
413
-
414
-    /**
415
-     * Set the custom entity class
416
-     *
417
-     * @param string $class namespaced class name
418
-     */
419
-    public function setClass($class)
420
-    {
421
-        $this->class = $class;
422
-    }
423
-
424
-    /**
425
-     * Get the embedded Value Objects
426
-     *
427
-     * @return array
428
-     */
429
-    public function getEmbeddables()
430
-    {
431
-        return $this->embeddables;
432
-    }
433
-
434
-    /**
435
-     * Set the embedded Value Objects
436
-     *
437
-     * @param array $embeddables
438
-     */
439
-    public function setEmbeddables(array $embeddables)
440
-    {
441
-        $this->embeddables = $embeddables;
442
-    }
443
-
444
-    /**
445
-     * Get the relationships to map on a custom domain
446
-     * class.
447
-     *
448
-     * @return array
449
-     */
450
-    public function getRelationships()
451
-    {
452
-        return $this->relationships;
453
-    }
454
-
455
-    /**  
456
-     * Get the relationships that will not have a proxy
457
-     * set on them
458
-     * 
459
-     * @return array
460
-     */
461
-    public function getRelationshipsWithoutProxy()
462
-    {
463
-        return $this->nonProxyRelationships;
464
-    }
465
-
466
-    /**
467
-     * Relationships of the Entity type
468
-     *
469
-     * @return array
470
-     */
471
-    public function getSingleRelationships()
472
-    {
473
-        return $this->singleRelations;
474
-    }
475
-
476
-    /**
477
-     * Relationships of type Collection
478
-     *
479
-     * @return array
480
-     */
481
-    public function getManyRelationships()
482
-    {
483
-        return $this->manyRelations;
484
-    }
485
-
486
-    /**
487
-     * Relationships with foreign key in the mapped entity record.
488
-     *
489
-     * @return array
490
-     */
491
-    public function getLocalRelationships()
492
-    {
493
-        return $this->localRelations;
494
-    }
495
-
496
-    /**  
497
-     * Return the local keys associated to the relationship
498
-     * 
499
-     * @param  string $relation
500
-     * @return string | array | null
501
-     */
502
-    public function getLocalKeys($relation)
503
-    {
504
-        return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
505
-    }
506
-
507
-    /**
508
-     * Relationships with foreign key in the related Entity record
509
-     *
510
-     * @return array
511
-     */
512
-    public function getForeignRelationships()
513
-    {
514
-        return $this->foreignRelations;
515
-    }
516
-
517
-    /**
518
-     * Relationships which keys are stored in a pivot record
519
-     *
520
-     * @return array
521
-     */
522
-    public function getPivotRelationships()
523
-    {
524
-        return $this->pivotRelations;
525
-    }
526
-
527
-    /**  
528
-     * Get the targetted type for a relationship. Return null if polymorphic
529
-     * 
530
-     * @param  string  $relation
531
-     * @return string | null
532
-     */
533
-    public function getTargettedClass($relation)
534
-    {
535
-        return $this->relatedClasses[$relation];
536
-    }
537
-
538
-    /**
539
-     * Add a Dynamic Relationship method at runtime. This has to be done
540
-     * by hooking the 'initializing' event, before entityMap is initialized.
541
-     *
542
-     * @param string  $name         Relation name
543
-     * @param \Closure $relationship
544
-     *
545
-     * @return void
546
-     */
547
-    public function addRelationshipMethod($name, \Closure $relationship)
548
-    {
549
-        $this->dynamicRelationships[$name] = $relationship;
550
-    }
551
-
552
-    /**
553
-     * Get the dynamic relationship method names.
554
-     *
555
-     * @return array
556
-     */
557
-    public function getDynamicRelationships()
558
-    {
559
-        return array_keys($this->dynamicRelationships);
560
-    }
561
-
562
-    /**
563
-     * Get the relationships that have to be eager loaded
564
-     * on each request.
565
-     *
566
-     * @return array
567
-     */
568
-    public function getEagerloadedRelationships()
569
-    {
570
-        return $this->with;
571
-    }
572
-
573
-    /**
574
-     * Get the primary key for the entity.
575
-     *
576
-     * @return string
577
-     */
578
-    public function getKeyName()
579
-    {
580
-        return $this->primaryKey;
581
-    }
582
-
583
-    /**
584
-     * Set the primary key for the entity.
585
-     *
586
-     * @param $key
587
-     * @return void
588
-     */
589
-    public function setKeyName($key)
590
-    {
591
-        $this->primaryKey = $key;
592
-    }
593
-
594
-    /**
595
-     * Get the table qualified key name.
596
-     *
597
-     * @return string
598
-     */
599
-    public function getQualifiedKeyName()
600
-    {
601
-        return $this->getTable() . '.' . $this->getKeyName();
602
-    }
603
-
604
-    /**
605
-     * Get the number of models to return per page.
606
-     *
607
-     * @return int
608
-     */
609
-    public function getPerPage()
610
-    {
611
-        return $this->perPage;
612
-    }
613
-
614
-    /**
615
-     * Set the number of models to return per page.
616
-     *
617
-     * @param  int $perPage
618
-     * @return void
619
-     */
620
-    public function setPerPage($perPage)
621
-    {
622
-        $this->perPage = $perPage;
623
-    }
624
-
625
-    /**
626
-     * Determine if the entity uses get.
627
-     *
628
-     * @return bool
629
-     */
630
-    public function usesTimestamps()
631
-    {
632
-        return $this->timestamps;
633
-    }
634
-
635
-    /**
636
-     * Determine if the entity uses soft deletes
637
-     *
638
-     * @return bool
639
-     */
640
-    public function usesSoftDeletes()
641
-    {
642
-        return $this->softDeletes;
643
-    }
644
-
645
-    /**
646
-     * Get the 'created_at' column name
647
-     *
648
-     * @return string
649
-     */
650
-    public function getCreatedAtColumn()
651
-    {
652
-        return $this->createdAtColumn;
653
-    }
654
-
655
-    /**
656
-     * Get the 'updated_at' column name
657
-     *
658
-     * @return string
659
-     */
660
-    public function getUpdatedAtColumn()
661
-    {
662
-        return $this->updatedAtColumn;
663
-    }
664
-
665
-    /**
666
-     * Get the deleted_at column
667
-     *
668
-     * @return string
669
-     */
670
-    public function getQualifiedDeletedAtColumn()
671
-    {
672
-        return $this->deletedAtColumn;
673
-    }
674
-
675
-    /**
676
-     * Get the default foreign key name for the model.
677
-     *
678
-     * @return string
679
-     */
680
-    public function getForeignKey()
681
-    {
682
-        return snake_case(class_basename($this->getClass())) . '_id';
683
-    }
684
-
685
-    /**
686
-     * Return the inheritance type used by the entity.
687
-     *
688
-     * @return string|null
689
-     */
690
-    public function getInheritanceType()
691
-    {
692
-        return $this->inheritanceType;
693
-    }
694
-
695
-    /**
696
-     * Return the discriminator column name on the entity that's
697
-     * used for table inheritance.
698
-     *
699
-     * @return string
700
-     */
701
-    public function getDiscriminatorColumn()
702
-    {
703
-        return $this->discriminatorColumn;
704
-    }
705
-
706
-    /**
707
-     * Return the mapping of discriminator column values to
708
-     * entity class names that are used for table inheritance.
709
-     *
710
-     * @return array
711
-     */
712
-    public function getDiscriminatorColumnMap()
713
-    {
714
-        return $this->discriminatorColumnMap;
715
-    }
716
-
717
-    /**
718
-     * Return true if the entity should be instanciated using
719
-     * the IoC Container
720
-     * 
721
-     * @return boolean
722
-     */
723
-    public function useDependencyInjection()
724
-    {
725
-        return $this->dependencyInjection;
726
-    }
727
-
728
-    /**
729
-     * Define a one-to-one relationship.
730
-     *
731
-     * @param  mixed  $entity
732
-     * @param  string $related entity class
733
-     * @param  string $foreignKey
734
-     * @param  string $localKey
735
-     * @throws MappingException
736
-     * @return \Analogue\ORM\Relationships\HasOne
737
-     */
738
-    public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
739
-    {
740
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
741
-
742
-        $relatedMapper = Manager::getInstance()->mapper($related);
743
-
744
-        $relatedMap = $relatedMapper->getEntityMap();
745
-
746
-        $localKey = $localKey ?: $this->getKeyName();
747
-
748
-        // Add the relation to the definition in map
749
-        list(, $caller) = debug_backtrace(false);
750
-        $relation = $caller['function'];
751
-        $this->relatedClasses[$relation] = $related;
752
-        $this->singleRelations[] = $relation;
753
-        $this->foreignRelations[] = $relation;
754
-        $this->nonProxyRelationships[] = $relation;
755
-
756
-        // This relationship will always be eager loaded, as proxying it would
757
-        // mean having an object that doesn't actually exists.
758
-        if(! in_array($relation, $this->with)) {
759
-            $this->with[] = $relation;
760
-        }
761
-
762
-        return new HasOne($relatedMapper, $entity, $relatedMap->getTable() . '.' . $foreignKey, $localKey);
763
-    }
764
-
765
-    /**
766
-     * Define a polymorphic one-to-one relationship.
767
-     *
768
-     * @param  mixed       $entity
769
-     * @param  string      $related
770
-     * @param  string      $name
771
-     * @param  string|null $type
772
-     * @param  string|null $id
773
-     * @param  string|null $localKey
774
-     * @throws MappingException
775
-     * @return \Analogue\ORM\Relationships\MorphOne
776
-     */
777
-    public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
778
-    {
779
-        list($type, $id) = $this->getMorphs($name, $type, $id);
780
-
781
-        $localKey = $localKey ?: $this->getKeyName();
782
-
783
-        $relatedMapper = Manager::getInstance()->mapper($related);
784
-
785
-        $table = $relatedMapper->getEntityMap()->getTable();
786
-
787
-        // Add the relation to the definition in map
788
-        list(, $caller) = debug_backtrace(false);
789
-        $relation = $caller['function'];
790
-        $this->relatedClasses[$relation] = $related;
791
-        $this->singleRelations[] = $relation;
792
-        $this->foreignRelations[] = $relation;
793
-        $this->nonProxyRelationships[] = $relation;
794
-
795
-        // This relationship will always be eager loaded, as proxying it would
796
-        // mean having an object that doesn't actually exists.
797
-        if(! in_array($relation, $this->with)) {
798
-            $this->with[] = $relation;
799
-        }
26
+	/**
27
+	 * The mapping driver to use with this entity
28
+	 *
29
+	 * @var  string
30
+	 */
31
+	protected $driver = 'illuminate';
32
+
33
+	/**
34
+	 * The Database Connection name for the model.
35
+	 *
36
+	 * @var string
37
+	 */
38
+	protected $connection;
39
+
40
+	/**
41
+	 * The table associated with the entity.
42
+	 *
43
+	 * @var string|null
44
+	 */
45
+	protected $table = null;
46
+
47
+	/**
48
+	 * The primary key for the model.
49
+	 *
50
+	 * @var string
51
+	 */
52
+	protected $primaryKey = 'id';
53
+
54
+	/**
55
+	 * Name of the entity's array property that should
56
+	 * contain the attributes
57
+	 * 
58
+	 * @var string
59
+	 */
60
+	protected $arrayName = 'attributes';
61
+
62
+	/**
63
+	 * Array containing the list of database columns to be mapped
64
+	 * in the attributes array of the entity. 
65
+	 *
66
+	 * @var array
67
+	 */
68
+	protected $attributes = [];
69
+
70
+	/**
71
+	 * Array containing the list of database columns to be mapped
72
+	 * to the entity's class properties. 
73
+	 *
74
+	 * @var array
75
+	 */
76
+	protected $properties = [];
77
+
78
+	/**
79
+	 * The Custom Domain Class to use with this mapping
80
+	 *
81
+	 * @var string|null
82
+	 */
83
+	protected $class = null;
84
+
85
+	/**
86
+	 * Embedded Value Objects
87
+	 * 
88
+	 * @var array
89
+	 */
90
+	protected $embeddables = [];
91
+
92
+	/**
93
+	 * Determine the relationships method used on the entity.
94
+	 * If not set, mapper will autodetect them
95
+	 *
96
+	 * @var array
97
+	 */
98
+	private $relationships = [];
99
+
100
+	/**
101
+	 * Relationships that should be treated as collection.
102
+	 *
103
+	 * @var array
104
+	 */
105
+	private $manyRelations = [];
106
+
107
+	/**
108
+	 * Relationships that should be treated as single entity.
109
+	 *
110
+	 * @var array
111
+	 */
112
+	private $singleRelations = [];
113
+
114
+	/**
115
+	 * Relationships for which the key is stored in the Entity itself
116
+	 *
117
+	 * @var array
118
+	 */
119
+	private $localRelations = [];
120
+
121
+	/** 
122
+	 * List of local keys associated to local relation methods
123
+	 * 
124
+	 * @var array
125
+	 */
126
+	private $localForeignKeys = [];
127
+
128
+	/**
129
+	 * Relationships for which the key is stored in the Related Entity
130
+	 *
131
+	 * @var array
132
+	 */
133
+	private $foreignRelations = [];
134
+
135
+	/**
136
+	 * Relationships which use a pivot record.
137
+	 *
138
+	 * @var array
139
+	 */
140
+	private $pivotRelations = [];
141
+
142
+	/**
143
+	 * Dynamic relationships
144
+	 *
145
+	 * @var array
146
+	 */
147
+	private $dynamicRelationships = [];
148
+
149
+	/**
150
+	 * Targetted class for the relationship method. value is set to `null` for
151
+	 * polymorphic relations. 
152
+	 * 
153
+	 * @var array
154
+	 */
155
+	private $relatedClasses = [];
156
+
157
+	/** 
158
+	 * Some relation methods like embedded objects, or HasOne and MorphOne,
159
+	 * will never have a proxy loaded on them. 
160
+	 * 
161
+	 * @var  array
162
+	 */
163
+	private $nonProxyRelationships = [];
164
+
165
+	/**
166
+	 * The number of models to return for pagination.
167
+	 *
168
+	 * @var int
169
+	 */
170
+	protected $perPage = 15;
171
+
172
+	/**
173
+	 * The relations to eager load on every query.
174
+	 *
175
+	 * @var array
176
+	 */
177
+	protected $with = [];
178
+
179
+	/**
180
+	 * The class name to be used in polymorphic relations.
181
+	 *
182
+	 * @var string
183
+	 */
184
+	protected $morphClass;
185
+
186
+	/**
187
+	 * Sequence name, to be used with postgreSql
188
+	 * defaults to %table_name%_id_seq
189
+	 *
190
+	 * @var string|null
191
+	 */
192
+	protected $sequence = null;
193
+
194
+	/**
195
+	 * Indicates if the entity should be timestamped.
196
+	 *
197
+	 * @var bool
198
+	 */
199
+	public $timestamps = false;
200
+
201
+	/**
202
+	 * The name of the "created at" column.
203
+	 *
204
+	 * @var string
205
+	 */
206
+	protected $createdAtColumn = 'created_at';
207
+
208
+	/**
209
+	 * The name of the "updated at" column.
210
+	 *
211
+	 * @var string
212
+	 */
213
+	protected $updatedAtColumn = 'updated_at';
214
+
215
+	/**
216
+	 * Indicates if the entity uses softdeletes
217
+	 *
218
+	 * @var boolean
219
+	 */
220
+	public $softDeletes = false;
221
+
222
+	/**
223
+	 * The name of the "deleted at" column.
224
+	 *
225
+	 * @var string
226
+	 */
227
+	protected $deletedAtColumn = 'deleted_at';
228
+
229
+	/**
230
+	 * The date format to use with the current database connection
231
+	 *
232
+	 * @var string
233
+	 */
234
+	protected $dateFormat;
235
+
236
+	/**
237
+	 * Set this property to true if the entity should be instantiated
238
+	 * using the IoC Container
239
+	 * 
240
+	 * @var boolean
241
+	 */
242
+	protected $dependencyInjection = false;
243
+
244
+	/**
245
+	 * Set the usage of inheritance, possible values are :
246
+	 * "single_table"
247
+	 * null
248
+	 * 
249
+	 * @var string | null
250
+	 */
251
+	protected $inheritanceType = null;
252
+
253
+	/**
254
+	 * Discriminator column name
255
+	 * 
256
+	 * @var string
257
+	 */
258
+	protected $discriminatorColumn = "type";
259
+
260
+	/**
261
+	 * Allow using a string to define which entity type should be instantiated.
262
+	 * If not set, analogue will uses entity's FQDN
263
+	 * 
264
+	 * @var array
265
+	 */
266
+	protected $discriminatorColumnMap = [];
267
+
268
+	/**
269
+	 * Return Domain class attributes, useful when mapping to a Plain PHP Object
270
+	 *
271
+	 * @return array
272
+	 */
273
+	public function getAttributes()
274
+	{
275
+		return $this->attributes;
276
+	}
277
+
278
+	/**
279
+	 * Set the domain class attributes
280
+	 *
281
+	 * @param array $attributeNames
282
+	 */
283
+	public function setAttributes(array $attributeNames)
284
+	{
285
+		$this->attributes = $attributeNames;
286
+	}
287
+
288
+	/**
289
+	 * Get all the attribute names for the class, including relationships, embeddables and primary key.
290
+	 *
291
+	 * @return array
292
+	 */
293
+	public function getCompiledAttributes()
294
+	{
295
+		$key = $this->getKeyName();
296
+
297
+		$embeddables = array_keys($this->getEmbeddables());
298
+
299
+		$relationships = $this->getRelationships();
300
+
301
+		$attributes = $this->getAttributes();
302
+
303
+		return array_merge([$key], $embeddables, $relationships, $attributes);
304
+	}
305
+
306
+	/**
307
+	 * Set the date format to use with the current database connection
308
+	 *
309
+	 * @param string $format
310
+	 */
311
+	public function setDateFormat($format)
312
+	{
313
+		$this->dateFormat = $format;
314
+	}
315
+
316
+	/**
317
+	 * Get the date format to use with the current database connection
318
+	 *
319
+	 *  @return string
320
+	 */
321
+	public function getDateFormat()
322
+	{
323
+		return $this->dateFormat;
324
+	}
325
+
326
+	/**
327
+	 * Set the Driver for this mapping
328
+	 *
329
+	 * @param string $driver
330
+	 */
331
+	public function setDriver($driver)
332
+	{
333
+		$this->driver = $driver;
334
+	}
335
+
336
+	/**
337
+	 * Get the Driver for this mapping.
338
+	 *
339
+	 * @return string
340
+	 */
341
+	public function getDriver()
342
+	{
343
+		return $this->driver;
344
+	}
345
+
346
+	/**
347
+	 * Set the db connection to use on the table
348
+	 *
349
+	 * @param $connection
350
+	 */
351
+	public function setConnection($connection)
352
+	{
353
+		$this->connection = $connection;
354
+	}
355
+
356
+	/**
357
+	 * Get the Database connection the Entity is stored on.
358
+	 *
359
+	 * @return string
360
+	 */
361
+	public function getConnection()
362
+	{
363
+		return $this->connection;
364
+	}
365
+
366
+	/**
367
+	 * Get the table associated with the entity.
368
+	 *
369
+	 * @return string
370
+	 */
371
+	public function getTable()
372
+	{
373
+		if (!is_null($this->table)) {
374
+			return $this->table;
375
+		}
376
+
377
+		return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
378
+	}
379
+
380
+	/**
381
+	 * Set the database table name
382
+	 *
383
+	 * @param  string $table
384
+	 */
385
+	public function setTable($table)
386
+	{
387
+		$this->table = $table;
388
+	}
389
+
390
+	/**
391
+	 * Get the pgSql sequence name
392
+	 *
393
+	 * @return string
394
+	 */
395
+	public function getSequence()
396
+	{
397
+		if (!is_null($this->sequence)) {
398
+			return $this->sequence;
399
+		} else {
400
+			return $this->getTable() . '_id_seq';
401
+		}
402
+	}
403
+
404
+	/**
405
+	 * Get the custom entity class
406
+	 *
407
+	 * @return string namespaced class name
408
+	 */
409
+	public function getClass()
410
+	{
411
+		return isset($this->class) ? $this->class : null;
412
+	}
413
+
414
+	/**
415
+	 * Set the custom entity class
416
+	 *
417
+	 * @param string $class namespaced class name
418
+	 */
419
+	public function setClass($class)
420
+	{
421
+		$this->class = $class;
422
+	}
423
+
424
+	/**
425
+	 * Get the embedded Value Objects
426
+	 *
427
+	 * @return array
428
+	 */
429
+	public function getEmbeddables()
430
+	{
431
+		return $this->embeddables;
432
+	}
433
+
434
+	/**
435
+	 * Set the embedded Value Objects
436
+	 *
437
+	 * @param array $embeddables
438
+	 */
439
+	public function setEmbeddables(array $embeddables)
440
+	{
441
+		$this->embeddables = $embeddables;
442
+	}
443
+
444
+	/**
445
+	 * Get the relationships to map on a custom domain
446
+	 * class.
447
+	 *
448
+	 * @return array
449
+	 */
450
+	public function getRelationships()
451
+	{
452
+		return $this->relationships;
453
+	}
454
+
455
+	/**  
456
+	 * Get the relationships that will not have a proxy
457
+	 * set on them
458
+	 * 
459
+	 * @return array
460
+	 */
461
+	public function getRelationshipsWithoutProxy()
462
+	{
463
+		return $this->nonProxyRelationships;
464
+	}
465
+
466
+	/**
467
+	 * Relationships of the Entity type
468
+	 *
469
+	 * @return array
470
+	 */
471
+	public function getSingleRelationships()
472
+	{
473
+		return $this->singleRelations;
474
+	}
475
+
476
+	/**
477
+	 * Relationships of type Collection
478
+	 *
479
+	 * @return array
480
+	 */
481
+	public function getManyRelationships()
482
+	{
483
+		return $this->manyRelations;
484
+	}
485
+
486
+	/**
487
+	 * Relationships with foreign key in the mapped entity record.
488
+	 *
489
+	 * @return array
490
+	 */
491
+	public function getLocalRelationships()
492
+	{
493
+		return $this->localRelations;
494
+	}
495
+
496
+	/**  
497
+	 * Return the local keys associated to the relationship
498
+	 * 
499
+	 * @param  string $relation
500
+	 * @return string | array | null
501
+	 */
502
+	public function getLocalKeys($relation)
503
+	{
504
+		return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
505
+	}
506
+
507
+	/**
508
+	 * Relationships with foreign key in the related Entity record
509
+	 *
510
+	 * @return array
511
+	 */
512
+	public function getForeignRelationships()
513
+	{
514
+		return $this->foreignRelations;
515
+	}
516
+
517
+	/**
518
+	 * Relationships which keys are stored in a pivot record
519
+	 *
520
+	 * @return array
521
+	 */
522
+	public function getPivotRelationships()
523
+	{
524
+		return $this->pivotRelations;
525
+	}
526
+
527
+	/**  
528
+	 * Get the targetted type for a relationship. Return null if polymorphic
529
+	 * 
530
+	 * @param  string  $relation
531
+	 * @return string | null
532
+	 */
533
+	public function getTargettedClass($relation)
534
+	{
535
+		return $this->relatedClasses[$relation];
536
+	}
537
+
538
+	/**
539
+	 * Add a Dynamic Relationship method at runtime. This has to be done
540
+	 * by hooking the 'initializing' event, before entityMap is initialized.
541
+	 *
542
+	 * @param string  $name         Relation name
543
+	 * @param \Closure $relationship
544
+	 *
545
+	 * @return void
546
+	 */
547
+	public function addRelationshipMethod($name, \Closure $relationship)
548
+	{
549
+		$this->dynamicRelationships[$name] = $relationship;
550
+	}
551
+
552
+	/**
553
+	 * Get the dynamic relationship method names.
554
+	 *
555
+	 * @return array
556
+	 */
557
+	public function getDynamicRelationships()
558
+	{
559
+		return array_keys($this->dynamicRelationships);
560
+	}
561
+
562
+	/**
563
+	 * Get the relationships that have to be eager loaded
564
+	 * on each request.
565
+	 *
566
+	 * @return array
567
+	 */
568
+	public function getEagerloadedRelationships()
569
+	{
570
+		return $this->with;
571
+	}
572
+
573
+	/**
574
+	 * Get the primary key for the entity.
575
+	 *
576
+	 * @return string
577
+	 */
578
+	public function getKeyName()
579
+	{
580
+		return $this->primaryKey;
581
+	}
582
+
583
+	/**
584
+	 * Set the primary key for the entity.
585
+	 *
586
+	 * @param $key
587
+	 * @return void
588
+	 */
589
+	public function setKeyName($key)
590
+	{
591
+		$this->primaryKey = $key;
592
+	}
593
+
594
+	/**
595
+	 * Get the table qualified key name.
596
+	 *
597
+	 * @return string
598
+	 */
599
+	public function getQualifiedKeyName()
600
+	{
601
+		return $this->getTable() . '.' . $this->getKeyName();
602
+	}
603
+
604
+	/**
605
+	 * Get the number of models to return per page.
606
+	 *
607
+	 * @return int
608
+	 */
609
+	public function getPerPage()
610
+	{
611
+		return $this->perPage;
612
+	}
613
+
614
+	/**
615
+	 * Set the number of models to return per page.
616
+	 *
617
+	 * @param  int $perPage
618
+	 * @return void
619
+	 */
620
+	public function setPerPage($perPage)
621
+	{
622
+		$this->perPage = $perPage;
623
+	}
624
+
625
+	/**
626
+	 * Determine if the entity uses get.
627
+	 *
628
+	 * @return bool
629
+	 */
630
+	public function usesTimestamps()
631
+	{
632
+		return $this->timestamps;
633
+	}
634
+
635
+	/**
636
+	 * Determine if the entity uses soft deletes
637
+	 *
638
+	 * @return bool
639
+	 */
640
+	public function usesSoftDeletes()
641
+	{
642
+		return $this->softDeletes;
643
+	}
644
+
645
+	/**
646
+	 * Get the 'created_at' column name
647
+	 *
648
+	 * @return string
649
+	 */
650
+	public function getCreatedAtColumn()
651
+	{
652
+		return $this->createdAtColumn;
653
+	}
654
+
655
+	/**
656
+	 * Get the 'updated_at' column name
657
+	 *
658
+	 * @return string
659
+	 */
660
+	public function getUpdatedAtColumn()
661
+	{
662
+		return $this->updatedAtColumn;
663
+	}
664
+
665
+	/**
666
+	 * Get the deleted_at column
667
+	 *
668
+	 * @return string
669
+	 */
670
+	public function getQualifiedDeletedAtColumn()
671
+	{
672
+		return $this->deletedAtColumn;
673
+	}
674
+
675
+	/**
676
+	 * Get the default foreign key name for the model.
677
+	 *
678
+	 * @return string
679
+	 */
680
+	public function getForeignKey()
681
+	{
682
+		return snake_case(class_basename($this->getClass())) . '_id';
683
+	}
684
+
685
+	/**
686
+	 * Return the inheritance type used by the entity.
687
+	 *
688
+	 * @return string|null
689
+	 */
690
+	public function getInheritanceType()
691
+	{
692
+		return $this->inheritanceType;
693
+	}
694
+
695
+	/**
696
+	 * Return the discriminator column name on the entity that's
697
+	 * used for table inheritance.
698
+	 *
699
+	 * @return string
700
+	 */
701
+	public function getDiscriminatorColumn()
702
+	{
703
+		return $this->discriminatorColumn;
704
+	}
705
+
706
+	/**
707
+	 * Return the mapping of discriminator column values to
708
+	 * entity class names that are used for table inheritance.
709
+	 *
710
+	 * @return array
711
+	 */
712
+	public function getDiscriminatorColumnMap()
713
+	{
714
+		return $this->discriminatorColumnMap;
715
+	}
716
+
717
+	/**
718
+	 * Return true if the entity should be instanciated using
719
+	 * the IoC Container
720
+	 * 
721
+	 * @return boolean
722
+	 */
723
+	public function useDependencyInjection()
724
+	{
725
+		return $this->dependencyInjection;
726
+	}
727
+
728
+	/**
729
+	 * Define a one-to-one relationship.
730
+	 *
731
+	 * @param  mixed  $entity
732
+	 * @param  string $related entity class
733
+	 * @param  string $foreignKey
734
+	 * @param  string $localKey
735
+	 * @throws MappingException
736
+	 * @return \Analogue\ORM\Relationships\HasOne
737
+	 */
738
+	public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
739
+	{
740
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
741
+
742
+		$relatedMapper = Manager::getInstance()->mapper($related);
743
+
744
+		$relatedMap = $relatedMapper->getEntityMap();
745
+
746
+		$localKey = $localKey ?: $this->getKeyName();
747
+
748
+		// Add the relation to the definition in map
749
+		list(, $caller) = debug_backtrace(false);
750
+		$relation = $caller['function'];
751
+		$this->relatedClasses[$relation] = $related;
752
+		$this->singleRelations[] = $relation;
753
+		$this->foreignRelations[] = $relation;
754
+		$this->nonProxyRelationships[] = $relation;
755
+
756
+		// This relationship will always be eager loaded, as proxying it would
757
+		// mean having an object that doesn't actually exists.
758
+		if(! in_array($relation, $this->with)) {
759
+			$this->with[] = $relation;
760
+		}
761
+
762
+		return new HasOne($relatedMapper, $entity, $relatedMap->getTable() . '.' . $foreignKey, $localKey);
763
+	}
764
+
765
+	/**
766
+	 * Define a polymorphic one-to-one relationship.
767
+	 *
768
+	 * @param  mixed       $entity
769
+	 * @param  string      $related
770
+	 * @param  string      $name
771
+	 * @param  string|null $type
772
+	 * @param  string|null $id
773
+	 * @param  string|null $localKey
774
+	 * @throws MappingException
775
+	 * @return \Analogue\ORM\Relationships\MorphOne
776
+	 */
777
+	public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
778
+	{
779
+		list($type, $id) = $this->getMorphs($name, $type, $id);
780
+
781
+		$localKey = $localKey ?: $this->getKeyName();
782
+
783
+		$relatedMapper = Manager::getInstance()->mapper($related);
784
+
785
+		$table = $relatedMapper->getEntityMap()->getTable();
786
+
787
+		// Add the relation to the definition in map
788
+		list(, $caller) = debug_backtrace(false);
789
+		$relation = $caller['function'];
790
+		$this->relatedClasses[$relation] = $related;
791
+		$this->singleRelations[] = $relation;
792
+		$this->foreignRelations[] = $relation;
793
+		$this->nonProxyRelationships[] = $relation;
794
+
795
+		// This relationship will always be eager loaded, as proxying it would
796
+		// mean having an object that doesn't actually exists.
797
+		if(! in_array($relation, $this->with)) {
798
+			$this->with[] = $relation;
799
+		}
800 800
         
801
-        return new MorphOne($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
802
-    }
803
-
804
-    /**
805
-     * Define an inverse one-to-one or many relationship.
806
-     *
807
-     * @param  mixed       $entity
808
-     * @param  string      $related
809
-     * @param  string|null $foreignKey
810
-     * @param  string|null $otherKey
811
-     * @param  string|null $relation
812
-     * @throws MappingException
813
-     * @return \Analogue\ORM\Relationships\BelongsTo
814
-     */
815
-    public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
816
-    {
817
-        // Add the relation to the definition in map
818
-        list(, $caller) = debug_backtrace(false);
819
-        $relation = $caller['function'];
820
-        $this->relatedClasses[$relation] = $related;
821
-        $this->singleRelations[] = $relation;
822
-        $this->localRelations[] = $relation;
823
-
824
-        // If no foreign key was supplied, we can use a backtrace to guess the proper
825
-        // foreign key name by using the name of the relationship function, which
826
-        // when combined with an "_id" should conventionally match the columns.
827
-        if (is_null($foreignKey)) {
828
-            $foreignKey = snake_case($relation) . '_id';
829
-        }
830
-
831
-        $this->localForeignKeys[$relation] = $foreignKey;
832
-
833
-        $relatedMapper = Manager::getInstance()->mapper($related);
834
-
835
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
836
-
837
-        return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
838
-    }
839
-
840
-    /**
841
-     * Define a polymorphic, inverse one-to-one or many relationship.
842
-     *
843
-     * @param  mixed       $entity
844
-     * @param  string|null $name
845
-     * @param  string|null $type
846
-     * @param  string|null $id
847
-     * @throws MappingException
848
-     * @return \Analogue\ORM\Relationships\MorphTo
849
-     */
850
-    public function morphTo($entity, $name = null, $type = null, $id = null)
851
-    {
852
-        // If no name is provided, we will use the backtrace to get the function name
853
-        // since that is most likely the name of the polymorphic interface. We can
854
-        // use that to get both the class and foreign key that will be utilized.
855
-        if (is_null($name)) {
856
-            list(, $caller) = debug_backtrace(false);
857
-
858
-            $name = snake_case($caller['function']);
859
-        }
860
-        $this->singleRelations[] = $name;
861
-        $this->localRelations[] = $relation;
862
-        $this->foreignRelations[] = $relation;
863
-        $this->relatedClass[$relation] = null;
864
-
865
-        list($type, $id) = $this->getMorphs($name, $type, $id);
866
-
867
-        // Store the foreign key in the entity map. 
868
-        // We might want to store the (key, type) as we might need it
869
-        // to build a MorphTo proxy
870
-        $this->localForeignKeys[$name] = [
871
-            "id" => $id,
872
-            "type" => $type
873
-        ];
801
+		return new MorphOne($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
802
+	}
803
+
804
+	/**
805
+	 * Define an inverse one-to-one or many relationship.
806
+	 *
807
+	 * @param  mixed       $entity
808
+	 * @param  string      $related
809
+	 * @param  string|null $foreignKey
810
+	 * @param  string|null $otherKey
811
+	 * @param  string|null $relation
812
+	 * @throws MappingException
813
+	 * @return \Analogue\ORM\Relationships\BelongsTo
814
+	 */
815
+	public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
816
+	{
817
+		// Add the relation to the definition in map
818
+		list(, $caller) = debug_backtrace(false);
819
+		$relation = $caller['function'];
820
+		$this->relatedClasses[$relation] = $related;
821
+		$this->singleRelations[] = $relation;
822
+		$this->localRelations[] = $relation;
823
+
824
+		// If no foreign key was supplied, we can use a backtrace to guess the proper
825
+		// foreign key name by using the name of the relationship function, which
826
+		// when combined with an "_id" should conventionally match the columns.
827
+		if (is_null($foreignKey)) {
828
+			$foreignKey = snake_case($relation) . '_id';
829
+		}
830
+
831
+		$this->localForeignKeys[$relation] = $foreignKey;
832
+
833
+		$relatedMapper = Manager::getInstance()->mapper($related);
834
+
835
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
836
+
837
+		return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
838
+	}
839
+
840
+	/**
841
+	 * Define a polymorphic, inverse one-to-one or many relationship.
842
+	 *
843
+	 * @param  mixed       $entity
844
+	 * @param  string|null $name
845
+	 * @param  string|null $type
846
+	 * @param  string|null $id
847
+	 * @throws MappingException
848
+	 * @return \Analogue\ORM\Relationships\MorphTo
849
+	 */
850
+	public function morphTo($entity, $name = null, $type = null, $id = null)
851
+	{
852
+		// If no name is provided, we will use the backtrace to get the function name
853
+		// since that is most likely the name of the polymorphic interface. We can
854
+		// use that to get both the class and foreign key that will be utilized.
855
+		if (is_null($name)) {
856
+			list(, $caller) = debug_backtrace(false);
857
+
858
+			$name = snake_case($caller['function']);
859
+		}
860
+		$this->singleRelations[] = $name;
861
+		$this->localRelations[] = $relation;
862
+		$this->foreignRelations[] = $relation;
863
+		$this->relatedClass[$relation] = null;
864
+
865
+		list($type, $id) = $this->getMorphs($name, $type, $id);
866
+
867
+		// Store the foreign key in the entity map. 
868
+		// We might want to store the (key, type) as we might need it
869
+		// to build a MorphTo proxy
870
+		$this->localForeignKeys[$name] = [
871
+			"id" => $id,
872
+			"type" => $type
873
+		];
874 874
         
875 875
 
876
-        $mapper = Manager::getInstance()->mapper(get_class($entity));
877
-
878
-        // If the type value is null it is probably safe to assume we're eager loading
879
-        // the relationship. When that is the case we will pass in a dummy query as
880
-        // there are multiple types in the morph and we can't use single queries.
881
-        $factory = new Factory;
882
-        $wrapper = $factory->make($entity);
883
-
884
-        if (is_null($class = $wrapper->getEntityAttribute($type))) {
885
-            return new MorphTo(
886
-                $mapper, $entity, $id, null, $type, $name
887
-            );
888
-        }
889
-
890
-        // If we are not eager loading the relationship we will essentially treat this
891
-        // as a belongs-to style relationship since morph-to extends that class and
892
-        // we will pass in the appropriate values so that it behaves as expected.
893
-        else {
894
-            $class = Manager::getInstance()->getInverseMorphMap($class);
895
-            $relatedMapper = Manager::getInstance()->mapper($class);
896
-
897
-            $foreignKey = $relatedMapper->getEntityMap()->getKeyName();
898
-
899
-            return new MorphTo(
900
-                $relatedMapper, $entity, $id, $foreignKey, $type, $name
901
-            );
902
-        }
903
-    }
904
-
905
-    /**
906
-     * Define a one-to-many relationship.
907
-     *
908
-     * @param  mixed       $entity
909
-     * @param  string      $related
910
-     * @param  string|null $foreignKey
911
-     * @param  string|null $localKey
912
-     * @throws MappingException
913
-     * @return \Analogue\ORM\Relationships\HasMany
914
-     */
915
-    public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
916
-    {
917
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
918
-
919
-        $relatedMapper = Manager::getInstance()->mapper($related);
920
-
921
-        $table = $relatedMapper->getEntityMap()->getTable() . '.' . $foreignKey;
922
-
923
-        $localKey = $localKey ?: $this->getKeyName();
924
-
925
-        // Add the relation to the definition in map
926
-        list(, $caller) = debug_backtrace(false);
927
-        $relation = $caller['function'];
928
-        $this->relatedClasses[$relation] = $related;
929
-        $this->manyRelations[] = $relation;
930
-        $this->foreignRelations[] = $relation;
931
-
932
-        return new HasMany($relatedMapper, $entity, $table, $localKey);
933
-    }
934
-
935
-    /**
936
-     * Define a has-many-through relationship.
937
-     *
938
-     * @param  mixed       $entity
939
-     * @param  string      $related
940
-     * @param  string      $through
941
-     * @param  string|null $firstKey
942
-     * @param  string|null $secondKey
943
-     * @throws MappingException
944
-     * @return \Analogue\ORM\Relationships\HasManyThrough
945
-     */
946
-    public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
947
-    {
948
-        $relatedMapper = Manager::getInstance()->mapper($related);
949
-
950
-        $throughMapper = Manager::getInstance()->mapper($through);
951
-
952
-
953
-        $firstKey = $firstKey ?: $this->getForeignKey();
954
-
955
-        $throughMap = $throughMapper->getEntityMap();
956
-
957
-        $secondKey = $secondKey ?: $throughMap->getForeignKey();
958
-
959
-        // Add the relation to the definition in map
960
-        list(, $caller) = debug_backtrace(false);
961
-        $relation = $caller['function'];
962
-        $this->relatedClasses[$relation] = $related;
963
-        $this->manyRelations[] = $relation;
964
-        $this->foreignRelations[] = $relation;
965
-
966
-        return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
967
-    }
968
-
969
-    /**
970
-     * Define a polymorphic one-to-many relationship.
971
-     *
972
-     * @param  mixed       $entity
973
-     * @param  string      $related
974
-     * @param  string      $name
975
-     * @param  string|null $type
976
-     * @param  string|null $id
977
-     * @param  string|null $localKey
978
-     * @return \Analogue\ORM\Relationships\MorphMany
979
-     */
980
-    public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
981
-    {
982
-        // Here we will gather up the morph type and ID for the relationship so that we
983
-        // can properly query the intermediate table of a relation. Finally, we will
984
-        // get the table and create the relationship instances for the developers.
985
-        list($type, $id) = $this->getMorphs($name, $type, $id);
986
-
987
-        $relatedMapper = Manager::getInstance()->mapper($related);
988
-
989
-        $table = $relatedMapper->getEntityMap()->getTable();
990
-
991
-        $localKey = $localKey ?: $this->getKeyName();
992
-
993
-        // Add the relation to the definition in map
994
-        list(, $caller) = debug_backtrace(false);
995
-        $relation = $caller['function'];
996
-        $this->relatedClasses[$relation] = $related;
997
-        $this->manyRelations[] = $relation;
998
-        $this->foreignRelations[] = $relation;
999
-
1000
-        return new MorphMany($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
1001
-    }
1002
-
1003
-    /**
1004
-     * Define a many-to-many relationship.
1005
-     *
1006
-     * @param  mixed       $entity
1007
-     * @param  string      $relatedClass
1008
-     * @param  string|null $table
1009
-     * @param  string|null $foreignKey
1010
-     * @param  string|null $otherKey
1011
-     * @param  string|null $relation
1012
-     * @throws MappingException
1013
-     * @return \Analogue\ORM\Relationships\BelongsToMany
1014
-     */
1015
-    public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1016
-    {
1017
-        // Add the relation to the definition in map
1018
-        list(, $caller) = debug_backtrace(false);
1019
-        $relation = $caller['function'];
1020
-        $this->relatedClasses[$relation] = $related;
1021
-        $this->manyRelations[] = $relation;
1022
-        $this->foreignRelations[] = $relation;
1023
-        $this->pivotRelations[] = $relation;
1024
-
1025
-        // First, we'll need to determine the foreign key and "other key" for the
1026
-        // relationship. Once we have determined the keys we'll make the query
1027
-        // instances as well as the relationship instances we need for this.
1028
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1029
-
1030
-        $relatedMapper = Manager::getInstance()->mapper($related);
1031
-
1032
-        $relatedMap = $relatedMapper->getEntityMap();
1033
-
1034
-        $otherKey = $otherKey ?: $relatedMap->getForeignKey();
1035
-
1036
-        // If no table name was provided, we can guess it by concatenating the two
1037
-        // models using underscores in alphabetical order. The two model names
1038
-        // are transformed to snake case from their default CamelCase also.
1039
-        if (is_null($table)) {
1040
-            $table = $this->joiningTable($relatedMap);
1041
-        }
1042
-
1043
-        return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1044
-    }
1045
-
1046
-    /**
1047
-     * Define a polymorphic many-to-many relationship.
1048
-     *
1049
-     * @param  mixed       $entity
1050
-     * @param  string      $related
1051
-     * @param  string      $name
1052
-     * @param  string|null $table
1053
-     * @param  string|null $foreignKey
1054
-     * @param  string|null $otherKey
1055
-     * @param  bool        $inverse
1056
-     * @throws MappingException
1057
-     * @return \Analogue\ORM\Relationships\MorphToMany
1058
-     */
1059
-    public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1060
-    {
1061
-        // Add the relation to the definition in map
1062
-        list(, $caller) = debug_backtrace(false);
1063
-        $relation = $caller['function'];
1064
-        $this->relatedClasses[$relation] = $related;
1065
-        $this->manyRelations[] = $relation;
1066
-        $this->foreignRelations[] = $relation;
1067
-        $this->pivotRelations[] = $relation;
1068
-
1069
-        // First, we will need to determine the foreign key and "other key" for the
1070
-        // relationship. Once we have determined the keys we will make the query
1071
-        // instances, as well as the relationship instances we need for these.
1072
-        $foreignKey = $foreignKey ?: $name . '_id';
1073
-
1074
-        $relatedMapper = Manager::getInstance()->mapper($related);
1075
-
1076
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1077
-
1078
-        $table = $table ?: str_plural($name);
1079
-
1080
-        return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1081
-    }
1082
-
1083
-    /**
1084
-     * Define a polymorphic, inverse many-to-many relationship.
1085
-     *
1086
-     * @param  mixed       $entity
1087
-     * @param  string      $related
1088
-     * @param  string      $name
1089
-     * @param  string|null $table
1090
-     * @param  string|null $foreignKey
1091
-     * @param  string|null $otherKey
1092
-     * @throws MappingException
1093
-     * @return \Analogue\ORM\Relationships\MorphToMany
1094
-     */
1095
-    public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1096
-    {
1097
-        // Add the relation to the definition in map
1098
-        list(, $caller) = debug_backtrace(false);
1099
-        $relation = $caller['function'];
1100
-        $this->relatedClasses[$relation] = $related;
1101
-        $this->manyRelations[] = $relation;
1102
-        $this->foreignRelations[] = $relation;
1103
-
1104
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1105
-
1106
-        // For the inverse of the polymorphic many-to-many relations, we will change
1107
-        // the way we determine the foreign and other keys, as it is the opposite
1108
-        // of the morph-to-many method since we're figuring out these inverses.
1109
-        $otherKey = $otherKey ?: $name . '_id';
1110
-
1111
-        return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1112
-    }
1113
-
1114
-    /**
1115
-     * Get the joining table name for a many-to-many relation.
1116
-     *
1117
-     * @param  EntityMap $relatedMap
1118
-     * @return string
1119
-     */
1120
-    public function joiningTable($relatedMap)
1121
-    {
1122
-        // The joining table name, by convention, is simply the snake cased models
1123
-        // sorted alphabetically and concatenated with an underscore, so we can
1124
-        // just sort the models and join them together to get the table name.
1125
-        $base = $this->getTable();
1126
-
1127
-        $related = $relatedMap->getTable();
1128
-
1129
-        $tables = [$related, $base];
1130
-
1131
-        // Now that we have the model names in an array we can just sort them and
1132
-        // use the implode function to join them together with an underscores,
1133
-        // which is typically used by convention within the database system.
1134
-        sort($tables);
1135
-
1136
-        return strtolower(implode('_', $tables));
1137
-    }
1138
-
1139
-    /**
1140
-     * Get the polymorphic relationship columns.
1141
-     *
1142
-     * @param  string $name
1143
-     * @param  string $type
1144
-     * @param  string $id
1145
-     * @return string[]
1146
-     */
1147
-    protected function getMorphs($name, $type, $id)
1148
-    {
1149
-        $type = $type ?: $name . '_type';
1150
-
1151
-        $id = $id ?: $name . '_id';
1152
-
1153
-        return [$type, $id];
1154
-    }
1155
-
1156
-    /**
1157
-     * Get the class name for polymorphic relations.
1158
-     *
1159
-     * @return string
1160
-     */
1161
-    public function getMorphClass()
1162
-    {
1163
-        $morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1164
-        return $this->morphClass ?: $morphClass;
1165
-    }
1166
-
1167
-    /**
1168
-     * Create a new Entity Collection instance.
1169
-     *
1170
-     * @param  array $entities
1171
-     * @return \Analogue\ORM\EntityCollection
1172
-     */
1173
-    public function newCollection(array $entities = [])
1174
-    {
1175
-        $collection = new EntityCollection($entities, $this);
1176
-        return $collection->keyBy($this->getKeyName());
1177
-    }
1178
-
1179
-    /**
1180
-     * Process EntityMap parsing at initialization time
1181
-     *
1182
-     * @return void
1183
-     */
1184
-    public function initialize()
1185
-    {
1186
-        $userMethods = $this->getCustomMethods();
1187
-
1188
-        // Parse EntityMap for method based relationship
1189
-        if (count($userMethods) > 0) {
1190
-            $this->relationships = $this->parseMethodsForRelationship($userMethods);
1191
-        }
1192
-
1193
-        // Parse EntityMap for dynamic relationships
1194
-        if (count($this->dynamicRelationships) > 0) {
1195
-            $this->relationships = $this->relationships + $this->getDynamicRelationships();
1196
-        }
1197
-    }
1198
-
1199
-    /**
1200
-     * Parse every relationships on the EntityMap and sort
1201
-     * them by type.
1202
-     *
1203
-     * @return void
1204
-     */
1205
-    public function boot()
1206
-    {
1207
-        if (count($this->relationships > 0)) {
1208
-            $this->sortRelationshipsByType();
1209
-        }
1210
-    }
1211
-
1212
-    /**
1213
-     * Get Methods that has been added in the child class.
1214
-     *
1215
-     * @return array
1216
-     */
1217
-    protected function getCustomMethods()
1218
-    {
1219
-        $mapMethods = get_class_methods($this);
1220
-
1221
-        $parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1222
-
1223
-        return array_diff($mapMethods, $parentsMethods);
1224
-    }
1225
-
1226
-    /**
1227
-     * Parse user's class methods for relationships
1228
-     *
1229
-     * @param  array $customMethods
1230
-     * @return array
1231
-     */
1232
-    protected function parseMethodsForRelationship(array $customMethods)
1233
-    {
1234
-        $relationships = [];
1235
-
1236
-        $class = new ReflectionClass(get_class($this));
1237
-
1238
-        // Get the mapped Entity class, as we will detect relationships
1239
-        // methods by testing that the first argument is type-hinted to
1240
-        // the same class as the mapped Entity.
1241
-        $entityClass = $this->getClass();
1242
-
1243
-        foreach ($customMethods as $methodName) {
1244
-            $method = $class->getMethod($methodName);
1245
-
1246
-            if ($method->getNumberOfParameters() > 0) {
1247
-                $params = $method->getParameters();
1248
-
1249
-                if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1250
-                    $relationships[] = $methodName;
1251
-                }
1252
-            }
1253
-        }
1254
-
1255
-        return $relationships;
1256
-    }
1257
-
1258
-    /**
1259
-     * Sort Relationships methods by type
1260
-     * 
1261
-     * TODO : replace this by direclty setting these value
1262
-     * in the corresponding methods, so we won't need
1263
-     * the correpondancy tabble
1264
-     * 
1265
-     * @return void
1266
-     */
1267
-    protected function sortRelationshipsByType()
1268
-    {
1269
-        $entityClass = $this->getClass();
1270
-
1271
-        // Instantiate a dummy entity which we will pass to relationship methods.
1272
-        $entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1273
-
1274
-        foreach ($this->relationships as $relation) {
1275
-            $this->$relation($entity);
1276
-        }
1277
-    }
1278
-
1279
-    /**
1280
-     * Override this method for custom entity instantiation
1281
-     *
1282
-     * @return null
1283
-     */
1284
-    public function activator()
1285
-    {
1286
-        return null;
1287
-    }
1288
-
1289
-    /**
1290
-     * Call dynamic relationship, if it exists
1291
-     *
1292
-     * @param  string $method
1293
-     * @param  array  $parameters
1294
-     * @throws Exception
1295
-     * @return mixed
1296
-     */
1297
-    public function __call($method, $parameters)
1298
-    {
1299
-        if (!array_key_exists($method, $this->dynamicRelationships)) {
1300
-            throw new Exception(get_class($this) . " has no method $method");
1301
-        }
1302
-
1303
-        // Add $this to parameters so the closure can call relationship method on the map.
1304
-        $parameters[] = $this;
1305
-
1306
-        return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1307
-    }
876
+		$mapper = Manager::getInstance()->mapper(get_class($entity));
877
+
878
+		// If the type value is null it is probably safe to assume we're eager loading
879
+		// the relationship. When that is the case we will pass in a dummy query as
880
+		// there are multiple types in the morph and we can't use single queries.
881
+		$factory = new Factory;
882
+		$wrapper = $factory->make($entity);
883
+
884
+		if (is_null($class = $wrapper->getEntityAttribute($type))) {
885
+			return new MorphTo(
886
+				$mapper, $entity, $id, null, $type, $name
887
+			);
888
+		}
889
+
890
+		// If we are not eager loading the relationship we will essentially treat this
891
+		// as a belongs-to style relationship since morph-to extends that class and
892
+		// we will pass in the appropriate values so that it behaves as expected.
893
+		else {
894
+			$class = Manager::getInstance()->getInverseMorphMap($class);
895
+			$relatedMapper = Manager::getInstance()->mapper($class);
896
+
897
+			$foreignKey = $relatedMapper->getEntityMap()->getKeyName();
898
+
899
+			return new MorphTo(
900
+				$relatedMapper, $entity, $id, $foreignKey, $type, $name
901
+			);
902
+		}
903
+	}
904
+
905
+	/**
906
+	 * Define a one-to-many relationship.
907
+	 *
908
+	 * @param  mixed       $entity
909
+	 * @param  string      $related
910
+	 * @param  string|null $foreignKey
911
+	 * @param  string|null $localKey
912
+	 * @throws MappingException
913
+	 * @return \Analogue\ORM\Relationships\HasMany
914
+	 */
915
+	public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
916
+	{
917
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
918
+
919
+		$relatedMapper = Manager::getInstance()->mapper($related);
920
+
921
+		$table = $relatedMapper->getEntityMap()->getTable() . '.' . $foreignKey;
922
+
923
+		$localKey = $localKey ?: $this->getKeyName();
924
+
925
+		// Add the relation to the definition in map
926
+		list(, $caller) = debug_backtrace(false);
927
+		$relation = $caller['function'];
928
+		$this->relatedClasses[$relation] = $related;
929
+		$this->manyRelations[] = $relation;
930
+		$this->foreignRelations[] = $relation;
931
+
932
+		return new HasMany($relatedMapper, $entity, $table, $localKey);
933
+	}
934
+
935
+	/**
936
+	 * Define a has-many-through relationship.
937
+	 *
938
+	 * @param  mixed       $entity
939
+	 * @param  string      $related
940
+	 * @param  string      $through
941
+	 * @param  string|null $firstKey
942
+	 * @param  string|null $secondKey
943
+	 * @throws MappingException
944
+	 * @return \Analogue\ORM\Relationships\HasManyThrough
945
+	 */
946
+	public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
947
+	{
948
+		$relatedMapper = Manager::getInstance()->mapper($related);
949
+
950
+		$throughMapper = Manager::getInstance()->mapper($through);
951
+
952
+
953
+		$firstKey = $firstKey ?: $this->getForeignKey();
954
+
955
+		$throughMap = $throughMapper->getEntityMap();
956
+
957
+		$secondKey = $secondKey ?: $throughMap->getForeignKey();
958
+
959
+		// Add the relation to the definition in map
960
+		list(, $caller) = debug_backtrace(false);
961
+		$relation = $caller['function'];
962
+		$this->relatedClasses[$relation] = $related;
963
+		$this->manyRelations[] = $relation;
964
+		$this->foreignRelations[] = $relation;
965
+
966
+		return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
967
+	}
968
+
969
+	/**
970
+	 * Define a polymorphic one-to-many relationship.
971
+	 *
972
+	 * @param  mixed       $entity
973
+	 * @param  string      $related
974
+	 * @param  string      $name
975
+	 * @param  string|null $type
976
+	 * @param  string|null $id
977
+	 * @param  string|null $localKey
978
+	 * @return \Analogue\ORM\Relationships\MorphMany
979
+	 */
980
+	public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
981
+	{
982
+		// Here we will gather up the morph type and ID for the relationship so that we
983
+		// can properly query the intermediate table of a relation. Finally, we will
984
+		// get the table and create the relationship instances for the developers.
985
+		list($type, $id) = $this->getMorphs($name, $type, $id);
986
+
987
+		$relatedMapper = Manager::getInstance()->mapper($related);
988
+
989
+		$table = $relatedMapper->getEntityMap()->getTable();
990
+
991
+		$localKey = $localKey ?: $this->getKeyName();
992
+
993
+		// Add the relation to the definition in map
994
+		list(, $caller) = debug_backtrace(false);
995
+		$relation = $caller['function'];
996
+		$this->relatedClasses[$relation] = $related;
997
+		$this->manyRelations[] = $relation;
998
+		$this->foreignRelations[] = $relation;
999
+
1000
+		return new MorphMany($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
1001
+	}
1002
+
1003
+	/**
1004
+	 * Define a many-to-many relationship.
1005
+	 *
1006
+	 * @param  mixed       $entity
1007
+	 * @param  string      $relatedClass
1008
+	 * @param  string|null $table
1009
+	 * @param  string|null $foreignKey
1010
+	 * @param  string|null $otherKey
1011
+	 * @param  string|null $relation
1012
+	 * @throws MappingException
1013
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
1014
+	 */
1015
+	public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1016
+	{
1017
+		// Add the relation to the definition in map
1018
+		list(, $caller) = debug_backtrace(false);
1019
+		$relation = $caller['function'];
1020
+		$this->relatedClasses[$relation] = $related;
1021
+		$this->manyRelations[] = $relation;
1022
+		$this->foreignRelations[] = $relation;
1023
+		$this->pivotRelations[] = $relation;
1024
+
1025
+		// First, we'll need to determine the foreign key and "other key" for the
1026
+		// relationship. Once we have determined the keys we'll make the query
1027
+		// instances as well as the relationship instances we need for this.
1028
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1029
+
1030
+		$relatedMapper = Manager::getInstance()->mapper($related);
1031
+
1032
+		$relatedMap = $relatedMapper->getEntityMap();
1033
+
1034
+		$otherKey = $otherKey ?: $relatedMap->getForeignKey();
1035
+
1036
+		// If no table name was provided, we can guess it by concatenating the two
1037
+		// models using underscores in alphabetical order. The two model names
1038
+		// are transformed to snake case from their default CamelCase also.
1039
+		if (is_null($table)) {
1040
+			$table = $this->joiningTable($relatedMap);
1041
+		}
1042
+
1043
+		return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1044
+	}
1045
+
1046
+	/**
1047
+	 * Define a polymorphic many-to-many relationship.
1048
+	 *
1049
+	 * @param  mixed       $entity
1050
+	 * @param  string      $related
1051
+	 * @param  string      $name
1052
+	 * @param  string|null $table
1053
+	 * @param  string|null $foreignKey
1054
+	 * @param  string|null $otherKey
1055
+	 * @param  bool        $inverse
1056
+	 * @throws MappingException
1057
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1058
+	 */
1059
+	public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1060
+	{
1061
+		// Add the relation to the definition in map
1062
+		list(, $caller) = debug_backtrace(false);
1063
+		$relation = $caller['function'];
1064
+		$this->relatedClasses[$relation] = $related;
1065
+		$this->manyRelations[] = $relation;
1066
+		$this->foreignRelations[] = $relation;
1067
+		$this->pivotRelations[] = $relation;
1068
+
1069
+		// First, we will need to determine the foreign key and "other key" for the
1070
+		// relationship. Once we have determined the keys we will make the query
1071
+		// instances, as well as the relationship instances we need for these.
1072
+		$foreignKey = $foreignKey ?: $name . '_id';
1073
+
1074
+		$relatedMapper = Manager::getInstance()->mapper($related);
1075
+
1076
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1077
+
1078
+		$table = $table ?: str_plural($name);
1079
+
1080
+		return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1081
+	}
1082
+
1083
+	/**
1084
+	 * Define a polymorphic, inverse many-to-many relationship.
1085
+	 *
1086
+	 * @param  mixed       $entity
1087
+	 * @param  string      $related
1088
+	 * @param  string      $name
1089
+	 * @param  string|null $table
1090
+	 * @param  string|null $foreignKey
1091
+	 * @param  string|null $otherKey
1092
+	 * @throws MappingException
1093
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1094
+	 */
1095
+	public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1096
+	{
1097
+		// Add the relation to the definition in map
1098
+		list(, $caller) = debug_backtrace(false);
1099
+		$relation = $caller['function'];
1100
+		$this->relatedClasses[$relation] = $related;
1101
+		$this->manyRelations[] = $relation;
1102
+		$this->foreignRelations[] = $relation;
1103
+
1104
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1105
+
1106
+		// For the inverse of the polymorphic many-to-many relations, we will change
1107
+		// the way we determine the foreign and other keys, as it is the opposite
1108
+		// of the morph-to-many method since we're figuring out these inverses.
1109
+		$otherKey = $otherKey ?: $name . '_id';
1110
+
1111
+		return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1112
+	}
1113
+
1114
+	/**
1115
+	 * Get the joining table name for a many-to-many relation.
1116
+	 *
1117
+	 * @param  EntityMap $relatedMap
1118
+	 * @return string
1119
+	 */
1120
+	public function joiningTable($relatedMap)
1121
+	{
1122
+		// The joining table name, by convention, is simply the snake cased models
1123
+		// sorted alphabetically and concatenated with an underscore, so we can
1124
+		// just sort the models and join them together to get the table name.
1125
+		$base = $this->getTable();
1126
+
1127
+		$related = $relatedMap->getTable();
1128
+
1129
+		$tables = [$related, $base];
1130
+
1131
+		// Now that we have the model names in an array we can just sort them and
1132
+		// use the implode function to join them together with an underscores,
1133
+		// which is typically used by convention within the database system.
1134
+		sort($tables);
1135
+
1136
+		return strtolower(implode('_', $tables));
1137
+	}
1138
+
1139
+	/**
1140
+	 * Get the polymorphic relationship columns.
1141
+	 *
1142
+	 * @param  string $name
1143
+	 * @param  string $type
1144
+	 * @param  string $id
1145
+	 * @return string[]
1146
+	 */
1147
+	protected function getMorphs($name, $type, $id)
1148
+	{
1149
+		$type = $type ?: $name . '_type';
1150
+
1151
+		$id = $id ?: $name . '_id';
1152
+
1153
+		return [$type, $id];
1154
+	}
1155
+
1156
+	/**
1157
+	 * Get the class name for polymorphic relations.
1158
+	 *
1159
+	 * @return string
1160
+	 */
1161
+	public function getMorphClass()
1162
+	{
1163
+		$morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1164
+		return $this->morphClass ?: $morphClass;
1165
+	}
1166
+
1167
+	/**
1168
+	 * Create a new Entity Collection instance.
1169
+	 *
1170
+	 * @param  array $entities
1171
+	 * @return \Analogue\ORM\EntityCollection
1172
+	 */
1173
+	public function newCollection(array $entities = [])
1174
+	{
1175
+		$collection = new EntityCollection($entities, $this);
1176
+		return $collection->keyBy($this->getKeyName());
1177
+	}
1178
+
1179
+	/**
1180
+	 * Process EntityMap parsing at initialization time
1181
+	 *
1182
+	 * @return void
1183
+	 */
1184
+	public function initialize()
1185
+	{
1186
+		$userMethods = $this->getCustomMethods();
1187
+
1188
+		// Parse EntityMap for method based relationship
1189
+		if (count($userMethods) > 0) {
1190
+			$this->relationships = $this->parseMethodsForRelationship($userMethods);
1191
+		}
1192
+
1193
+		// Parse EntityMap for dynamic relationships
1194
+		if (count($this->dynamicRelationships) > 0) {
1195
+			$this->relationships = $this->relationships + $this->getDynamicRelationships();
1196
+		}
1197
+	}
1198
+
1199
+	/**
1200
+	 * Parse every relationships on the EntityMap and sort
1201
+	 * them by type.
1202
+	 *
1203
+	 * @return void
1204
+	 */
1205
+	public function boot()
1206
+	{
1207
+		if (count($this->relationships > 0)) {
1208
+			$this->sortRelationshipsByType();
1209
+		}
1210
+	}
1211
+
1212
+	/**
1213
+	 * Get Methods that has been added in the child class.
1214
+	 *
1215
+	 * @return array
1216
+	 */
1217
+	protected function getCustomMethods()
1218
+	{
1219
+		$mapMethods = get_class_methods($this);
1220
+
1221
+		$parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1222
+
1223
+		return array_diff($mapMethods, $parentsMethods);
1224
+	}
1225
+
1226
+	/**
1227
+	 * Parse user's class methods for relationships
1228
+	 *
1229
+	 * @param  array $customMethods
1230
+	 * @return array
1231
+	 */
1232
+	protected function parseMethodsForRelationship(array $customMethods)
1233
+	{
1234
+		$relationships = [];
1235
+
1236
+		$class = new ReflectionClass(get_class($this));
1237
+
1238
+		// Get the mapped Entity class, as we will detect relationships
1239
+		// methods by testing that the first argument is type-hinted to
1240
+		// the same class as the mapped Entity.
1241
+		$entityClass = $this->getClass();
1242
+
1243
+		foreach ($customMethods as $methodName) {
1244
+			$method = $class->getMethod($methodName);
1245
+
1246
+			if ($method->getNumberOfParameters() > 0) {
1247
+				$params = $method->getParameters();
1248
+
1249
+				if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1250
+					$relationships[] = $methodName;
1251
+				}
1252
+			}
1253
+		}
1254
+
1255
+		return $relationships;
1256
+	}
1257
+
1258
+	/**
1259
+	 * Sort Relationships methods by type
1260
+	 * 
1261
+	 * TODO : replace this by direclty setting these value
1262
+	 * in the corresponding methods, so we won't need
1263
+	 * the correpondancy tabble
1264
+	 * 
1265
+	 * @return void
1266
+	 */
1267
+	protected function sortRelationshipsByType()
1268
+	{
1269
+		$entityClass = $this->getClass();
1270
+
1271
+		// Instantiate a dummy entity which we will pass to relationship methods.
1272
+		$entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1273
+
1274
+		foreach ($this->relationships as $relation) {
1275
+			$this->$relation($entity);
1276
+		}
1277
+	}
1278
+
1279
+	/**
1280
+	 * Override this method for custom entity instantiation
1281
+	 *
1282
+	 * @return null
1283
+	 */
1284
+	public function activator()
1285
+	{
1286
+		return null;
1287
+	}
1288
+
1289
+	/**
1290
+	 * Call dynamic relationship, if it exists
1291
+	 *
1292
+	 * @param  string $method
1293
+	 * @param  array  $parameters
1294
+	 * @throws Exception
1295
+	 * @return mixed
1296
+	 */
1297
+	public function __call($method, $parameters)
1298
+	{
1299
+		if (!array_key_exists($method, $this->dynamicRelationships)) {
1300
+			throw new Exception(get_class($this) . " has no method $method");
1301
+		}
1302
+
1303
+		// Add $this to parameters so the closure can call relationship method on the map.
1304
+		$parameters[] = $this;
1305
+
1306
+		return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1307
+	}
1308 1308
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
         if (!is_null($this->sequence)) {
398 398
             return $this->sequence;
399 399
         } else {
400
-            return $this->getTable() . '_id_seq';
400
+            return $this->getTable().'_id_seq';
401 401
         }
402 402
     }
403 403
 
@@ -598,7 +598,7 @@  discard block
 block discarded – undo
598 598
      */
599 599
     public function getQualifiedKeyName()
600 600
     {
601
-        return $this->getTable() . '.' . $this->getKeyName();
601
+        return $this->getTable().'.'.$this->getKeyName();
602 602
     }
603 603
 
604 604
     /**
@@ -679,7 +679,7 @@  discard block
 block discarded – undo
679 679
      */
680 680
     public function getForeignKey()
681 681
     {
682
-        return snake_case(class_basename($this->getClass())) . '_id';
682
+        return snake_case(class_basename($this->getClass())).'_id';
683 683
     }
684 684
 
685 685
     /**
@@ -755,11 +755,11 @@  discard block
 block discarded – undo
755 755
 
756 756
         // This relationship will always be eager loaded, as proxying it would
757 757
         // mean having an object that doesn't actually exists.
758
-        if(! in_array($relation, $this->with)) {
758
+        if (!in_array($relation, $this->with)) {
759 759
             $this->with[] = $relation;
760 760
         }
761 761
 
762
-        return new HasOne($relatedMapper, $entity, $relatedMap->getTable() . '.' . $foreignKey, $localKey);
762
+        return new HasOne($relatedMapper, $entity, $relatedMap->getTable().'.'.$foreignKey, $localKey);
763 763
     }
764 764
 
765 765
     /**
@@ -794,11 +794,11 @@  discard block
 block discarded – undo
794 794
 
795 795
         // This relationship will always be eager loaded, as proxying it would
796 796
         // mean having an object that doesn't actually exists.
797
-        if(! in_array($relation, $this->with)) {
797
+        if (!in_array($relation, $this->with)) {
798 798
             $this->with[] = $relation;
799 799
         }
800 800
         
801
-        return new MorphOne($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
801
+        return new MorphOne($relatedMapper, $entity, $table.'.'.$type, $table.'.'.$id, $localKey);
802 802
     }
803 803
 
804 804
     /**
@@ -825,7 +825,7 @@  discard block
 block discarded – undo
825 825
         // foreign key name by using the name of the relationship function, which
826 826
         // when combined with an "_id" should conventionally match the columns.
827 827
         if (is_null($foreignKey)) {
828
-            $foreignKey = snake_case($relation) . '_id';
828
+            $foreignKey = snake_case($relation).'_id';
829 829
         }
830 830
 
831 831
         $this->localForeignKeys[$relation] = $foreignKey;
@@ -918,7 +918,7 @@  discard block
 block discarded – undo
918 918
 
919 919
         $relatedMapper = Manager::getInstance()->mapper($related);
920 920
 
921
-        $table = $relatedMapper->getEntityMap()->getTable() . '.' . $foreignKey;
921
+        $table = $relatedMapper->getEntityMap()->getTable().'.'.$foreignKey;
922 922
 
923 923
         $localKey = $localKey ?: $this->getKeyName();
924 924
 
@@ -997,7 +997,7 @@  discard block
 block discarded – undo
997 997
         $this->manyRelations[] = $relation;
998 998
         $this->foreignRelations[] = $relation;
999 999
 
1000
-        return new MorphMany($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
1000
+        return new MorphMany($relatedMapper, $entity, $table.'.'.$type, $table.'.'.$id, $localKey);
1001 1001
     }
1002 1002
 
1003 1003
     /**
@@ -1069,7 +1069,7 @@  discard block
 block discarded – undo
1069 1069
         // First, we will need to determine the foreign key and "other key" for the
1070 1070
         // relationship. Once we have determined the keys we will make the query
1071 1071
         // instances, as well as the relationship instances we need for these.
1072
-        $foreignKey = $foreignKey ?: $name . '_id';
1072
+        $foreignKey = $foreignKey ?: $name.'_id';
1073 1073
 
1074 1074
         $relatedMapper = Manager::getInstance()->mapper($related);
1075 1075
 
@@ -1106,7 +1106,7 @@  discard block
 block discarded – undo
1106 1106
         // For the inverse of the polymorphic many-to-many relations, we will change
1107 1107
         // the way we determine the foreign and other keys, as it is the opposite
1108 1108
         // of the morph-to-many method since we're figuring out these inverses.
1109
-        $otherKey = $otherKey ?: $name . '_id';
1109
+        $otherKey = $otherKey ?: $name.'_id';
1110 1110
 
1111 1111
         return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1112 1112
     }
@@ -1146,9 +1146,9 @@  discard block
 block discarded – undo
1146 1146
      */
1147 1147
     protected function getMorphs($name, $type, $id)
1148 1148
     {
1149
-        $type = $type ?: $name . '_type';
1149
+        $type = $type ?: $name.'_type';
1150 1150
 
1151
-        $id = $id ?: $name . '_id';
1151
+        $id = $id ?: $name.'_id';
1152 1152
 
1153 1153
         return [$type, $id];
1154 1154
     }
@@ -1297,7 +1297,7 @@  discard block
 block discarded – undo
1297 1297
     public function __call($method, $parameters)
1298 1298
     {
1299 1299
         if (!array_key_exists($method, $this->dynamicRelationships)) {
1300
-            throw new Exception(get_class($this) . " has no method $method");
1300
+            throw new Exception(get_class($this)." has no method $method");
1301 1301
         }
1302 1302
 
1303 1303
         // Add $this to parameters so the closure can call relationship method on the map.
Please login to merge, or discard this patch.
src/System/Proxies/CollectionProxy.php 5 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -209,7 +209,7 @@
 block discarded – undo
209 209
      *
210 210
      * @param  int  $step
211 211
      * @param  int  $offset
212
-     * @return static
212
+     * @return boolean
213 213
      */
214 214
     public function every($step, $offset = 0)
215 215
     {
Please login to merge, or discard this patch.
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -2,7 +2,6 @@
 block discarded – undo
2 2
 
3 3
 namespace Analogue\ORM\System\Proxies;
4 4
 
5
-use Closure;
6 5
 use CachingIterator;
7 6
 use Analogue\ORM\EntityCollection;
8 7
 use ProxyManager\Proxy\ProxyInterface;
Please login to merge, or discard this patch.
Indentation   +1183 added lines, -1183 removed lines patch added patch discarded remove patch
@@ -10,1191 +10,1191 @@
 block discarded – undo
10 10
 
11 11
 class CollectionProxy extends EntityCollection implements ProxyInterface
12 12
 {
13
-    /** 
14
-     * Indicate if the relationship has been lazy loaded
15
-     * @var boolean
16
-     */
17
-    protected $relationshipLoaded = false;
18
-
19
-    protected $addedItems = [];
20
-
21
-	/**
22
-     * Create a new collection.
23
-     *
24
-     * @param mixed $entity
25
-     * @param string $relation
26
-     * 
27
-     * @return void
28
-     */
29
-    public function __construct($entity, $relation)
30
-    {
31
-        $this->parentEntity = $entity;
32
-        $this->relationshipMethod = $relation;
33
-    }
34
-
35
-    /**
36
-     * Return Items that has been added without lady loading
37
-     * the underlying collection
38
-     *
39
-     * @return array
40
-     */
41
-    public function getAddedItems()
42
-    {
43
-        return $this->addedItems;
44
-    }
45
-
46
-    /**
47
-     * Force initialization of the proxy
48
-     *
49
-     * @return bool true if the proxy could be initialized
50
-     */
51
-    public function initializeProxy() : bool
52
-    {
53
-        if($this->isProxyInitialized() ) return true;
13
+	/** 
14
+	 * Indicate if the relationship has been lazy loaded
15
+	 * @var boolean
16
+	 */
17
+	protected $relationshipLoaded = false;
18
+
19
+	protected $addedItems = [];
20
+
21
+	/**
22
+	 * Create a new collection.
23
+	 *
24
+	 * @param mixed $entity
25
+	 * @param string $relation
26
+	 * 
27
+	 * @return void
28
+	 */
29
+	public function __construct($entity, $relation)
30
+	{
31
+		$this->parentEntity = $entity;
32
+		$this->relationshipMethod = $relation;
33
+	}
34
+
35
+	/**
36
+	 * Return Items that has been added without lady loading
37
+	 * the underlying collection
38
+	 *
39
+	 * @return array
40
+	 */
41
+	public function getAddedItems()
42
+	{
43
+		return $this->addedItems;
44
+	}
45
+
46
+	/**
47
+	 * Force initialization of the proxy
48
+	 *
49
+	 * @return bool true if the proxy could be initialized
50
+	 */
51
+	public function initializeProxy() : bool
52
+	{
53
+		if($this->isProxyInitialized() ) return true;
54 54
         
55
-        $relation = $this->relationshipMethod;
56
-        $entity = $this->parentEntity;
57
-
58
-        $entityMap = Manager::getMapper($entity)->getEntityMap();
59
-
60
-        $this->items = $entityMap->$relation($entity)->getResults($relation)->all() + $this->addedItems;
61
-
62
-        $this->relationshipLoaded = true;
63
-
64
-        return true;
65
-    }
66
-
67
-    /**
68
-     * Retrieves current initialization status of the proxy
69
-     *
70
-     * @return bool
71
-     */
72
-    public function isProxyInitialized() : bool
73
-    {
74
-        return $this->relationshipLoaded;
75
-    }
76
-
77
-    /**
78
-     * Get all of the items in the collection.
79
-     *
80
-     * @return array
81
-     */
82
-    public function all()
83
-    {
84
-    	$this->initializeProxy();
85
-
86
-        return parent::all();
87
-    }
88
-
89
-    /**
90
-     * Get the average value of a given key.
91
-     *
92
-     * @param  callable|string|null  $callback
93
-     * @return mixed
94
-     */
95
-    public function avg($callback = null)
96
-    {
97
-        $this->initializeProxy();
98
-
99
-        return parent::avg($callback);
100
-    }
101
-
102
-    /**
103
-     * Get the median of a given key.
104
-     *
105
-     * @param  null $key
106
-     * @return mixed|null
107
-     */
108
-    public function median($key = null)
109
-    {
110
-        $this->initializeProxy();
111
-
112
-        return parent::median($key);
113
-    }
114
-
115
-    /**
116
-     * Get the mode of a given key.
117
-     *
118
-     * @param  mixed  $key
119
-     * @return array
120
-     */
121
-    public function mode($key = null)
122
-    {
123
-        $this->initializeProxy();
124
-
125
-        return parent::mode($key);
126
-    }
127
-
128
-    /**
129
-     * Collapse the collection of items into a single array.
130
-     *
131
-     * @return static
132
-     */
133
-    public function collapse()
134
-    {
135
-    	$this->initializeProxy();
136
-
137
-        return parent::collapse();
138
-    }
139
-
140
-    /**
141
-     * Determine if an item exists in the collection.
142
-     *
143
-     * @param  mixed  $key
144
-     * @param  mixed  $value
145
-     * @return bool
146
-     */
147
-    public function contains($key, $value = null)
148
-    {
149
-        $this->initializeProxy();
150
-
151
-        return parent::contains($key, $value);
152
-    }
153
-
154
-    /**
155
-     * Determine if an item exists in the collection using strict comparison.
156
-     *
157
-     * @param  mixed  $key
158
-     * @param  mixed  $value
159
-     * @return bool
160
-     */
161
-    public function containsStrict($key, $value = null)
162
-    {
163
-        $this->initializeProxy();
164
-
165
-        return parent::containsStrict($key, $value);
166
-    }
167
-
168
-    /**
169
-     * Get the items in the collection that are not present in the given items.
170
-     *
171
-     * @param  mixed  $items
172
-     * @return static
173
-     */
174
-    public function diff($items)
175
-    {
176
-        $this->initializeProxy();
177
-
178
-        return parent::diff($items);
179
-    }
180
-
181
-    /**
182
-     * Get the items in the collection whose keys are not present in the given items.
183
-     *
184
-     * @param  mixed  $items
185
-     * @return static
186
-     */
187
-    public function diffKeys($items)
188
-    {
189
-        $this->initializeProxy();
190
-
191
-        return parent::diffKeys($items);
192
-    }
193
-
194
-    /**
195
-     * Execute a callback over each item.
196
-     *
197
-     * @param  callable  $callback
198
-     * @return $this
199
-     */
200
-    public function each(callable $callback)
201
-    {
202
-        $this->initializeProxy();
203
-
204
-        return parent::each($callback);
205
-    }
206
-
207
-    /**
208
-     * Create a new collection consisting of every n-th element.
209
-     *
210
-     * @param  int  $step
211
-     * @param  int  $offset
212
-     * @return static
213
-     */
214
-    public function every($step, $offset = 0)
215
-    {
216
-        $this->initializeProxy();
217
-
218
-        return parent::every($step, $offset);
219
-    }
220
-
221
-    /**
222
-     * Get all items except for those with the specified keys.
223
-     *
224
-     * @param  mixed  $keys
225
-     * @return static
226
-     */
227
-    public function except($keys)
228
-    {
229
-        $this->initializeProxy();
230
-
231
-        return parent::except($keys);
232
-    }
233
-
234
-    /**
235
-     * Run a filter over each of the items.
236
-     *
237
-     * @param  callable|null  $callback
238
-     * @return static
239
-     */
240
-    public function filter(callable $callback = null)
241
-    {
242
-        $this->initializeProxy();
243
-
244
-        return parent::filter($callback);
245
-    }
246
-
247
-    /**
248
-     * Filter items by the given key value pair.
249
-     *
250
-     * @param  string  $key
251
-     * @param  mixed  $operator
252
-     * @param  mixed  $value
253
-     * @return static
254
-     */
255
-    public function where($key, $operator, $value = null)
256
-    {
257
-        $this->initializeProxy();
258
-
259
-        return parent::where($key, $operator, $value);
260
-    }
261
-
262
-    /**
263
-     * Filter items by the given key value pair using strict comparison.
264
-     *
265
-     * @param  string  $key
266
-     * @param  mixed  $value
267
-     * @return static
268
-     */
269
-    public function whereStrict($key, $value)
270
-    {
271
-        $this->initializeProxy();
272
-
273
-        return parent::whereStrict($key, $value);
274
-    }
275
-
276
-    /**
277
-     * Filter items by the given key value pair.
278
-     *
279
-     * @param  string  $key
280
-     * @param  mixed  $values
281
-     * @param  bool  $strict
282
-     * @return static
283
-     */
284
-    public function whereIn($key, $values, $strict = false)
285
-    {
286
-        $this->initializeProxy();
287
-
288
-        return parent::whereIn($key, $values, $strict);
289
-    }
290
-
291
-    /**
292
-     * Filter items by the given key value pair using strict comparison.
293
-     *
294
-     * @param  string  $key
295
-     * @param  mixed  $values
296
-     * @return static
297
-     */
298
-    public function whereInStrict($key, $values)
299
-    {
300
-    	$this->initializeProxy();
301
-
302
-        return parent::whereInStrict($key, $values);
303
-    }
304
-
305
-    /**
306
-     * Get the first item from the collection.
307
-     *
308
-     * @param  callable|null  $callback
309
-     * @param  mixed  $default
310
-     * @return mixed
311
-     */
312
-    public function first(callable $callback = null, $default = null)
313
-    {
314
-    	// TODO Consider partial loading
315
-    	$this->initializeProxy();
316
-
317
-        return parent::first($callback, $default);
318
-    }
319
-
320
-    /**
321
-     * Get a flattened array of the items in the collection.
322
-     *
323
-     * @param  int  $depth
324
-     * @return static
325
-     */
326
-    public function flatten($depth = INF)
327
-    {
328
-        $this->initializeProxy();
329
-
330
-        return parent::flatten($depth);
331
-    }
332
-
333
-    /**
334
-     * Flip the items in the collection.
335
-     *
336
-     * @return static
337
-     */
338
-    public function flip()
339
-    {
340
-        $this->initializeProxy();
341
-
342
-        return parent::flip();
343
-    }
344
-
345
-    /**
346
-     * Remove an item from the collection by key.
347
-     *
348
-     * @param  string|array  $keys
349
-     * @return $this
350
-     */
351
-    public function forget($keys)
352
-    {
353
-    	// TODO, we could consider these as 
354
-    	// 'pending deletion', the same way that
355
-    	// we treat added items
356
-        $this->initializeProxy();
357
-
358
-        return parent::forget($keys);
359
-    }
360
-
361
-    /**
362
-     * Get an item from the collection by key.
363
-     *
364
-     * @param  mixed  $key
365
-     * @param  mixed  $default
366
-     * @return mixed
367
-     */
368
-    public function get($key, $default = null)
369
-    {
370
-    	// TODO : We could also consider partial loading
371
-    	// here
372
-        $this->initializeProxy();
373
-
374
-        return parent::get($key, $default);
375
-    }
376
-
377
-    /**
378
-     * Group an associative array by a field or using a callback.
379
-     *
380
-     * @param  callable|string  $groupBy
381
-     * @param  bool  $preserveKeys
382
-     * @return static
383
-     */
384
-    public function groupBy($groupBy, $preserveKeys = false)
385
-    {
386
-        $this->initializeProxy();
387
-
388
-        return parent::groupBy($groupBy, $preserveKeys);
389
-    }
390
-
391
-    /**
392
-     * Key an associative array by a field or using a callback.
393
-     *
394
-     * @param  callable|string  $keyBy
395
-     * @return static
396
-     */
397
-    public function keyBy($keyBy)
398
-    {
399
-        $this->initializeProxy();
400
-
401
-        return parent::keyBy($keyBy);
402
-    }
403
-
404
-    /**
405
-     * Determine if an item exists in the collection by key.
406
-     *
407
-     * @param  mixed  $key
408
-     * @return bool
409
-     */
410
-    public function has($key)
411
-    {
412
-    	// TODO : we could do automagic here by directly
413
-    	// calling the database if the collection hasn't 
414
-    	// been initialized yet. 
415
-    	// Potential issue is that several calls to this
416
-    	// could cause a lot queries vs a single get query.
417
-        $this->initializeProxy();
418
-
419
-        return parent::has($key);
420
-    }
421
-
422
-    /**
423
-     * Concatenate values of a given key as a string.
424
-     *
425
-     * @param  string  $value
426
-     * @param  string  $glue
427
-     * @return string
428
-     */
429
-    public function implode($value, $glue = null)
430
-    {
431
-        $this->initializeProxy();
432
-
433
-        return parent::implode($value, $glue);
434
-    }
435
-
436
-    /**
437
-     * Intersect the collection with the given items.
438
-     *
439
-     * @param  mixed  $items
440
-     * @return static
441
-     */
442
-    public function intersect($items)
443
-    {
444
-        $this->initializeProxy();
445
-
446
-        return parent::intersect($items);
447
-    }
448
-
449
-    /**
450
-     * Determine if the collection is empty or not.
451
-     *
452
-     * @return bool
453
-     */
454
-    public function isEmpty()
455
-    {
456
-        $this->initializeProxy();
457
-
458
-        return parent::isEmpty();
459
-    }
460
-
461
-    /**
462
-     * Get the keys of the collection items.
463
-     *
464
-     * @return static
465
-     */
466
-    public function keys()
467
-    {
468
-        $this->initializeProxy();
469
-
470
-        return parent::keys();
471
-    }
472
-
473
-    /**
474
-     * Get the last item from the collection.
475
-     *
476
-     * @param  callable|null  $callback
477
-     * @param  mixed  $default
478
-     * @return mixed
479
-     */
480
-    public function last(callable $callback = null, $default = null)
481
-    {
482
-    	// TODO : we could do partial loading there as well
483
-        $this->initializeProxy();
484
-
485
-        return parent::last($callback, $default);
486
-    }
487
-
488
-    /**
489
-     * Get the values of a given key.
490
-     *
491
-     * @param  string  $value
492
-     * @param  string|null  $key
493
-     * @return static
494
-     */
495
-    public function pluck($value, $key = null)
496
-    {
497
-    	// TODO : automagic call to QB if not initialized
498
-        $this->initializeProxy();
499
-
500
-        return parent::pluck($value, $key);
501
-    }
502
-
503
-    /**
504
-     * Run a map over each of the items.
505
-     *
506
-     * @param  callable  $callback
507
-     * @return static
508
-     */
509
-    public function map(callable $callback)
510
-    {
511
-        $this->initializeProxy();
512
-
513
-        return parent::map($callback);
514
-    }
515
-
516
-    /**
517
-     * Run an associative map over each of the items.
518
-     *
519
-     * The callback should return an associative array with a single key/value pair.
520
-     *
521
-     * @param  callable  $callback
522
-     * @return static
523
-     */
524
-    public function mapWithKeys(callable $callback)
525
-    {
526
-        $this->initializeProxy();
527
-
528
-        return parent::mapWithKeys($callback);
529
-    }
530
-
531
-    /**
532
-     * Map a collection and flatten the result by a single level.
533
-     *
534
-     * @param  callable  $callback
535
-     * @return static
536
-     */
537
-    public function flatMap(callable $callback)
538
-    {
539
-        $this->initializeProxy();
540
-
541
-        return parent::flatMap($callback);
542
-    }
543
-
544
-    /**
545
-     * Get the max value of a given key.
546
-     *
547
-     * @param  callable|string|null  $callback
548
-     * @return mixed
549
-     */
550
-    public function max($callback = null)
551
-    {
552
-        $this->initializeProxy();
553
-
554
-        return parent::max($callback);
555
-    }
556
-
557
-    /**
558
-     * Merge the collection with the given items.
559
-     *
560
-     * @param  mixed  $items
561
-     * @return static
562
-     */
563
-    public function merge($items)
564
-    {
565
-    	// TODO : Check if the EntityCollection
566
-    	// returns a native Collection, as it 
567
-    	// is what we want here
568
-        $this->initializeProxy();
569
-
570
-        return parent::merge($items);
571
-    }
572
-
573
-    /**
574
-     * Create a collection by using this collection for keys and another for its values.
575
-     *
576
-     * @param  mixed  $values
577
-     * @return static
578
-     */
579
-    public function combine($values)
580
-    {
581
-        // TODO : Check if the EntityCollection
582
-    	// returns a native Collection, as it 
583
-    	// is what we want here
584
-        $this->initializeProxy();
585
-
586
-        return parent::combine($values);
587
-    }
588
-
589
-    /**
590
-     * Union the collection with the given items.
591
-     *
592
-     * @param  mixed  $items
593
-     * @return static
594
-     */
595
-    public function union($items)
596
-    {
597
-        // TODO : Check if the EntityCollection
598
-    	// returns a native Collection, as it 
599
-    	// is what we want here
600
-        $this->initializeProxy();
601
-
602
-        return parent::union($items);
603
-    }
604
-
605
-    /**
606
-     * Get the min value of a given key.
607
-     *
608
-     * @param  callable|string|null  $callback
609
-     * @return mixed
610
-     */
611
-    public function min($callback = null)
612
-    {
613
-        // TODO : we could rely on the QB
614
-        // for thos, if initialization has not
615
-        // take place yet
616
-        $this->initializeProxy();
617
-
618
-        return parent::min($callback);
619
-    }
620
-
621
-    /**
622
-     * Get the items with the specified keys.
623
-     *
624
-     * @param  mixed  $keys
625
-     * @return static
626
-     */
627
-    public function only($keys)
628
-    {
629
-        // TODO : we could rely on the QB if
630
-        // the collection hasn't been initialized yet
631
-        $this->initializeProxy();
632
-
633
-        return parent::only($keys);
634
-    }
635
-
636
-    /**
637
-     * "Paginate" the collection by slicing it into a smaller collection.
638
-     *
639
-     * @param  int  $page
640
-     * @param  int  $perPage
641
-     * @return static
642
-     */
643
-    public function forPage($page, $perPage)
644
-    {
645
-    	// TODO : check possibility of partial loading
646
-    	// if not initialized
647
-        $this->initializeProxy();
648
-
649
-        return parent::forPage($page, $perPage);
650
-    }
651
-
652
-    /**
653
-     * Partition the collection into two arrays using the given callback or key.
654
-     *
655
-     * @param  callable|string  $callback
656
-     * @return static
657
-     */
658
-    public function partition($callback)
659
-    {
660
-        $this->initializeProxy();
661
-
662
-        return parent::partition($callback);
663
-    }
664
-
665
-    /**
666
-     * Pass the collection to the given callback and return the result.
667
-     *
668
-     * @param  callable $callback
669
-     * @return mixed
670
-     */
671
-    public function pipe(callable $callback)
672
-    {
673
-        $this->initializeProxy();
674
-
675
-        return parent::pipe($callback);
676
-    }
677
-
678
-    /**
679
-     * Get and remove the last item from the collection.
680
-     *
681
-     * @return mixed
682
-     */
683
-    public function pop()
684
-    {
685
-        $this->initializeProxy();
686
-
687
-        return parent::pop();
688
-    }
689
-
690
-    /**
691
-     * Push an item onto the beginning of the collection.
692
-     *
693
-     * @param  mixed  $value
694
-     * @param  mixed  $key
695
-     * @return $this
696
-     */
697
-    public function prepend($value, $key = null)
698
-    {
699
-    	// TODO : partial adding of values.
700
-    	// we could have a $prepended , and $pushed arrays
701
-    	// which we would combine at full initialization
702
-
703
-        $this->initializeProxy();
704
-
705
-        return parent::prepend($value, $key);
706
-    }
707
-
708
-    /**
709
-     * Push an item onto the end of the collection.
710
-     *
711
-     * @param  mixed  $value
712
-     * @return $this
713
-     */
714
-    public function push($value)
715
-    {
716
-    	// TODO : partial adding of values.
717
-    	// we could have a $prepended , and $pushed arrays
718
-    	// which we would combine at full initialization
55
+		$relation = $this->relationshipMethod;
56
+		$entity = $this->parentEntity;
57
+
58
+		$entityMap = Manager::getMapper($entity)->getEntityMap();
59
+
60
+		$this->items = $entityMap->$relation($entity)->getResults($relation)->all() + $this->addedItems;
61
+
62
+		$this->relationshipLoaded = true;
63
+
64
+		return true;
65
+	}
66
+
67
+	/**
68
+	 * Retrieves current initialization status of the proxy
69
+	 *
70
+	 * @return bool
71
+	 */
72
+	public function isProxyInitialized() : bool
73
+	{
74
+		return $this->relationshipLoaded;
75
+	}
76
+
77
+	/**
78
+	 * Get all of the items in the collection.
79
+	 *
80
+	 * @return array
81
+	 */
82
+	public function all()
83
+	{
84
+		$this->initializeProxy();
85
+
86
+		return parent::all();
87
+	}
88
+
89
+	/**
90
+	 * Get the average value of a given key.
91
+	 *
92
+	 * @param  callable|string|null  $callback
93
+	 * @return mixed
94
+	 */
95
+	public function avg($callback = null)
96
+	{
97
+		$this->initializeProxy();
98
+
99
+		return parent::avg($callback);
100
+	}
101
+
102
+	/**
103
+	 * Get the median of a given key.
104
+	 *
105
+	 * @param  null $key
106
+	 * @return mixed|null
107
+	 */
108
+	public function median($key = null)
109
+	{
110
+		$this->initializeProxy();
111
+
112
+		return parent::median($key);
113
+	}
114
+
115
+	/**
116
+	 * Get the mode of a given key.
117
+	 *
118
+	 * @param  mixed  $key
119
+	 * @return array
120
+	 */
121
+	public function mode($key = null)
122
+	{
123
+		$this->initializeProxy();
124
+
125
+		return parent::mode($key);
126
+	}
127
+
128
+	/**
129
+	 * Collapse the collection of items into a single array.
130
+	 *
131
+	 * @return static
132
+	 */
133
+	public function collapse()
134
+	{
135
+		$this->initializeProxy();
136
+
137
+		return parent::collapse();
138
+	}
139
+
140
+	/**
141
+	 * Determine if an item exists in the collection.
142
+	 *
143
+	 * @param  mixed  $key
144
+	 * @param  mixed  $value
145
+	 * @return bool
146
+	 */
147
+	public function contains($key, $value = null)
148
+	{
149
+		$this->initializeProxy();
150
+
151
+		return parent::contains($key, $value);
152
+	}
153
+
154
+	/**
155
+	 * Determine if an item exists in the collection using strict comparison.
156
+	 *
157
+	 * @param  mixed  $key
158
+	 * @param  mixed  $value
159
+	 * @return bool
160
+	 */
161
+	public function containsStrict($key, $value = null)
162
+	{
163
+		$this->initializeProxy();
164
+
165
+		return parent::containsStrict($key, $value);
166
+	}
167
+
168
+	/**
169
+	 * Get the items in the collection that are not present in the given items.
170
+	 *
171
+	 * @param  mixed  $items
172
+	 * @return static
173
+	 */
174
+	public function diff($items)
175
+	{
176
+		$this->initializeProxy();
177
+
178
+		return parent::diff($items);
179
+	}
180
+
181
+	/**
182
+	 * Get the items in the collection whose keys are not present in the given items.
183
+	 *
184
+	 * @param  mixed  $items
185
+	 * @return static
186
+	 */
187
+	public function diffKeys($items)
188
+	{
189
+		$this->initializeProxy();
190
+
191
+		return parent::diffKeys($items);
192
+	}
193
+
194
+	/**
195
+	 * Execute a callback over each item.
196
+	 *
197
+	 * @param  callable  $callback
198
+	 * @return $this
199
+	 */
200
+	public function each(callable $callback)
201
+	{
202
+		$this->initializeProxy();
203
+
204
+		return parent::each($callback);
205
+	}
206
+
207
+	/**
208
+	 * Create a new collection consisting of every n-th element.
209
+	 *
210
+	 * @param  int  $step
211
+	 * @param  int  $offset
212
+	 * @return static
213
+	 */
214
+	public function every($step, $offset = 0)
215
+	{
216
+		$this->initializeProxy();
217
+
218
+		return parent::every($step, $offset);
219
+	}
220
+
221
+	/**
222
+	 * Get all items except for those with the specified keys.
223
+	 *
224
+	 * @param  mixed  $keys
225
+	 * @return static
226
+	 */
227
+	public function except($keys)
228
+	{
229
+		$this->initializeProxy();
230
+
231
+		return parent::except($keys);
232
+	}
233
+
234
+	/**
235
+	 * Run a filter over each of the items.
236
+	 *
237
+	 * @param  callable|null  $callback
238
+	 * @return static
239
+	 */
240
+	public function filter(callable $callback = null)
241
+	{
242
+		$this->initializeProxy();
243
+
244
+		return parent::filter($callback);
245
+	}
246
+
247
+	/**
248
+	 * Filter items by the given key value pair.
249
+	 *
250
+	 * @param  string  $key
251
+	 * @param  mixed  $operator
252
+	 * @param  mixed  $value
253
+	 * @return static
254
+	 */
255
+	public function where($key, $operator, $value = null)
256
+	{
257
+		$this->initializeProxy();
258
+
259
+		return parent::where($key, $operator, $value);
260
+	}
261
+
262
+	/**
263
+	 * Filter items by the given key value pair using strict comparison.
264
+	 *
265
+	 * @param  string  $key
266
+	 * @param  mixed  $value
267
+	 * @return static
268
+	 */
269
+	public function whereStrict($key, $value)
270
+	{
271
+		$this->initializeProxy();
272
+
273
+		return parent::whereStrict($key, $value);
274
+	}
275
+
276
+	/**
277
+	 * Filter items by the given key value pair.
278
+	 *
279
+	 * @param  string  $key
280
+	 * @param  mixed  $values
281
+	 * @param  bool  $strict
282
+	 * @return static
283
+	 */
284
+	public function whereIn($key, $values, $strict = false)
285
+	{
286
+		$this->initializeProxy();
287
+
288
+		return parent::whereIn($key, $values, $strict);
289
+	}
290
+
291
+	/**
292
+	 * Filter items by the given key value pair using strict comparison.
293
+	 *
294
+	 * @param  string  $key
295
+	 * @param  mixed  $values
296
+	 * @return static
297
+	 */
298
+	public function whereInStrict($key, $values)
299
+	{
300
+		$this->initializeProxy();
301
+
302
+		return parent::whereInStrict($key, $values);
303
+	}
304
+
305
+	/**
306
+	 * Get the first item from the collection.
307
+	 *
308
+	 * @param  callable|null  $callback
309
+	 * @param  mixed  $default
310
+	 * @return mixed
311
+	 */
312
+	public function first(callable $callback = null, $default = null)
313
+	{
314
+		// TODO Consider partial loading
315
+		$this->initializeProxy();
316
+
317
+		return parent::first($callback, $default);
318
+	}
319
+
320
+	/**
321
+	 * Get a flattened array of the items in the collection.
322
+	 *
323
+	 * @param  int  $depth
324
+	 * @return static
325
+	 */
326
+	public function flatten($depth = INF)
327
+	{
328
+		$this->initializeProxy();
329
+
330
+		return parent::flatten($depth);
331
+	}
332
+
333
+	/**
334
+	 * Flip the items in the collection.
335
+	 *
336
+	 * @return static
337
+	 */
338
+	public function flip()
339
+	{
340
+		$this->initializeProxy();
341
+
342
+		return parent::flip();
343
+	}
344
+
345
+	/**
346
+	 * Remove an item from the collection by key.
347
+	 *
348
+	 * @param  string|array  $keys
349
+	 * @return $this
350
+	 */
351
+	public function forget($keys)
352
+	{
353
+		// TODO, we could consider these as 
354
+		// 'pending deletion', the same way that
355
+		// we treat added items
356
+		$this->initializeProxy();
357
+
358
+		return parent::forget($keys);
359
+	}
360
+
361
+	/**
362
+	 * Get an item from the collection by key.
363
+	 *
364
+	 * @param  mixed  $key
365
+	 * @param  mixed  $default
366
+	 * @return mixed
367
+	 */
368
+	public function get($key, $default = null)
369
+	{
370
+		// TODO : We could also consider partial loading
371
+		// here
372
+		$this->initializeProxy();
373
+
374
+		return parent::get($key, $default);
375
+	}
376
+
377
+	/**
378
+	 * Group an associative array by a field or using a callback.
379
+	 *
380
+	 * @param  callable|string  $groupBy
381
+	 * @param  bool  $preserveKeys
382
+	 * @return static
383
+	 */
384
+	public function groupBy($groupBy, $preserveKeys = false)
385
+	{
386
+		$this->initializeProxy();
387
+
388
+		return parent::groupBy($groupBy, $preserveKeys);
389
+	}
390
+
391
+	/**
392
+	 * Key an associative array by a field or using a callback.
393
+	 *
394
+	 * @param  callable|string  $keyBy
395
+	 * @return static
396
+	 */
397
+	public function keyBy($keyBy)
398
+	{
399
+		$this->initializeProxy();
400
+
401
+		return parent::keyBy($keyBy);
402
+	}
403
+
404
+	/**
405
+	 * Determine if an item exists in the collection by key.
406
+	 *
407
+	 * @param  mixed  $key
408
+	 * @return bool
409
+	 */
410
+	public function has($key)
411
+	{
412
+		// TODO : we could do automagic here by directly
413
+		// calling the database if the collection hasn't 
414
+		// been initialized yet. 
415
+		// Potential issue is that several calls to this
416
+		// could cause a lot queries vs a single get query.
417
+		$this->initializeProxy();
418
+
419
+		return parent::has($key);
420
+	}
421
+
422
+	/**
423
+	 * Concatenate values of a given key as a string.
424
+	 *
425
+	 * @param  string  $value
426
+	 * @param  string  $glue
427
+	 * @return string
428
+	 */
429
+	public function implode($value, $glue = null)
430
+	{
431
+		$this->initializeProxy();
432
+
433
+		return parent::implode($value, $glue);
434
+	}
435
+
436
+	/**
437
+	 * Intersect the collection with the given items.
438
+	 *
439
+	 * @param  mixed  $items
440
+	 * @return static
441
+	 */
442
+	public function intersect($items)
443
+	{
444
+		$this->initializeProxy();
445
+
446
+		return parent::intersect($items);
447
+	}
448
+
449
+	/**
450
+	 * Determine if the collection is empty or not.
451
+	 *
452
+	 * @return bool
453
+	 */
454
+	public function isEmpty()
455
+	{
456
+		$this->initializeProxy();
457
+
458
+		return parent::isEmpty();
459
+	}
460
+
461
+	/**
462
+	 * Get the keys of the collection items.
463
+	 *
464
+	 * @return static
465
+	 */
466
+	public function keys()
467
+	{
468
+		$this->initializeProxy();
469
+
470
+		return parent::keys();
471
+	}
472
+
473
+	/**
474
+	 * Get the last item from the collection.
475
+	 *
476
+	 * @param  callable|null  $callback
477
+	 * @param  mixed  $default
478
+	 * @return mixed
479
+	 */
480
+	public function last(callable $callback = null, $default = null)
481
+	{
482
+		// TODO : we could do partial loading there as well
483
+		$this->initializeProxy();
484
+
485
+		return parent::last($callback, $default);
486
+	}
487
+
488
+	/**
489
+	 * Get the values of a given key.
490
+	 *
491
+	 * @param  string  $value
492
+	 * @param  string|null  $key
493
+	 * @return static
494
+	 */
495
+	public function pluck($value, $key = null)
496
+	{
497
+		// TODO : automagic call to QB if not initialized
498
+		$this->initializeProxy();
499
+
500
+		return parent::pluck($value, $key);
501
+	}
502
+
503
+	/**
504
+	 * Run a map over each of the items.
505
+	 *
506
+	 * @param  callable  $callback
507
+	 * @return static
508
+	 */
509
+	public function map(callable $callback)
510
+	{
511
+		$this->initializeProxy();
512
+
513
+		return parent::map($callback);
514
+	}
515
+
516
+	/**
517
+	 * Run an associative map over each of the items.
518
+	 *
519
+	 * The callback should return an associative array with a single key/value pair.
520
+	 *
521
+	 * @param  callable  $callback
522
+	 * @return static
523
+	 */
524
+	public function mapWithKeys(callable $callback)
525
+	{
526
+		$this->initializeProxy();
527
+
528
+		return parent::mapWithKeys($callback);
529
+	}
530
+
531
+	/**
532
+	 * Map a collection and flatten the result by a single level.
533
+	 *
534
+	 * @param  callable  $callback
535
+	 * @return static
536
+	 */
537
+	public function flatMap(callable $callback)
538
+	{
539
+		$this->initializeProxy();
540
+
541
+		return parent::flatMap($callback);
542
+	}
543
+
544
+	/**
545
+	 * Get the max value of a given key.
546
+	 *
547
+	 * @param  callable|string|null  $callback
548
+	 * @return mixed
549
+	 */
550
+	public function max($callback = null)
551
+	{
552
+		$this->initializeProxy();
553
+
554
+		return parent::max($callback);
555
+	}
556
+
557
+	/**
558
+	 * Merge the collection with the given items.
559
+	 *
560
+	 * @param  mixed  $items
561
+	 * @return static
562
+	 */
563
+	public function merge($items)
564
+	{
565
+		// TODO : Check if the EntityCollection
566
+		// returns a native Collection, as it 
567
+		// is what we want here
568
+		$this->initializeProxy();
569
+
570
+		return parent::merge($items);
571
+	}
572
+
573
+	/**
574
+	 * Create a collection by using this collection for keys and another for its values.
575
+	 *
576
+	 * @param  mixed  $values
577
+	 * @return static
578
+	 */
579
+	public function combine($values)
580
+	{
581
+		// TODO : Check if the EntityCollection
582
+		// returns a native Collection, as it 
583
+		// is what we want here
584
+		$this->initializeProxy();
585
+
586
+		return parent::combine($values);
587
+	}
588
+
589
+	/**
590
+	 * Union the collection with the given items.
591
+	 *
592
+	 * @param  mixed  $items
593
+	 * @return static
594
+	 */
595
+	public function union($items)
596
+	{
597
+		// TODO : Check if the EntityCollection
598
+		// returns a native Collection, as it 
599
+		// is what we want here
600
+		$this->initializeProxy();
601
+
602
+		return parent::union($items);
603
+	}
604
+
605
+	/**
606
+	 * Get the min value of a given key.
607
+	 *
608
+	 * @param  callable|string|null  $callback
609
+	 * @return mixed
610
+	 */
611
+	public function min($callback = null)
612
+	{
613
+		// TODO : we could rely on the QB
614
+		// for thos, if initialization has not
615
+		// take place yet
616
+		$this->initializeProxy();
617
+
618
+		return parent::min($callback);
619
+	}
620
+
621
+	/**
622
+	 * Get the items with the specified keys.
623
+	 *
624
+	 * @param  mixed  $keys
625
+	 * @return static
626
+	 */
627
+	public function only($keys)
628
+	{
629
+		// TODO : we could rely on the QB if
630
+		// the collection hasn't been initialized yet
631
+		$this->initializeProxy();
632
+
633
+		return parent::only($keys);
634
+	}
635
+
636
+	/**
637
+	 * "Paginate" the collection by slicing it into a smaller collection.
638
+	 *
639
+	 * @param  int  $page
640
+	 * @param  int  $perPage
641
+	 * @return static
642
+	 */
643
+	public function forPage($page, $perPage)
644
+	{
645
+		// TODO : check possibility of partial loading
646
+		// if not initialized
647
+		$this->initializeProxy();
648
+
649
+		return parent::forPage($page, $perPage);
650
+	}
651
+
652
+	/**
653
+	 * Partition the collection into two arrays using the given callback or key.
654
+	 *
655
+	 * @param  callable|string  $callback
656
+	 * @return static
657
+	 */
658
+	public function partition($callback)
659
+	{
660
+		$this->initializeProxy();
661
+
662
+		return parent::partition($callback);
663
+	}
664
+
665
+	/**
666
+	 * Pass the collection to the given callback and return the result.
667
+	 *
668
+	 * @param  callable $callback
669
+	 * @return mixed
670
+	 */
671
+	public function pipe(callable $callback)
672
+	{
673
+		$this->initializeProxy();
674
+
675
+		return parent::pipe($callback);
676
+	}
677
+
678
+	/**
679
+	 * Get and remove the last item from the collection.
680
+	 *
681
+	 * @return mixed
682
+	 */
683
+	public function pop()
684
+	{
685
+		$this->initializeProxy();
686
+
687
+		return parent::pop();
688
+	}
689
+
690
+	/**
691
+	 * Push an item onto the beginning of the collection.
692
+	 *
693
+	 * @param  mixed  $value
694
+	 * @param  mixed  $key
695
+	 * @return $this
696
+	 */
697
+	public function prepend($value, $key = null)
698
+	{
699
+		// TODO : partial adding of values.
700
+		// we could have a $prepended , and $pushed arrays
701
+		// which we would combine at full initialization
702
+
703
+		$this->initializeProxy();
704
+
705
+		return parent::prepend($value, $key);
706
+	}
707
+
708
+	/**
709
+	 * Push an item onto the end of the collection.
710
+	 *
711
+	 * @param  mixed  $value
712
+	 * @return $this
713
+	 */
714
+	public function push($value)
715
+	{
716
+		// TODO : partial adding of values.
717
+		// we could have a $prepended , and $pushed arrays
718
+		// which we would combine at full initialization
719 719
   
720
-        $this->initializeProxy();
721
-
722
-        return parent::push($value);
723
-    }
724
-
725
-    /**
726
-     * Get and remove an item from the collection.
727
-     *
728
-     * @param  mixed  $key
729
-     * @param  mixed  $default
730
-     * @return mixed
731
-     */
732
-    public function pull($key, $default = null)
733
-    {
734
-    	// TODO : QB query if the collection
735
-    	// hasn't been initialized yet
736
-
737
-        $this->initializeProxy();
738
-
739
-        return parent::pull($key, $default);
740
-    }
741
-
742
-    /**
743
-     * Put an item in the collection by key.
744
-     *
745
-     * @param  mixed  $key
746
-     * @param  mixed  $value
747
-     * @return $this
748
-     */
749
-    public function put($key, $value)
750
-    {
751
-        // TODO : Partial loading ?
752
-
753
-        $this->initializeProxy();
754
-
755
-        return parent::put($key, $value);
756
-    }
757
-
758
-    /**
759
-     * Get one or more items randomly from the collection.
760
-     *
761
-     * @param  int  $amount
762
-     * @return mixed
763
-     *
764
-     * @throws \InvalidArgumentException
765
-     */
766
-    public function random($amount = 1)
767
-    {
768
-    	// TODO : we could optimize this by only 
769
-    	// fetching the keys from the database
770
-    	// and performing partial loading
771
-
772
-        $this->initializeProxy();
773
-
774
-        return parent::random($amount);
775
-    }
776
-
777
-    /**
778
-     * Reduce the collection to a single value.
779
-     *
780
-     * @param  callable  $callback
781
-     * @param  mixed     $initial
782
-     * @return mixed
783
-     */
784
-    public function reduce(callable $callback, $initial = null)
785
-    {
786
-        $this->initializeProxy();
787
-
788
-        return parent::reduce($callback, $initial);
789
-    }
790
-
791
-    /**
792
-     * Create a collection of all elements that do not pass a given truth test.
793
-     *
794
-     * @param  callable|mixed  $callback
795
-     * @return static
796
-     */
797
-    public function reject($callback)
798
-    {
799
-        $this->initializeProxy();
800
-
801
-        return parent::reject($callback);
802
-    }
803
-
804
-    /**
805
-     * Reverse items order.
806
-     *
807
-     * @return static
808
-     */
809
-    public function reverse()
810
-    {
811
-        $this->initializeProxy();
812
-
813
-        return parent::reverse();
814
-    }
815
-
816
-    /**
817
-     * Search the collection for a given value and return the corresponding key if successful.
818
-     *
819
-     * @param  mixed  $value
820
-     * @param  bool   $strict
821
-     * @return mixed
822
-     */
823
-    public function search($value, $strict = false)
824
-    {
825
-        $this->initializeProxy();
826
-
827
-        return parent::search($value, $strict);
828
-    }
829
-
830
-    /**
831
-     * Get and remove the first item from the collection.
832
-     *
833
-     * @return mixed
834
-     */
835
-    public function shift()
836
-    {
837
-    	// Todo : Partial Removing
838
-    	// we could have a pending removal array
839
-        $this->initializeProxy();
840
-
841
-        return parent::shift();
842
-    }
843
-
844
-    /**
845
-     * Shuffle the items in the collection.
846
-     *
847
-     * @param int $seed
848
-     * @return static
849
-     */
850
-    public function shuffle($seed = null)
851
-    {
852
-    	$this->initializeProxy();
853
-
854
-        return parent::shuffle($seed);
855
-    }
856
-
857
-    /**
858
-     * Slice the underlying collection array.
859
-     *
860
-     * @param  int   $offset
861
-     * @param  int   $length
862
-     * @return static
863
-     */
864
-    public function slice($offset, $length = null)
865
-    {
866
-        $this->initializeProxy();
867
-
868
-        return parent::slice($offset, $length);
869
-    }
870
-
871
-    /**
872
-     * Split a collection into a certain number of groups.
873
-     *
874
-     * @param  int  $numberOfGroups
875
-     * @return static
876
-     */
877
-    public function split($numberOfGroups)
878
-    {
879
-        $this->initializeProxy();
880
-
881
-        return parent::split($numberOfGroups);
882
-    }
883
-
884
-    /**
885
-     * Chunk the underlying collection array.
886
-     *
887
-     * @param  int   $size
888
-     * @return static
889
-     */
890
-    public function chunk($size)
891
-    {
892
-    	// TODO : partial loading ?
893
-        $this->initializeProxy();
894
-
895
-        return parent::chunk($size);
896
-    }
897
-
898
-    /**
899
-     * Sort through each item with a callback.
900
-     *
901
-     * @param  callable|null  $callback
902
-     * @return static
903
-     */
904
-    public function sort(callable $callback = null)
905
-    {
906
-        $this->initializeProxy();
907
-
908
-        return parent::sort($callback);
909
-    }
910
-
911
-    /**
912
-     * Sort the collection using the given callback.
913
-     *
914
-     * @param  callable|string  $callback
915
-     * @param  int   $options
916
-     * @param  bool  $descending
917
-     * @return static
918
-     */
919
-    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
920
-    {
921
-        $this->initializeProxy();
922
-
923
-        return parent::sort($callback, $options, $descending);
924
-    }
925
-
926
-    /**
927
-     * Splice a portion of the underlying collection array.
928
-     *
929
-     * @param  int  $offset
930
-     * @param  int|null  $length
931
-     * @param  mixed  $replacement
932
-     * @return static
933
-     */
934
-    public function splice($offset, $length = null, $replacement = [])
935
-    {
936
-        $this->initializeProxy();
937
-
938
-        return parent::splice($offset, $length, $replacement);
939
-    }
940
-
941
-    /**
942
-     * Get the sum of the given values.
943
-     *
944
-     * @param  callable|string|null  $callback
945
-     * @return mixed
946
-     */
947
-    public function sum($callback = null)
948
-    {
949
-        $this->initializeProxy();
950
-
951
-        return parent::sum($callback);
952
-    }
953
-
954
-    /**
955
-     * Take the first or last {$limit} items.
956
-     *
957
-     * @param  int  $limit
958
-     * @return static
959
-     */
960
-    public function take($limit)
961
-    {	
962
-    	// TODO: partial loading
963
-        $this->initializeProxy();
964
-
965
-        return parent::take($limit);
966
-    }
967
-
968
-    /**
969
-     * Transform each item in the collection using a callback.
970
-     *
971
-     * @param  callable  $callback
972
-     * @return $this
973
-     */
974
-    public function transform(callable $callback)
975
-    {
976
-        $this->initializeProxy();
977
-
978
-        return parent::transform($callback);
979
-    }
980
-
981
-    /**
982
-     * Return only unique items from the collection array.
983
-     *
984
-     * @param  string|callable|null  $key
985
-     * @param  bool  $strict
986
-     *
987
-     * @return static
988
-     */
989
-    public function unique($key = null, $strict = false)
990
-    {
991
-        $this->initializeProxy();
992
-
993
-        return parent::unique($key, $strict);
994
-    }
995
-
996
-    /**
997
-     * Reset the keys on the underlying array.
998
-     *
999
-     * @return static
1000
-     */
1001
-    public function values()
1002
-    {
1003
-        $this->initializeProxy();
1004
-
1005
-        return parent::values();
1006
-    }
1007
-
1008
-    /**
1009
-     * Zip the collection together with one or more arrays.
1010
-     *
1011
-     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1012
-     *      => [[1, 4], [2, 5], [3, 6]]
1013
-     *
1014
-     * @param  mixed ...$items
1015
-     * @return static
1016
-     */
1017
-    public function zip($items)
1018
-    {
1019
-        $this->initializeProxy();
1020
-
1021
-        return parent::zip($items);
1022
-    }
1023
-
1024
-    /**
1025
-     * Get the collection of items as a plain array.
1026
-     *
1027
-     * @return array
1028
-     */
1029
-    public function toArray()
1030
-    {
1031
-    	// If this is called on all subsequent proxy, 
1032
-    	// this would eventually trigger all lazy loading,
1033
-    	// which is NOT what we would expect... 
1034
-    	// TODO : must think of this. 
1035
-        $this->initializeProxy();
1036
-
1037
-        return parent::toArray();
1038
-    }
1039
-
1040
-    /**
1041
-     * Convert the object into something JSON serializable.
1042
-     *
1043
-     * @return array
1044
-     */
1045
-    public function jsonSerialize()
1046
-    {
1047
-        // If this is called on all subsequent proxy, 
1048
-    	// this would eventually trigger all lazy loading,
1049
-    	// which is NOT what we would expect... 
1050
-    	// TODO : must think of this. 
1051
-        $this->initializeProxy();
1052
-
1053
-        return parent::jsonSerialize();
1054
-    }
1055
-
1056
-    /**
1057
-     * Get the collection of items as JSON.
1058
-     *
1059
-     * @param  int  $options
1060
-     * @return string
1061
-     */
1062
-    public function toJson($options = 0)
1063
-    {
1064
-        // If this is called on all subsequent proxy, 
1065
-    	// this would eventually trigger all lazy loading,
1066
-    	// which is NOT what we would expect... 
1067
-    	// TODO : must think of this. 
1068
-        $this->initializeProxy();
1069
-
1070
-        return parent::toJson($options);
1071
-    }
1072
-
1073
-    /**
1074
-     * Get an iterator for the items.
1075
-     *
1076
-     * @return \ArrayIterator
1077
-     */
1078
-    public function getIterator()
1079
-    {
1080
-        $this->initializeProxy();
1081
-
1082
-        return parent::getIterator();
1083
-    }
1084
-
1085
-    /**
1086
-     * Get a CachingIterator instance.
1087
-     *
1088
-     * @param  int  $flags
1089
-     * @return \CachingIterator
1090
-     */
1091
-    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1092
-    {
1093
-        $this->initializeProxy();
1094
-
1095
-        return parent::getCachingIterator($flags);
1096
-    }
1097
-
1098
-    /**
1099
-     * Count the number of items in the collection.
1100
-     *
1101
-     * @return int
1102
-     */
1103
-    public function count()
1104
-    {
1105
-    	// TODO rely on QB if not initialized
1106
-        $this->initializeProxy();
720
+		$this->initializeProxy();
721
+
722
+		return parent::push($value);
723
+	}
724
+
725
+	/**
726
+	 * Get and remove an item from the collection.
727
+	 *
728
+	 * @param  mixed  $key
729
+	 * @param  mixed  $default
730
+	 * @return mixed
731
+	 */
732
+	public function pull($key, $default = null)
733
+	{
734
+		// TODO : QB query if the collection
735
+		// hasn't been initialized yet
736
+
737
+		$this->initializeProxy();
738
+
739
+		return parent::pull($key, $default);
740
+	}
741
+
742
+	/**
743
+	 * Put an item in the collection by key.
744
+	 *
745
+	 * @param  mixed  $key
746
+	 * @param  mixed  $value
747
+	 * @return $this
748
+	 */
749
+	public function put($key, $value)
750
+	{
751
+		// TODO : Partial loading ?
752
+
753
+		$this->initializeProxy();
754
+
755
+		return parent::put($key, $value);
756
+	}
757
+
758
+	/**
759
+	 * Get one or more items randomly from the collection.
760
+	 *
761
+	 * @param  int  $amount
762
+	 * @return mixed
763
+	 *
764
+	 * @throws \InvalidArgumentException
765
+	 */
766
+	public function random($amount = 1)
767
+	{
768
+		// TODO : we could optimize this by only 
769
+		// fetching the keys from the database
770
+		// and performing partial loading
771
+
772
+		$this->initializeProxy();
773
+
774
+		return parent::random($amount);
775
+	}
776
+
777
+	/**
778
+	 * Reduce the collection to a single value.
779
+	 *
780
+	 * @param  callable  $callback
781
+	 * @param  mixed     $initial
782
+	 * @return mixed
783
+	 */
784
+	public function reduce(callable $callback, $initial = null)
785
+	{
786
+		$this->initializeProxy();
787
+
788
+		return parent::reduce($callback, $initial);
789
+	}
790
+
791
+	/**
792
+	 * Create a collection of all elements that do not pass a given truth test.
793
+	 *
794
+	 * @param  callable|mixed  $callback
795
+	 * @return static
796
+	 */
797
+	public function reject($callback)
798
+	{
799
+		$this->initializeProxy();
800
+
801
+		return parent::reject($callback);
802
+	}
803
+
804
+	/**
805
+	 * Reverse items order.
806
+	 *
807
+	 * @return static
808
+	 */
809
+	public function reverse()
810
+	{
811
+		$this->initializeProxy();
812
+
813
+		return parent::reverse();
814
+	}
815
+
816
+	/**
817
+	 * Search the collection for a given value and return the corresponding key if successful.
818
+	 *
819
+	 * @param  mixed  $value
820
+	 * @param  bool   $strict
821
+	 * @return mixed
822
+	 */
823
+	public function search($value, $strict = false)
824
+	{
825
+		$this->initializeProxy();
826
+
827
+		return parent::search($value, $strict);
828
+	}
829
+
830
+	/**
831
+	 * Get and remove the first item from the collection.
832
+	 *
833
+	 * @return mixed
834
+	 */
835
+	public function shift()
836
+	{
837
+		// Todo : Partial Removing
838
+		// we could have a pending removal array
839
+		$this->initializeProxy();
840
+
841
+		return parent::shift();
842
+	}
843
+
844
+	/**
845
+	 * Shuffle the items in the collection.
846
+	 *
847
+	 * @param int $seed
848
+	 * @return static
849
+	 */
850
+	public function shuffle($seed = null)
851
+	{
852
+		$this->initializeProxy();
853
+
854
+		return parent::shuffle($seed);
855
+	}
856
+
857
+	/**
858
+	 * Slice the underlying collection array.
859
+	 *
860
+	 * @param  int   $offset
861
+	 * @param  int   $length
862
+	 * @return static
863
+	 */
864
+	public function slice($offset, $length = null)
865
+	{
866
+		$this->initializeProxy();
867
+
868
+		return parent::slice($offset, $length);
869
+	}
870
+
871
+	/**
872
+	 * Split a collection into a certain number of groups.
873
+	 *
874
+	 * @param  int  $numberOfGroups
875
+	 * @return static
876
+	 */
877
+	public function split($numberOfGroups)
878
+	{
879
+		$this->initializeProxy();
880
+
881
+		return parent::split($numberOfGroups);
882
+	}
883
+
884
+	/**
885
+	 * Chunk the underlying collection array.
886
+	 *
887
+	 * @param  int   $size
888
+	 * @return static
889
+	 */
890
+	public function chunk($size)
891
+	{
892
+		// TODO : partial loading ?
893
+		$this->initializeProxy();
894
+
895
+		return parent::chunk($size);
896
+	}
897
+
898
+	/**
899
+	 * Sort through each item with a callback.
900
+	 *
901
+	 * @param  callable|null  $callback
902
+	 * @return static
903
+	 */
904
+	public function sort(callable $callback = null)
905
+	{
906
+		$this->initializeProxy();
907
+
908
+		return parent::sort($callback);
909
+	}
910
+
911
+	/**
912
+	 * Sort the collection using the given callback.
913
+	 *
914
+	 * @param  callable|string  $callback
915
+	 * @param  int   $options
916
+	 * @param  bool  $descending
917
+	 * @return static
918
+	 */
919
+	public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
920
+	{
921
+		$this->initializeProxy();
922
+
923
+		return parent::sort($callback, $options, $descending);
924
+	}
925
+
926
+	/**
927
+	 * Splice a portion of the underlying collection array.
928
+	 *
929
+	 * @param  int  $offset
930
+	 * @param  int|null  $length
931
+	 * @param  mixed  $replacement
932
+	 * @return static
933
+	 */
934
+	public function splice($offset, $length = null, $replacement = [])
935
+	{
936
+		$this->initializeProxy();
937
+
938
+		return parent::splice($offset, $length, $replacement);
939
+	}
940
+
941
+	/**
942
+	 * Get the sum of the given values.
943
+	 *
944
+	 * @param  callable|string|null  $callback
945
+	 * @return mixed
946
+	 */
947
+	public function sum($callback = null)
948
+	{
949
+		$this->initializeProxy();
950
+
951
+		return parent::sum($callback);
952
+	}
953
+
954
+	/**
955
+	 * Take the first or last {$limit} items.
956
+	 *
957
+	 * @param  int  $limit
958
+	 * @return static
959
+	 */
960
+	public function take($limit)
961
+	{	
962
+		// TODO: partial loading
963
+		$this->initializeProxy();
964
+
965
+		return parent::take($limit);
966
+	}
967
+
968
+	/**
969
+	 * Transform each item in the collection using a callback.
970
+	 *
971
+	 * @param  callable  $callback
972
+	 * @return $this
973
+	 */
974
+	public function transform(callable $callback)
975
+	{
976
+		$this->initializeProxy();
977
+
978
+		return parent::transform($callback);
979
+	}
980
+
981
+	/**
982
+	 * Return only unique items from the collection array.
983
+	 *
984
+	 * @param  string|callable|null  $key
985
+	 * @param  bool  $strict
986
+	 *
987
+	 * @return static
988
+	 */
989
+	public function unique($key = null, $strict = false)
990
+	{
991
+		$this->initializeProxy();
992
+
993
+		return parent::unique($key, $strict);
994
+	}
995
+
996
+	/**
997
+	 * Reset the keys on the underlying array.
998
+	 *
999
+	 * @return static
1000
+	 */
1001
+	public function values()
1002
+	{
1003
+		$this->initializeProxy();
1004
+
1005
+		return parent::values();
1006
+	}
1007
+
1008
+	/**
1009
+	 * Zip the collection together with one or more arrays.
1010
+	 *
1011
+	 * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1012
+	 *      => [[1, 4], [2, 5], [3, 6]]
1013
+	 *
1014
+	 * @param  mixed ...$items
1015
+	 * @return static
1016
+	 */
1017
+	public function zip($items)
1018
+	{
1019
+		$this->initializeProxy();
1020
+
1021
+		return parent::zip($items);
1022
+	}
1023
+
1024
+	/**
1025
+	 * Get the collection of items as a plain array.
1026
+	 *
1027
+	 * @return array
1028
+	 */
1029
+	public function toArray()
1030
+	{
1031
+		// If this is called on all subsequent proxy, 
1032
+		// this would eventually trigger all lazy loading,
1033
+		// which is NOT what we would expect... 
1034
+		// TODO : must think of this. 
1035
+		$this->initializeProxy();
1036
+
1037
+		return parent::toArray();
1038
+	}
1039
+
1040
+	/**
1041
+	 * Convert the object into something JSON serializable.
1042
+	 *
1043
+	 * @return array
1044
+	 */
1045
+	public function jsonSerialize()
1046
+	{
1047
+		// If this is called on all subsequent proxy, 
1048
+		// this would eventually trigger all lazy loading,
1049
+		// which is NOT what we would expect... 
1050
+		// TODO : must think of this. 
1051
+		$this->initializeProxy();
1052
+
1053
+		return parent::jsonSerialize();
1054
+	}
1055
+
1056
+	/**
1057
+	 * Get the collection of items as JSON.
1058
+	 *
1059
+	 * @param  int  $options
1060
+	 * @return string
1061
+	 */
1062
+	public function toJson($options = 0)
1063
+	{
1064
+		// If this is called on all subsequent proxy, 
1065
+		// this would eventually trigger all lazy loading,
1066
+		// which is NOT what we would expect... 
1067
+		// TODO : must think of this. 
1068
+		$this->initializeProxy();
1069
+
1070
+		return parent::toJson($options);
1071
+	}
1072
+
1073
+	/**
1074
+	 * Get an iterator for the items.
1075
+	 *
1076
+	 * @return \ArrayIterator
1077
+	 */
1078
+	public function getIterator()
1079
+	{
1080
+		$this->initializeProxy();
1081
+
1082
+		return parent::getIterator();
1083
+	}
1084
+
1085
+	/**
1086
+	 * Get a CachingIterator instance.
1087
+	 *
1088
+	 * @param  int  $flags
1089
+	 * @return \CachingIterator
1090
+	 */
1091
+	public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1092
+	{
1093
+		$this->initializeProxy();
1094
+
1095
+		return parent::getCachingIterator($flags);
1096
+	}
1097
+
1098
+	/**
1099
+	 * Count the number of items in the collection.
1100
+	 *
1101
+	 * @return int
1102
+	 */
1103
+	public function count()
1104
+	{
1105
+		// TODO rely on QB if not initialized
1106
+		$this->initializeProxy();
1107 1107
         
1108
-        return parent::count();
1109
-    }
1110
-
1111
-    /**
1112
-     * Get a base Support collection instance from this collection.
1113
-     *
1114
-     * @return \Illuminate\Support\Collection
1115
-     */
1116
-    public function toBase()
1117
-    {
1118
-    	$this->initializeProxy();
1119
-
1120
-        return parent::toBase();
1121
-    }
1122
-
1123
-    /**
1124
-     * Determine if an item exists at an offset.
1125
-     *
1126
-     * @param  mixed  $key
1127
-     * @return bool
1128
-     */
1129
-    public function offsetExists($key)
1130
-    {
1131
-    	// TODO rely on QB if no collection
1132
-    	// initialized
1133
-    	$this->initializeProxy();
1134
-
1135
-        return parent::offsetExists($key);
1136
-    }
1137
-
1138
-    /**
1139
-     * Get an item at a given offset.
1140
-     *
1141
-     * @param  mixed  $key
1142
-     * @return mixed
1143
-     */
1144
-    public function offsetGet($key)
1145
-    {
1146
-    	// TODO rely on partial init if no collection
1147
-    	// initialized
1148
-    	$this->initializeProxy();
1149
-
1150
-        return parent::offsetGet($key);
1151
-    }
1152
-
1153
-    /**
1154
-     * Set the item at a given offset.
1155
-     *
1156
-     * @param  mixed  $key
1157
-     * @param  mixed  $value
1158
-     * @return void
1159
-     */
1160
-    public function offsetSet($key, $value)
1161
-    {
1162
-    	// TODO : think of the use of it into a ProxyCollection
1163
-    	// context
1164
-        $this->initializeProxy();
1165
-
1166
-        return parent::offsetSet($key, $value);
1167
-    }
1168
-
1169
-    /**
1170
-     * Unset the item at a given offset.
1171
-     *
1172
-     * @param  string  $key
1173
-     * @return void
1174
-     */
1175
-    public function offsetUnset($key)
1176
-    {
1177
-        // TODO : think of the use of it into a ProxyCollection
1178
-    	// context
1179
-        $this->initializeProxy();
1180
-
1181
-        return parent::offsetUnset($key);
1182
-    }
1183
-
1184
-    /**
1185
-     * Dynamically handle calls to the class.
1186
-     *
1187
-     * @param  string  $method
1188
-     * @param  array   $parameters
1189
-     * @return mixed
1190
-     *
1191
-     * @throws \BadMethodCallException
1192
-     */
1193
-    public function __call($method, $parameters)
1194
-    {
1195
-        $this->initializeProxy();
1196
-
1197
-        return parent::__call($method, $parameters);
1198
-    }
1108
+		return parent::count();
1109
+	}
1110
+
1111
+	/**
1112
+	 * Get a base Support collection instance from this collection.
1113
+	 *
1114
+	 * @return \Illuminate\Support\Collection
1115
+	 */
1116
+	public function toBase()
1117
+	{
1118
+		$this->initializeProxy();
1119
+
1120
+		return parent::toBase();
1121
+	}
1122
+
1123
+	/**
1124
+	 * Determine if an item exists at an offset.
1125
+	 *
1126
+	 * @param  mixed  $key
1127
+	 * @return bool
1128
+	 */
1129
+	public function offsetExists($key)
1130
+	{
1131
+		// TODO rely on QB if no collection
1132
+		// initialized
1133
+		$this->initializeProxy();
1134
+
1135
+		return parent::offsetExists($key);
1136
+	}
1137
+
1138
+	/**
1139
+	 * Get an item at a given offset.
1140
+	 *
1141
+	 * @param  mixed  $key
1142
+	 * @return mixed
1143
+	 */
1144
+	public function offsetGet($key)
1145
+	{
1146
+		// TODO rely on partial init if no collection
1147
+		// initialized
1148
+		$this->initializeProxy();
1149
+
1150
+		return parent::offsetGet($key);
1151
+	}
1152
+
1153
+	/**
1154
+	 * Set the item at a given offset.
1155
+	 *
1156
+	 * @param  mixed  $key
1157
+	 * @param  mixed  $value
1158
+	 * @return void
1159
+	 */
1160
+	public function offsetSet($key, $value)
1161
+	{
1162
+		// TODO : think of the use of it into a ProxyCollection
1163
+		// context
1164
+		$this->initializeProxy();
1165
+
1166
+		return parent::offsetSet($key, $value);
1167
+	}
1168
+
1169
+	/**
1170
+	 * Unset the item at a given offset.
1171
+	 *
1172
+	 * @param  string  $key
1173
+	 * @return void
1174
+	 */
1175
+	public function offsetUnset($key)
1176
+	{
1177
+		// TODO : think of the use of it into a ProxyCollection
1178
+		// context
1179
+		$this->initializeProxy();
1180
+
1181
+		return parent::offsetUnset($key);
1182
+	}
1183
+
1184
+	/**
1185
+	 * Dynamically handle calls to the class.
1186
+	 *
1187
+	 * @param  string  $method
1188
+	 * @param  array   $parameters
1189
+	 * @return mixed
1190
+	 *
1191
+	 * @throws \BadMethodCallException
1192
+	 */
1193
+	public function __call($method, $parameters)
1194
+	{
1195
+		$this->initializeProxy();
1196
+
1197
+		return parent::__call($method, $parameters);
1198
+	}
1199 1199
 
1200 1200
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -50,7 +50,7 @@
 block discarded – undo
50 50
      */
51 51
     public function initializeProxy() : bool
52 52
     {
53
-        if($this->isProxyInitialized() ) return true;
53
+        if ($this->isProxyInitialized()) return true;
54 54
         
55 55
         $relation = $this->relationshipMethod;
56 56
         $entity = $this->parentEntity;
Please login to merge, or discard this patch.
Braces   +3 added lines, -1 removed lines patch added patch discarded remove patch
@@ -50,7 +50,9 @@
 block discarded – undo
50 50
      */
51 51
     public function initializeProxy() : bool
52 52
     {
53
-        if($this->isProxyInitialized() ) return true;
53
+        if($this->isProxyInitialized() ) {
54
+        	return true;
55
+        }
54 56
         
55 57
         $relation = $this->relationshipMethod;
56 58
         $entity = $this->parentEntity;
Please login to merge, or discard this patch.
src/System/Query.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -179,7 +179,7 @@
 block discarded – undo
179 179
      * @param  mixed $id
180 180
      * @param  array $columns
181 181
      * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
182
-     * @return mixed|self
182
+     * @return \Analogue\ORM\Mappable
183 183
      */
184 184
     public function findOrFail($id, $columns = ['*'])
185 185
     {
Please login to merge, or discard this patch.
Indentation   +842 added lines, -842 removed lines patch added patch discarded remove patch
@@ -19,846 +19,846 @@
 block discarded – undo
19 19
  */
20 20
 class Query
21 21
 {
22
-    /**
23
-     * Mapper Instance
24
-     *
25
-     * @var \Analogue\ORM\System\Mapper
26
-     */
27
-    protected $mapper;
28
-
29
-    /**
30
-     * DB Adatper
31
-     *
32
-     * @var \Analogue\ORM\Drivers\DBAdapter
33
-     */
34
-    protected $adapter;
35
-
36
-    /**
37
-     * Query Builder Instance
38
-     *
39
-     * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
-     */
41
-    protected $query;
42
-
43
-    /**
44
-     * Entity Map Instance
45
-     *
46
-     * @var \Analogue\ORM\EntityMap
47
-     */
48
-    protected $entityMap;
49
-
50
-    /**
51
-     * The relationships that should be eager loaded.
52
-     *
53
-     * @var array
54
-     */
55
-    protected $eagerLoad = [];
56
-
57
-    /**
58
-     * All of the registered builder macros.
59
-     *
60
-     * @var array
61
-     */
62
-    protected $macros = [];
63
-
64
-    /**
65
-     * The methods that should be returned from query builder.
66
-     *
67
-     * @var array
68
-     */
69
-    protected $passthru = [
70
-        'toSql',
71
-        'lists',
72
-        'pluck',
73
-        'count',
74
-        'min',
75
-        'max',
76
-        'avg',
77
-        'sum',
78
-        'exists',
79
-        'getBindings',
80
-    ];
81
-
82
-    /**
83
-     * Query Builder Blacklist
84
-     */
85
-    protected $blacklist = [
86
-        'insert',
87
-        'insertGetId',
88
-        'lock',
89
-        'lockForUpdate',
90
-        'sharedLock',
91
-        'update',
92
-        'increment',
93
-        'decrement',
94
-        'delete',
95
-        'truncate',
96
-        'raw',
97
-    ];
98
-
99
-    /**
100
-     * Create a new Analogue Query Builder instance.
101
-     *
102
-     * @param  Mapper    $mapper
103
-     * @param  DBAdapter $adapter
104
-     */
105
-    public function __construct(Mapper $mapper, DBAdapter $adapter)
106
-    {
107
-        $this->mapper = $mapper;
108
-
109
-        $this->adapter = $adapter;
110
-
111
-        $this->entityMap = $mapper->getEntityMap();
112
-
113
-        // Specify the table to work on
114
-        $this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
-
116
-        $this->with($this->entityMap->getEagerloadedRelationships());
117
-    }
118
-
119
-    /**
120
-     * Run the query and return the result
121
-     *
122
-     * @param  array $columns
123
-     * @return \Analogue\ORM\EntityCollection
124
-     */
125
-    public function get($columns = ['*'])
126
-    {
127
-        $entities = $this->getEntities($columns);
128
-
129
-        // If we actually found models we will also eager load any relationships that
130
-        // have been specified as needing to be eager loaded, which will solve the
131
-        // n+1 query issue for the developers to avoid running a lot of queries.
132
-
133
-        if (count($entities) > 0) {
134
-            $entities = $this->eagerLoadRelations($entities);
135
-        }
136
-
137
-        return $this->entityMap->newCollection($entities);
138
-    }
139
-
140
-    /**
141
-     * Find an entity by its primary key
142
-     *
143
-     * @param  string|integer $id
144
-     * @param  array          $columns
145
-     * @return \Analogue\ORM\Mappable
146
-     */
147
-    public function find($id, $columns = ['*'])
148
-    {
149
-        if (is_array($id)) {
150
-            return $this->findMany($id, $columns);
151
-        }
152
-
153
-        $this->query->where($this->entityMap->getKeyName(), '=', $id);
154
-
155
-        return $this->first($columns);
156
-    }
157
-
158
-    /**
159
-     * Find many entities by their primary keys.
160
-     *
161
-     * @param  array $id
162
-     * @param  array $columns
163
-     * @return EntityCollection
164
-     */
165
-    public function findMany($id, $columns = ['*'])
166
-    {
167
-        if (empty($id)) {
168
-            return new EntityCollection;
169
-        }
170
-
171
-        $this->query->whereIn($this->entityMap->getKeyName(), $id);
172
-
173
-        return $this->get($columns);
174
-    }
175
-
176
-    /**
177
-     * Find a model by its primary key or throw an exception.
178
-     *
179
-     * @param  mixed $id
180
-     * @param  array $columns
181
-     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
182
-     * @return mixed|self
183
-     */
184
-    public function findOrFail($id, $columns = ['*'])
185
-    {
186
-        if (!is_null($entity = $this->find($id, $columns))) {
187
-            return $entity;
188
-        }
189
-
190
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
191
-    }
192
-
193
-
194
-    /**
195
-     * Execute the query and get the first result.
196
-     *
197
-     * @param  array $columns
198
-     * @return \Analogue\ORM\Entity
199
-     */
200
-    public function first($columns = ['*'])
201
-    {
202
-        return $this->take(1)->get($columns)->first();
203
-    }
204
-
205
-    /**
206
-     * Execute the query and get the first result or throw an exception.
207
-     *
208
-     * @param  array $columns
209
-     * @throws EntityNotFoundException
210
-     * @return \Analogue\ORM\Entity
211
-     */
212
-    public function firstOrFail($columns = ['*'])
213
-    {
214
-        if (!is_null($entity = $this->first($columns))) {
215
-            return $entity;
216
-        }
217
-
218
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
219
-    }
220
-
221
-    /**
222
-     * Pluck a single column from the database.
223
-     *
224
-     * @param  string $column
225
-     * @return mixed
226
-     */
227
-    public function pluck($column)
228
-    {
229
-        $result = $this->first([$column]);
230
-
231
-        if ($result) {
232
-            return $result->{$column};
233
-        }
234
-    }
235
-
236
-    /**
237
-     * Chunk the results of the query.
238
-     *
239
-     * @param  int      $count
240
-     * @param  callable $callback
241
-     * @return void
242
-     */
243
-    public function chunk($count, callable $callback)
244
-    {
245
-        $results = $this->forPage($page = 1, $count)->get();
246
-
247
-        while (count($results) > 0) {
248
-            // On each chunk result set, we will pass them to the callback and then let the
249
-            // developer take care of everything within the callback, which allows us to
250
-            // keep the memory low for spinning through large result sets for working.
251
-            call_user_func($callback, $results);
252
-
253
-            $page++;
254
-
255
-            $results = $this->forPage($page, $count)->get();
256
-        }
257
-    }
258
-
259
-    /**
260
-     * Get an array with the values of a given column.
261
-     *
262
-     * @param  string $column
263
-     * @param  string $key
264
-     * @return array
265
-     */
266
-    public function lists($column, $key = null)
267
-    {
268
-        return $this->query->pluck($column, $key);
269
-    }
270
-
271
-    /**
272
-     * Get a paginator for the "select" statement.
273
-     *
274
-     * @param  int   $perPage
275
-     * @param  array $columns
276
-     * @return LengthAwarePaginator
277
-     */
278
-    public function paginate($perPage = null, $columns = ['*'])
279
-    {
280
-        $total = $this->query->getCountForPagination();
281
-
282
-        $this->query->forPage(
283
-            $page = Paginator::resolveCurrentPage(),
284
-            $perPage = $perPage ?: $this->entityMap->getPerPage()
285
-        );
286
-
287
-        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
288
-            'path' => Paginator::resolveCurrentPath()
289
-        ]);
290
-    }
291
-
292
-    /**
293
-     * Get a paginator for a grouped statement.
294
-     *
295
-     * @param  \Illuminate\Pagination\Factory $paginator
296
-     * @param  int                            $perPage
297
-     * @param  array                          $columns
298
-     * @return \Illuminate\Pagination\Paginator
299
-     */
300
-    protected function groupedPaginate($paginator, $perPage, $columns)
301
-    {
302
-        $results = $this->get($columns)->all();
303
-
304
-        return $this->query->buildRawPaginator($paginator, $results, $perPage);
305
-    }
306
-
307
-    /**
308
-     * Get a paginator for an ungrouped statement.
309
-     *
310
-     * @param  \Illuminate\Pagination\Factory $paginator
311
-     * @param  int                            $perPage
312
-     * @param  array                          $columns
313
-     * @return \Illuminate\Pagination\Paginator
314
-     */
315
-    protected function ungroupedPaginate($paginator, $perPage, $columns)
316
-    {
317
-        $total = $this->query->getPaginationCount();
318
-
319
-        // Once we have the paginator we need to set the limit and offset values for
320
-        // the query so we can get the properly paginated items. Once we have an
321
-        // array of items we can create the paginator instances for the items.
322
-        $page = $paginator->getCurrentPage($total);
323
-
324
-        $this->query->forPage($page, $perPage);
325
-
326
-        return $paginator->make($this->get($columns)->all(), $total, $perPage);
327
-    }
328
-
329
-    /**
330
-     * Paginate the given query into a simple paginator.
331
-     *
332
-     * @param  int   $perPage
333
-     * @param  array $columns
334
-     * @return \Illuminate\Contracts\Pagination\Paginator
335
-     */
336
-    public function simplePaginate($perPage = null, $columns = ['*'])
337
-    {
338
-        $page = Paginator::resolveCurrentPage();
339
-
340
-        $perPage = $perPage ?: $this->entityMap->getPerPage();
341
-
342
-        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
343
-
344
-        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
345
-    }
346
-
347
-    /**
348
-     * Add a basic where clause to the query.
349
-     *
350
-     * @param  string $column
351
-     * @param  string $operator
352
-     * @param  mixed  $value
353
-     * @param  string $boolean
354
-     * @return $this
355
-     */
356
-    public function where($column, $operator = null, $value = null, $boolean = 'and')
357
-    {
358
-        if ($column instanceof Closure) {
359
-            $query = $this->newQueryWithoutScopes();
360
-
361
-            call_user_func($column, $query);
362
-
363
-            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
364
-        } else {
365
-            call_user_func_array([$this->query, 'where'], func_get_args());
366
-        }
367
-
368
-        return $this;
369
-    }
370
-
371
-    /**
372
-     * Add an "or where" clause to the query.
373
-     *
374
-     * @param  string $column
375
-     * @param  string $operator
376
-     * @param  mixed  $value
377
-     * @return \Analogue\ORM\System\Query
378
-     */
379
-    public function orWhere($column, $operator = null, $value = null)
380
-    {
381
-        return $this->where($column, $operator, $value, 'or');
382
-    }
383
-
384
-    /**
385
-     * Add a relationship count condition to the query.
386
-     *
387
-     * @param  string   $relation
388
-     * @param  string   $operator
389
-     * @param  int      $count
390
-     * @param  string   $boolean
391
-     * @param  \Closure $callback
392
-     * @return \Analogue\ORM\System\Query
393
-     */
394
-    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
395
-    {
396
-        $entity = $this->mapper->newInstance();
397
-
398
-        $relation = $this->getHasRelationQuery($relation, $entity);
399
-
400
-        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
401
-
402
-        if ($callback) {
403
-            call_user_func($callback, $query);
404
-        }
405
-
406
-        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
407
-    }
408
-
409
-    /**
410
-     * Add a relationship count condition to the query with where clauses.
411
-     *
412
-     * @param  string   $relation
413
-     * @param  \Closure $callback
414
-     * @param  string   $operator
415
-     * @param  int      $count
416
-     * @return \Analogue\ORM\System\Query
417
-     */
418
-    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
419
-    {
420
-        return $this->has($relation, $operator, $count, 'and', $callback);
421
-    }
422
-
423
-    /**
424
-     * Add a relationship count condition to the query with an "or".
425
-     *
426
-     * @param  string $relation
427
-     * @param  string $operator
428
-     * @param  int    $count
429
-     * @return \Analogue\ORM\System\Query
430
-     */
431
-    public function orHas($relation, $operator = '>=', $count = 1)
432
-    {
433
-        return $this->has($relation, $operator, $count, 'or');
434
-    }
435
-
436
-    /**
437
-     * Add a relationship count condition to the query with where clauses and an "or".
438
-     *
439
-     * @param  string   $relation
440
-     * @param  \Closure $callback
441
-     * @param  string   $operator
442
-     * @param  int      $count
443
-     * @return \Analogue\ORM\System\Query
444
-     */
445
-    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
446
-    {
447
-        return $this->has($relation, $operator, $count, 'or', $callback);
448
-    }
449
-
450
-    /**
451
-     * Add the "has" condition where clause to the query.
452
-     *
453
-     * @param  \Analogue\ORM\System\Query               $hasQuery
454
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
455
-     * @param  string                                   $operator
456
-     * @param  int                                      $count
457
-     * @param  string                                   $boolean
458
-     * @return \Analogue\ORM\System\Query
459
-     */
460
-    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
461
-    {
462
-        $this->mergeWheresToHas($hasQuery, $relation);
463
-
464
-        if (is_numeric($count)) {
465
-            $count = new Expression($count);
466
-        }
467
-
468
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
469
-    }
470
-
471
-    /**
472
-     * Merge the "wheres" from a relation query to a has query.
473
-     *
474
-     * @param  \Analogue\ORM\System\Query               $hasQuery
475
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
476
-     * @return void
477
-     */
478
-    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
479
-    {
480
-        // Here we have the "has" query and the original relation. We need to copy over any
481
-        // where clauses the developer may have put in the relationship function over to
482
-        // the has query, and then copy the bindings from the "has" query to the main.
483
-        $relationQuery = $relation->getBaseQuery();
484
-
485
-        $hasQuery->mergeWheres(
486
-            $relationQuery->wheres, $relationQuery->getBindings()
487
-        );
488
-
489
-        $this->query->mergeBindings($hasQuery->getQuery());
490
-    }
491
-
492
-    /**
493
-     * Get the "has relation" base query instance.
494
-     *
495
-     * @param  string $relation
496
-     * @param         $entity
497
-     * @return \Analogue\ORM\System\Query
498
-     */
499
-    protected function getHasRelationQuery($relation, $entity)
500
-    {
501
-        return Relationship::noConstraints(function () use ($relation, $entity) {
502
-            return $this->entityMap->$relation($entity);
503
-        });
504
-    }
505
-
506
-    /**
507
-     * Get the table for the current query object
508
-     *
509
-     * @return string
510
-     */
511
-    public function getTable()
512
-    {
513
-        return $this->entityMap->getTable();
514
-    }
515
-
516
-    /**
517
-     * Set the relationships that should be eager loaded.
518
-     *
519
-     * @param  mixed $relations
520
-     * @return $this
521
-     */
522
-    public function with($relations)
523
-    {
524
-        if (is_string($relations)) {
525
-            $relations = func_get_args();
526
-        }
527
-
528
-        $eagers = $this->parseRelations($relations);
529
-
530
-        $this->eagerLoad = array_merge($this->eagerLoad, $eagers);
531
-
532
-        return $this;
533
-    }
534
-
535
-    /**
536
-     * Parse a list of relations into individuals.
537
-     *
538
-     * @param  array $relations
539
-     * @return array
540
-     */
541
-    protected function parseRelations(array $relations)
542
-    {
543
-        $results = [];
544
-
545
-        foreach ($relations as $name => $constraints) {
546
-            // If the "relation" value is actually a numeric key, we can assume that no
547
-            // constraints have been specified for the eager load and we'll just put
548
-            // an empty Closure with the loader so that we can treat all the same.
549
-            if (is_numeric($name)) {
550
-                $f = function () {};
551
-
552
-                list($name, $constraints) = [$constraints, $f];
553
-            }
554
-
555
-            // We need to separate out any nested includes. Which allows the developers
556
-            // to load deep relationships using "dots" without stating each level of
557
-            // the relationship with its own key in the array of eager load names.
558
-            $results = $this->parseNested($name, $results);
559
-
560
-            $results[$name] = $constraints;
561
-        }
562
-
563
-        return $results;
564
-    }
565
-
566
-
567
-    /**
568
-     * Parse the nested relationships in a relation.
569
-     *
570
-     * @param  string $name
571
-     * @param  array  $results
572
-     * @return array
573
-     */
574
-    protected function parseNested($name, $results)
575
-    {
576
-        $progress = [];
577
-
578
-        // If the relation has already been set on the result array, we will not set it
579
-        // again, since that would override any constraints that were already placed
580
-        // on the relationships. We will only set the ones that are not specified.
581
-        foreach (explode('.', $name) as $segment) {
582
-            $progress[] = $segment;
583
-
584
-            if (!isset($results[$last = implode('.', $progress)])) {
585
-                $results[$last] = function () {};
586
-            }
587
-        }
588
-
589
-        return $results;
590
-    }
591
-
592
-    /**
593
-     * Get the relationships being eagerly loaded.
594
-     *
595
-     * @return array
596
-     */
597
-    public function getEagerLoads()
598
-    {
599
-        return $this->eagerLoad;
600
-    }
601
-
602
-    /**
603
-     * Set the relationships being eagerly loaded.
604
-     *
605
-     * @param  array $eagerLoad
606
-     * @return void
607
-     */
608
-    public function setEagerLoads(array $eagerLoad)
609
-    {
610
-        $this->eagerLoad = $eagerLoad;
611
-    }
612
-
613
-    /**
614
-     * Eager load the relationships for the entities.
615
-     *
616
-     * @param  array $entities
617
-     * @return array
618
-     */
619
-    public function eagerLoadRelations($entities)
620
-    {
621
-        foreach ($this->eagerLoad as $name => $constraints) {
622
-            // For nested eager loads we'll skip loading them here and they will be set as an
623
-            // eager load on the query to retrieve the relation so that they will be eager
624
-            // loaded on that query, because that is where they get hydrated as models.
625
-            if (strpos($name, '.') === false) {
626
-                $entities = $this->loadRelation($entities, $name, $constraints);
627
-            }
628
-        }
629
-
630
-        return $entities;
631
-    }
632
-
633
-    /**
634
-     * Eagerly load the relationship on a set of entities.
635
-     *
636
-     * @param  array    $entities
637
-     * @param  string   $name
638
-     * @param  \Closure $constraints
639
-     * @return array
640
-     */
641
-    protected function loadRelation(array $entities, $name, Closure $constraints)
642
-    {
643
-        // First we will "back up" the existing where conditions on the query so we can
644
-        // add our eager constraints. Then we will merge the wheres that were on the
645
-        // query back to it in order that any where conditions might be specified.
646
-        $relation = $this->getRelation($name);
647
-
648
-        $relation->addEagerConstraints($entities);
649
-
650
-        call_user_func($constraints, $relation);
651
-
652
-        $entities = $relation->initRelation($entities, $name);
653
-
654
-        // Once we have the results, we just match those back up to their parent models
655
-        // using the relationship instance. Then we just return the finished arrays
656
-        // of models which have been eagerly hydrated and are readied for return.
657
-
658
-        $results = $relation->getEager();
659
-
660
-        return $relation->match($entities, $results, $name);
661
-    }
662
-
663
-    /**
664
-     * Get the relation instance for the given relation name.
665
-     *
666
-     * @param  string $relation
667
-     * @return \Analogue\ORM\Relationships\Relationship
668
-     */
669
-    public function getRelation($relation)
670
-    {
671
-        // We want to run a relationship query without any constrains so that we will
672
-        // not have to remove these where clauses manually which gets really hacky
673
-        // and is error prone while we remove the developer's own where clauses.
674
-        $query = Relationship::noConstraints(function () use ($relation) {
675
-            return $this->entityMap->$relation($this->getEntityInstance());
676
-        });
677
-
678
-        $nested = $this->nestedRelations($relation);
679
-
680
-        // If there are nested relationships set on the query, we will put those onto
681
-        // the query instances so that they can be handled after this relationship
682
-        // is loaded. In this way they will all trickle down as they are loaded.
683
-        if (count($nested) > 0) {
684
-            $query->getQuery()->with($nested);
685
-        }
686
-
687
-        return $query;
688
-    }
689
-
690
-    /**
691
-     * Get the deeply nested relations for a given top-level relation.
692
-     *
693
-     * @param  string $relation
694
-     * @return array
695
-     */
696
-    protected function nestedRelations($relation)
697
-    {
698
-        $nested = [];
699
-
700
-        // We are basically looking for any relationships that are nested deeper than
701
-        // the given top-level relationship. We will just check for any relations
702
-        // that start with the given top relations and adds them to our arrays.
703
-        foreach ($this->eagerLoad as $name => $constraints) {
704
-            if ($this->isNested($name, $relation)) {
705
-                $nested[substr($name, strlen($relation . '.'))] = $constraints;
706
-            }
707
-        }
708
-
709
-        return $nested;
710
-    }
711
-
712
-    /**
713
-     * Determine if the relationship is nested.
714
-     *
715
-     * @param  string $name
716
-     * @param  string $relation
717
-     * @return bool
718
-     */
719
-    protected function isNested($name, $relation)
720
-    {
721
-        $dots = str_contains($name, '.');
722
-
723
-        return $dots && starts_with($name, $relation . '.');
724
-    }
725
-
726
-    /**
727
-     * Add the Entity primary key if not in requested columns
728
-     *
729
-     * @param  array $columns
730
-     * @return array
731
-     */
732
-    protected function enforceIdColumn($columns)
733
-    {
734
-        if (!in_array($this->entityMap->getKeyName(), $columns)) {
735
-            $columns[] = $this->entityMap->getKeyName();
736
-        }
737
-        return $columns;
738
-    }
739
-
740
-    /**
741
-     * Get the hydrated models without eager loading.
742
-     *
743
-     * @param  array  $columns
744
-     * @return \Analogue\ORM\EntityCollection
745
-     */
746
-    public function getEntities($columns = ['*'])
747
-    {
748
-        // As we need the primary key to feed the
749
-        // entity cache, we need it loaded on each
750
-        // request
751
-        $columns = $this->enforceIdColumn($columns);
752
-
753
-        // Run the query
754
-        $results = $this->query->get($columns)->toArray();
755
-
756
-        // Create a result builder.
757
-        $builder = new ResultBuilder(Manager::getInstance(), $this->mapper, array_keys($this->getEagerLoads()));
758
-
759
-        return $builder->build($results);
760
-    }
761
-
762
-    /**
763
-     * Get a new instance for the entity
764
-     *
765
-     * @return \Analogue\ORM\Entity
766
-     */
767
-    public function getEntityInstance()
768
-    {
769
-        return $this->mapper->newInstance();
770
-    }
771
-
772
-    /**
773
-     * Extend the builder with a given callback.
774
-     *
775
-     * @param  string   $name
776
-     * @param  \Closure $callback
777
-     * @return void
778
-     */
779
-    public function macro($name, Closure $callback)
780
-    {
781
-        $this->macros[$name] = $callback;
782
-    }
783
-
784
-    /**
785
-     * Get the given macro by name.
786
-     *
787
-     * @param  string $name
788
-     * @return \Closure
789
-     */
790
-    public function getMacro($name)
791
-    {
792
-        return array_get($this->macros, $name);
793
-    }
794
-
795
-    /**
796
-     * Get a new query builder for the model's table.
797
-     *
798
-     * @return \Analogue\ORM\System\Query
799
-     */
800
-    public function newQuery()
801
-    {
802
-        $builder = new Query($this->mapper, $this->adapter);
803
-
804
-        return $this->applyGlobalScopes($builder);
805
-    }
806
-
807
-    /**
808
-     * Get a new query builder without any scope applied.
809
-     *
810
-     * @return \Analogue\ORM\System\Query
811
-     */
812
-    public function newQueryWithoutScopes()
813
-    {
814
-        return new Query($this->mapper, $this->adapter);
815
-    }
816
-
817
-    /**
818
-     * Get the Mapper instance for this Query Builder
819
-     *
820
-     * @return \Analogue\ORM\System\Mapper
821
-     */
822
-    public function getMapper()
823
-    {
824
-        return $this->mapper;
825
-    }
826
-
827
-    /**
828
-     * Get the underlying query adapter
829
-     *
830
-     * (REFACTOR: this method should move out, we need to provide the client classes
831
-     * with the adapter instead.)
832
-     *
833
-     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
834
-     */
835
-    public function getQuery()
836
-    {
837
-        return $this->query;
838
-    }
839
-
840
-    /**
841
-     * Dynamically handle calls into the query instance.
842
-     *
843
-     * @param  string $method
844
-     * @param  array  $parameters
845
-     * @throws Exception
846
-     * @return mixed
847
-     */
848
-    public function __call($method, $parameters)
849
-    {
850
-        if (isset($this->macros[$method])) {
851
-            array_unshift($parameters, $this);
852
-
853
-            return call_user_func_array($this->macros[$method], $parameters);
854
-        }
855
-
856
-        if (in_array($method, $this->blacklist)) {
857
-            throw new Exception("Method $method doesn't exist");
858
-        }
859
-
860
-        $result = call_user_func_array([$this->query, $method], $parameters);
861
-
862
-        return in_array($method, $this->passthru) ? $result : $this;
863
-    }
22
+	/**
23
+	 * Mapper Instance
24
+	 *
25
+	 * @var \Analogue\ORM\System\Mapper
26
+	 */
27
+	protected $mapper;
28
+
29
+	/**
30
+	 * DB Adatper
31
+	 *
32
+	 * @var \Analogue\ORM\Drivers\DBAdapter
33
+	 */
34
+	protected $adapter;
35
+
36
+	/**
37
+	 * Query Builder Instance
38
+	 *
39
+	 * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
+	 */
41
+	protected $query;
42
+
43
+	/**
44
+	 * Entity Map Instance
45
+	 *
46
+	 * @var \Analogue\ORM\EntityMap
47
+	 */
48
+	protected $entityMap;
49
+
50
+	/**
51
+	 * The relationships that should be eager loaded.
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $eagerLoad = [];
56
+
57
+	/**
58
+	 * All of the registered builder macros.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $macros = [];
63
+
64
+	/**
65
+	 * The methods that should be returned from query builder.
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $passthru = [
70
+		'toSql',
71
+		'lists',
72
+		'pluck',
73
+		'count',
74
+		'min',
75
+		'max',
76
+		'avg',
77
+		'sum',
78
+		'exists',
79
+		'getBindings',
80
+	];
81
+
82
+	/**
83
+	 * Query Builder Blacklist
84
+	 */
85
+	protected $blacklist = [
86
+		'insert',
87
+		'insertGetId',
88
+		'lock',
89
+		'lockForUpdate',
90
+		'sharedLock',
91
+		'update',
92
+		'increment',
93
+		'decrement',
94
+		'delete',
95
+		'truncate',
96
+		'raw',
97
+	];
98
+
99
+	/**
100
+	 * Create a new Analogue Query Builder instance.
101
+	 *
102
+	 * @param  Mapper    $mapper
103
+	 * @param  DBAdapter $adapter
104
+	 */
105
+	public function __construct(Mapper $mapper, DBAdapter $adapter)
106
+	{
107
+		$this->mapper = $mapper;
108
+
109
+		$this->adapter = $adapter;
110
+
111
+		$this->entityMap = $mapper->getEntityMap();
112
+
113
+		// Specify the table to work on
114
+		$this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
+
116
+		$this->with($this->entityMap->getEagerloadedRelationships());
117
+	}
118
+
119
+	/**
120
+	 * Run the query and return the result
121
+	 *
122
+	 * @param  array $columns
123
+	 * @return \Analogue\ORM\EntityCollection
124
+	 */
125
+	public function get($columns = ['*'])
126
+	{
127
+		$entities = $this->getEntities($columns);
128
+
129
+		// If we actually found models we will also eager load any relationships that
130
+		// have been specified as needing to be eager loaded, which will solve the
131
+		// n+1 query issue for the developers to avoid running a lot of queries.
132
+
133
+		if (count($entities) > 0) {
134
+			$entities = $this->eagerLoadRelations($entities);
135
+		}
136
+
137
+		return $this->entityMap->newCollection($entities);
138
+	}
139
+
140
+	/**
141
+	 * Find an entity by its primary key
142
+	 *
143
+	 * @param  string|integer $id
144
+	 * @param  array          $columns
145
+	 * @return \Analogue\ORM\Mappable
146
+	 */
147
+	public function find($id, $columns = ['*'])
148
+	{
149
+		if (is_array($id)) {
150
+			return $this->findMany($id, $columns);
151
+		}
152
+
153
+		$this->query->where($this->entityMap->getKeyName(), '=', $id);
154
+
155
+		return $this->first($columns);
156
+	}
157
+
158
+	/**
159
+	 * Find many entities by their primary keys.
160
+	 *
161
+	 * @param  array $id
162
+	 * @param  array $columns
163
+	 * @return EntityCollection
164
+	 */
165
+	public function findMany($id, $columns = ['*'])
166
+	{
167
+		if (empty($id)) {
168
+			return new EntityCollection;
169
+		}
170
+
171
+		$this->query->whereIn($this->entityMap->getKeyName(), $id);
172
+
173
+		return $this->get($columns);
174
+	}
175
+
176
+	/**
177
+	 * Find a model by its primary key or throw an exception.
178
+	 *
179
+	 * @param  mixed $id
180
+	 * @param  array $columns
181
+	 * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
182
+	 * @return mixed|self
183
+	 */
184
+	public function findOrFail($id, $columns = ['*'])
185
+	{
186
+		if (!is_null($entity = $this->find($id, $columns))) {
187
+			return $entity;
188
+		}
189
+
190
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
191
+	}
192
+
193
+
194
+	/**
195
+	 * Execute the query and get the first result.
196
+	 *
197
+	 * @param  array $columns
198
+	 * @return \Analogue\ORM\Entity
199
+	 */
200
+	public function first($columns = ['*'])
201
+	{
202
+		return $this->take(1)->get($columns)->first();
203
+	}
204
+
205
+	/**
206
+	 * Execute the query and get the first result or throw an exception.
207
+	 *
208
+	 * @param  array $columns
209
+	 * @throws EntityNotFoundException
210
+	 * @return \Analogue\ORM\Entity
211
+	 */
212
+	public function firstOrFail($columns = ['*'])
213
+	{
214
+		if (!is_null($entity = $this->first($columns))) {
215
+			return $entity;
216
+		}
217
+
218
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
219
+	}
220
+
221
+	/**
222
+	 * Pluck a single column from the database.
223
+	 *
224
+	 * @param  string $column
225
+	 * @return mixed
226
+	 */
227
+	public function pluck($column)
228
+	{
229
+		$result = $this->first([$column]);
230
+
231
+		if ($result) {
232
+			return $result->{$column};
233
+		}
234
+	}
235
+
236
+	/**
237
+	 * Chunk the results of the query.
238
+	 *
239
+	 * @param  int      $count
240
+	 * @param  callable $callback
241
+	 * @return void
242
+	 */
243
+	public function chunk($count, callable $callback)
244
+	{
245
+		$results = $this->forPage($page = 1, $count)->get();
246
+
247
+		while (count($results) > 0) {
248
+			// On each chunk result set, we will pass them to the callback and then let the
249
+			// developer take care of everything within the callback, which allows us to
250
+			// keep the memory low for spinning through large result sets for working.
251
+			call_user_func($callback, $results);
252
+
253
+			$page++;
254
+
255
+			$results = $this->forPage($page, $count)->get();
256
+		}
257
+	}
258
+
259
+	/**
260
+	 * Get an array with the values of a given column.
261
+	 *
262
+	 * @param  string $column
263
+	 * @param  string $key
264
+	 * @return array
265
+	 */
266
+	public function lists($column, $key = null)
267
+	{
268
+		return $this->query->pluck($column, $key);
269
+	}
270
+
271
+	/**
272
+	 * Get a paginator for the "select" statement.
273
+	 *
274
+	 * @param  int   $perPage
275
+	 * @param  array $columns
276
+	 * @return LengthAwarePaginator
277
+	 */
278
+	public function paginate($perPage = null, $columns = ['*'])
279
+	{
280
+		$total = $this->query->getCountForPagination();
281
+
282
+		$this->query->forPage(
283
+			$page = Paginator::resolveCurrentPage(),
284
+			$perPage = $perPage ?: $this->entityMap->getPerPage()
285
+		);
286
+
287
+		return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
288
+			'path' => Paginator::resolveCurrentPath()
289
+		]);
290
+	}
291
+
292
+	/**
293
+	 * Get a paginator for a grouped statement.
294
+	 *
295
+	 * @param  \Illuminate\Pagination\Factory $paginator
296
+	 * @param  int                            $perPage
297
+	 * @param  array                          $columns
298
+	 * @return \Illuminate\Pagination\Paginator
299
+	 */
300
+	protected function groupedPaginate($paginator, $perPage, $columns)
301
+	{
302
+		$results = $this->get($columns)->all();
303
+
304
+		return $this->query->buildRawPaginator($paginator, $results, $perPage);
305
+	}
306
+
307
+	/**
308
+	 * Get a paginator for an ungrouped statement.
309
+	 *
310
+	 * @param  \Illuminate\Pagination\Factory $paginator
311
+	 * @param  int                            $perPage
312
+	 * @param  array                          $columns
313
+	 * @return \Illuminate\Pagination\Paginator
314
+	 */
315
+	protected function ungroupedPaginate($paginator, $perPage, $columns)
316
+	{
317
+		$total = $this->query->getPaginationCount();
318
+
319
+		// Once we have the paginator we need to set the limit and offset values for
320
+		// the query so we can get the properly paginated items. Once we have an
321
+		// array of items we can create the paginator instances for the items.
322
+		$page = $paginator->getCurrentPage($total);
323
+
324
+		$this->query->forPage($page, $perPage);
325
+
326
+		return $paginator->make($this->get($columns)->all(), $total, $perPage);
327
+	}
328
+
329
+	/**
330
+	 * Paginate the given query into a simple paginator.
331
+	 *
332
+	 * @param  int   $perPage
333
+	 * @param  array $columns
334
+	 * @return \Illuminate\Contracts\Pagination\Paginator
335
+	 */
336
+	public function simplePaginate($perPage = null, $columns = ['*'])
337
+	{
338
+		$page = Paginator::resolveCurrentPage();
339
+
340
+		$perPage = $perPage ?: $this->entityMap->getPerPage();
341
+
342
+		$this->skip(($page - 1) * $perPage)->take($perPage + 1);
343
+
344
+		return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
345
+	}
346
+
347
+	/**
348
+	 * Add a basic where clause to the query.
349
+	 *
350
+	 * @param  string $column
351
+	 * @param  string $operator
352
+	 * @param  mixed  $value
353
+	 * @param  string $boolean
354
+	 * @return $this
355
+	 */
356
+	public function where($column, $operator = null, $value = null, $boolean = 'and')
357
+	{
358
+		if ($column instanceof Closure) {
359
+			$query = $this->newQueryWithoutScopes();
360
+
361
+			call_user_func($column, $query);
362
+
363
+			$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
364
+		} else {
365
+			call_user_func_array([$this->query, 'where'], func_get_args());
366
+		}
367
+
368
+		return $this;
369
+	}
370
+
371
+	/**
372
+	 * Add an "or where" clause to the query.
373
+	 *
374
+	 * @param  string $column
375
+	 * @param  string $operator
376
+	 * @param  mixed  $value
377
+	 * @return \Analogue\ORM\System\Query
378
+	 */
379
+	public function orWhere($column, $operator = null, $value = null)
380
+	{
381
+		return $this->where($column, $operator, $value, 'or');
382
+	}
383
+
384
+	/**
385
+	 * Add a relationship count condition to the query.
386
+	 *
387
+	 * @param  string   $relation
388
+	 * @param  string   $operator
389
+	 * @param  int      $count
390
+	 * @param  string   $boolean
391
+	 * @param  \Closure $callback
392
+	 * @return \Analogue\ORM\System\Query
393
+	 */
394
+	public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
395
+	{
396
+		$entity = $this->mapper->newInstance();
397
+
398
+		$relation = $this->getHasRelationQuery($relation, $entity);
399
+
400
+		$query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
401
+
402
+		if ($callback) {
403
+			call_user_func($callback, $query);
404
+		}
405
+
406
+		return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
407
+	}
408
+
409
+	/**
410
+	 * Add a relationship count condition to the query with where clauses.
411
+	 *
412
+	 * @param  string   $relation
413
+	 * @param  \Closure $callback
414
+	 * @param  string   $operator
415
+	 * @param  int      $count
416
+	 * @return \Analogue\ORM\System\Query
417
+	 */
418
+	public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
419
+	{
420
+		return $this->has($relation, $operator, $count, 'and', $callback);
421
+	}
422
+
423
+	/**
424
+	 * Add a relationship count condition to the query with an "or".
425
+	 *
426
+	 * @param  string $relation
427
+	 * @param  string $operator
428
+	 * @param  int    $count
429
+	 * @return \Analogue\ORM\System\Query
430
+	 */
431
+	public function orHas($relation, $operator = '>=', $count = 1)
432
+	{
433
+		return $this->has($relation, $operator, $count, 'or');
434
+	}
435
+
436
+	/**
437
+	 * Add a relationship count condition to the query with where clauses and an "or".
438
+	 *
439
+	 * @param  string   $relation
440
+	 * @param  \Closure $callback
441
+	 * @param  string   $operator
442
+	 * @param  int      $count
443
+	 * @return \Analogue\ORM\System\Query
444
+	 */
445
+	public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
446
+	{
447
+		return $this->has($relation, $operator, $count, 'or', $callback);
448
+	}
449
+
450
+	/**
451
+	 * Add the "has" condition where clause to the query.
452
+	 *
453
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
454
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
455
+	 * @param  string                                   $operator
456
+	 * @param  int                                      $count
457
+	 * @param  string                                   $boolean
458
+	 * @return \Analogue\ORM\System\Query
459
+	 */
460
+	protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
461
+	{
462
+		$this->mergeWheresToHas($hasQuery, $relation);
463
+
464
+		if (is_numeric($count)) {
465
+			$count = new Expression($count);
466
+		}
467
+
468
+		return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
469
+	}
470
+
471
+	/**
472
+	 * Merge the "wheres" from a relation query to a has query.
473
+	 *
474
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
475
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
476
+	 * @return void
477
+	 */
478
+	protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
479
+	{
480
+		// Here we have the "has" query and the original relation. We need to copy over any
481
+		// where clauses the developer may have put in the relationship function over to
482
+		// the has query, and then copy the bindings from the "has" query to the main.
483
+		$relationQuery = $relation->getBaseQuery();
484
+
485
+		$hasQuery->mergeWheres(
486
+			$relationQuery->wheres, $relationQuery->getBindings()
487
+		);
488
+
489
+		$this->query->mergeBindings($hasQuery->getQuery());
490
+	}
491
+
492
+	/**
493
+	 * Get the "has relation" base query instance.
494
+	 *
495
+	 * @param  string $relation
496
+	 * @param         $entity
497
+	 * @return \Analogue\ORM\System\Query
498
+	 */
499
+	protected function getHasRelationQuery($relation, $entity)
500
+	{
501
+		return Relationship::noConstraints(function () use ($relation, $entity) {
502
+			return $this->entityMap->$relation($entity);
503
+		});
504
+	}
505
+
506
+	/**
507
+	 * Get the table for the current query object
508
+	 *
509
+	 * @return string
510
+	 */
511
+	public function getTable()
512
+	{
513
+		return $this->entityMap->getTable();
514
+	}
515
+
516
+	/**
517
+	 * Set the relationships that should be eager loaded.
518
+	 *
519
+	 * @param  mixed $relations
520
+	 * @return $this
521
+	 */
522
+	public function with($relations)
523
+	{
524
+		if (is_string($relations)) {
525
+			$relations = func_get_args();
526
+		}
527
+
528
+		$eagers = $this->parseRelations($relations);
529
+
530
+		$this->eagerLoad = array_merge($this->eagerLoad, $eagers);
531
+
532
+		return $this;
533
+	}
534
+
535
+	/**
536
+	 * Parse a list of relations into individuals.
537
+	 *
538
+	 * @param  array $relations
539
+	 * @return array
540
+	 */
541
+	protected function parseRelations(array $relations)
542
+	{
543
+		$results = [];
544
+
545
+		foreach ($relations as $name => $constraints) {
546
+			// If the "relation" value is actually a numeric key, we can assume that no
547
+			// constraints have been specified for the eager load and we'll just put
548
+			// an empty Closure with the loader so that we can treat all the same.
549
+			if (is_numeric($name)) {
550
+				$f = function () {};
551
+
552
+				list($name, $constraints) = [$constraints, $f];
553
+			}
554
+
555
+			// We need to separate out any nested includes. Which allows the developers
556
+			// to load deep relationships using "dots" without stating each level of
557
+			// the relationship with its own key in the array of eager load names.
558
+			$results = $this->parseNested($name, $results);
559
+
560
+			$results[$name] = $constraints;
561
+		}
562
+
563
+		return $results;
564
+	}
565
+
566
+
567
+	/**
568
+	 * Parse the nested relationships in a relation.
569
+	 *
570
+	 * @param  string $name
571
+	 * @param  array  $results
572
+	 * @return array
573
+	 */
574
+	protected function parseNested($name, $results)
575
+	{
576
+		$progress = [];
577
+
578
+		// If the relation has already been set on the result array, we will not set it
579
+		// again, since that would override any constraints that were already placed
580
+		// on the relationships. We will only set the ones that are not specified.
581
+		foreach (explode('.', $name) as $segment) {
582
+			$progress[] = $segment;
583
+
584
+			if (!isset($results[$last = implode('.', $progress)])) {
585
+				$results[$last] = function () {};
586
+			}
587
+		}
588
+
589
+		return $results;
590
+	}
591
+
592
+	/**
593
+	 * Get the relationships being eagerly loaded.
594
+	 *
595
+	 * @return array
596
+	 */
597
+	public function getEagerLoads()
598
+	{
599
+		return $this->eagerLoad;
600
+	}
601
+
602
+	/**
603
+	 * Set the relationships being eagerly loaded.
604
+	 *
605
+	 * @param  array $eagerLoad
606
+	 * @return void
607
+	 */
608
+	public function setEagerLoads(array $eagerLoad)
609
+	{
610
+		$this->eagerLoad = $eagerLoad;
611
+	}
612
+
613
+	/**
614
+	 * Eager load the relationships for the entities.
615
+	 *
616
+	 * @param  array $entities
617
+	 * @return array
618
+	 */
619
+	public function eagerLoadRelations($entities)
620
+	{
621
+		foreach ($this->eagerLoad as $name => $constraints) {
622
+			// For nested eager loads we'll skip loading them here and they will be set as an
623
+			// eager load on the query to retrieve the relation so that they will be eager
624
+			// loaded on that query, because that is where they get hydrated as models.
625
+			if (strpos($name, '.') === false) {
626
+				$entities = $this->loadRelation($entities, $name, $constraints);
627
+			}
628
+		}
629
+
630
+		return $entities;
631
+	}
632
+
633
+	/**
634
+	 * Eagerly load the relationship on a set of entities.
635
+	 *
636
+	 * @param  array    $entities
637
+	 * @param  string   $name
638
+	 * @param  \Closure $constraints
639
+	 * @return array
640
+	 */
641
+	protected function loadRelation(array $entities, $name, Closure $constraints)
642
+	{
643
+		// First we will "back up" the existing where conditions on the query so we can
644
+		// add our eager constraints. Then we will merge the wheres that were on the
645
+		// query back to it in order that any where conditions might be specified.
646
+		$relation = $this->getRelation($name);
647
+
648
+		$relation->addEagerConstraints($entities);
649
+
650
+		call_user_func($constraints, $relation);
651
+
652
+		$entities = $relation->initRelation($entities, $name);
653
+
654
+		// Once we have the results, we just match those back up to their parent models
655
+		// using the relationship instance. Then we just return the finished arrays
656
+		// of models which have been eagerly hydrated and are readied for return.
657
+
658
+		$results = $relation->getEager();
659
+
660
+		return $relation->match($entities, $results, $name);
661
+	}
662
+
663
+	/**
664
+	 * Get the relation instance for the given relation name.
665
+	 *
666
+	 * @param  string $relation
667
+	 * @return \Analogue\ORM\Relationships\Relationship
668
+	 */
669
+	public function getRelation($relation)
670
+	{
671
+		// We want to run a relationship query without any constrains so that we will
672
+		// not have to remove these where clauses manually which gets really hacky
673
+		// and is error prone while we remove the developer's own where clauses.
674
+		$query = Relationship::noConstraints(function () use ($relation) {
675
+			return $this->entityMap->$relation($this->getEntityInstance());
676
+		});
677
+
678
+		$nested = $this->nestedRelations($relation);
679
+
680
+		// If there are nested relationships set on the query, we will put those onto
681
+		// the query instances so that they can be handled after this relationship
682
+		// is loaded. In this way they will all trickle down as they are loaded.
683
+		if (count($nested) > 0) {
684
+			$query->getQuery()->with($nested);
685
+		}
686
+
687
+		return $query;
688
+	}
689
+
690
+	/**
691
+	 * Get the deeply nested relations for a given top-level relation.
692
+	 *
693
+	 * @param  string $relation
694
+	 * @return array
695
+	 */
696
+	protected function nestedRelations($relation)
697
+	{
698
+		$nested = [];
699
+
700
+		// We are basically looking for any relationships that are nested deeper than
701
+		// the given top-level relationship. We will just check for any relations
702
+		// that start with the given top relations and adds them to our arrays.
703
+		foreach ($this->eagerLoad as $name => $constraints) {
704
+			if ($this->isNested($name, $relation)) {
705
+				$nested[substr($name, strlen($relation . '.'))] = $constraints;
706
+			}
707
+		}
708
+
709
+		return $nested;
710
+	}
711
+
712
+	/**
713
+	 * Determine if the relationship is nested.
714
+	 *
715
+	 * @param  string $name
716
+	 * @param  string $relation
717
+	 * @return bool
718
+	 */
719
+	protected function isNested($name, $relation)
720
+	{
721
+		$dots = str_contains($name, '.');
722
+
723
+		return $dots && starts_with($name, $relation . '.');
724
+	}
725
+
726
+	/**
727
+	 * Add the Entity primary key if not in requested columns
728
+	 *
729
+	 * @param  array $columns
730
+	 * @return array
731
+	 */
732
+	protected function enforceIdColumn($columns)
733
+	{
734
+		if (!in_array($this->entityMap->getKeyName(), $columns)) {
735
+			$columns[] = $this->entityMap->getKeyName();
736
+		}
737
+		return $columns;
738
+	}
739
+
740
+	/**
741
+	 * Get the hydrated models without eager loading.
742
+	 *
743
+	 * @param  array  $columns
744
+	 * @return \Analogue\ORM\EntityCollection
745
+	 */
746
+	public function getEntities($columns = ['*'])
747
+	{
748
+		// As we need the primary key to feed the
749
+		// entity cache, we need it loaded on each
750
+		// request
751
+		$columns = $this->enforceIdColumn($columns);
752
+
753
+		// Run the query
754
+		$results = $this->query->get($columns)->toArray();
755
+
756
+		// Create a result builder.
757
+		$builder = new ResultBuilder(Manager::getInstance(), $this->mapper, array_keys($this->getEagerLoads()));
758
+
759
+		return $builder->build($results);
760
+	}
761
+
762
+	/**
763
+	 * Get a new instance for the entity
764
+	 *
765
+	 * @return \Analogue\ORM\Entity
766
+	 */
767
+	public function getEntityInstance()
768
+	{
769
+		return $this->mapper->newInstance();
770
+	}
771
+
772
+	/**
773
+	 * Extend the builder with a given callback.
774
+	 *
775
+	 * @param  string   $name
776
+	 * @param  \Closure $callback
777
+	 * @return void
778
+	 */
779
+	public function macro($name, Closure $callback)
780
+	{
781
+		$this->macros[$name] = $callback;
782
+	}
783
+
784
+	/**
785
+	 * Get the given macro by name.
786
+	 *
787
+	 * @param  string $name
788
+	 * @return \Closure
789
+	 */
790
+	public function getMacro($name)
791
+	{
792
+		return array_get($this->macros, $name);
793
+	}
794
+
795
+	/**
796
+	 * Get a new query builder for the model's table.
797
+	 *
798
+	 * @return \Analogue\ORM\System\Query
799
+	 */
800
+	public function newQuery()
801
+	{
802
+		$builder = new Query($this->mapper, $this->adapter);
803
+
804
+		return $this->applyGlobalScopes($builder);
805
+	}
806
+
807
+	/**
808
+	 * Get a new query builder without any scope applied.
809
+	 *
810
+	 * @return \Analogue\ORM\System\Query
811
+	 */
812
+	public function newQueryWithoutScopes()
813
+	{
814
+		return new Query($this->mapper, $this->adapter);
815
+	}
816
+
817
+	/**
818
+	 * Get the Mapper instance for this Query Builder
819
+	 *
820
+	 * @return \Analogue\ORM\System\Mapper
821
+	 */
822
+	public function getMapper()
823
+	{
824
+		return $this->mapper;
825
+	}
826
+
827
+	/**
828
+	 * Get the underlying query adapter
829
+	 *
830
+	 * (REFACTOR: this method should move out, we need to provide the client classes
831
+	 * with the adapter instead.)
832
+	 *
833
+	 * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
834
+	 */
835
+	public function getQuery()
836
+	{
837
+		return $this->query;
838
+	}
839
+
840
+	/**
841
+	 * Dynamically handle calls into the query instance.
842
+	 *
843
+	 * @param  string $method
844
+	 * @param  array  $parameters
845
+	 * @throws Exception
846
+	 * @return mixed
847
+	 */
848
+	public function __call($method, $parameters)
849
+	{
850
+		if (isset($this->macros[$method])) {
851
+			array_unshift($parameters, $this);
852
+
853
+			return call_user_func_array($this->macros[$method], $parameters);
854
+		}
855
+
856
+		if (in_array($method, $this->blacklist)) {
857
+			throw new Exception("Method $method doesn't exist");
858
+		}
859
+
860
+		$result = call_user_func_array([$this->query, $method], $parameters);
861
+
862
+		return in_array($method, $this->passthru) ? $result : $this;
863
+	}
864 864
 }
865 865
\ No newline at end of file
Please login to merge, or discard this patch.
src/System/Wrappers/Wrapper.php 5 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -45,7 +45,7 @@  discard block
 block discarded – undo
45 45
     /**
46 46
      * Return the wrapped entity class
47 47
      *
48
-     * @return mixed
48
+     * @return string
49 49
      */
50 50
     public function getEntityClass()
51 51
     {
@@ -65,7 +65,7 @@  discard block
 block discarded – undo
65 65
     /**
66 66
      * Returns the wrapped entity's map
67 67
      *
68
-     * @return mixed
68
+     * @return \Analogue\ORM\EntityMap
69 69
      */
70 70
     public function getMap()
71 71
     {
Please login to merge, or discard this patch.
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -4,7 +4,6 @@
 block discarded – undo
4 4
 
5 5
 use Analogue\ORM\System\InternallyMappable;
6 6
 use Analogue\ORM\System\Proxies\ProxyFactory;
7
-use Analogue\ORM\System\Proxies\CollectionProxy;
8 7
 
9 8
 /**
10 9
  * The Wrapper Class provides a single interface access several Entity types
Please login to merge, or discard this patch.
Indentation   +175 added lines, -175 removed lines patch added patch discarded remove patch
@@ -11,180 +11,180 @@
 block discarded – undo
11 11
  */
12 12
 abstract class Wrapper implements InternallyMappable
13 13
 {
14
-    /**
15
-     * Original Entity Object
16
-     *
17
-     * @var mixed
18
-     */
19
-    protected $entity;
20
-
21
-    /**
22
-     * Corresponding EntityMap
23
-     *
24
-     * @var \Analogue\ORM\EntityMap
25
-     */
26
-    protected $entityMap;
27
-
28
-    /**
29
-     * @var \Analogue\ORM\System\Proxirs\ProxyFactory
30
-     */
31
-    protected $proxyFactory;
32
-
33
-    /**
34
-     * Wrapper constructor.
35
-     * @param $entity
36
-     * @param $entityMap
37
-     */
38
-    public function __construct($entity, $entityMap)
39
-    {
40
-        $this->entity = $entity;
41
-        $this->entityMap = $entityMap;
42
-        $this->proxyFactory = new ProxyFactory;
43
-    }
44
-
45
-    /**
46
-     * Return the wrapped entity class
47
-     *
48
-     * @return mixed
49
-     */
50
-    public function getEntityClass()
51
-    {
52
-        return get_class($this->entity);
53
-    }
54
-
55
-    /**
56
-     * Returns the wrapped entity
57
-     *
58
-     * @return mixed
59
-     */
60
-    public function getObject()
61
-    {
62
-        return $this->entity;
63
-    }
64
-
65
-    /**
66
-     * Returns the wrapped entity's map
67
-     *
68
-     * @return mixed
69
-     */
70
-    public function getMap()
71
-    {
72
-        return $this->entityMap;
73
-    }
74
-
75
-    /**
76
-     * Set the lazyloading proxies on the wrapped entity objet
77
-     * 
78
-     * @param  array  $relations  list of relations to be lazy loaded
79
-     * 
80
-     * @return void
81
-     */
82
-    public function setProxies(array $relations = null)
83
-    {
84
-        $attributes = $this->getEntityAttributes();
85
-        $proxies = [];
86
-
87
-        if(is_null($relations)) {
88
-            $relations = $this->getRelationsToProxy();
89
-        }
90
-
91
-        foreach ($relations as $relation) {
92
-            // We first look if we need to build the proxy on the relationship.
93
-            // If the key is handled locally and we know it not to be set,
94
-            // we'll set the relationship to null
95
-            if (! $this->relationNeedsProxy($relation, $attributes))  {
96
-                $proxies[$relation] = null;
97
-            }
98
-            else {
99
-                $targetClass = $this->entityMap->getTargettedClass($relation);
100
-                $proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
101
-            }
102
-        }
103
-
104
-        foreach ($proxies as $key => $value) {
105
-            $this->setEntityAttribute($key, $value);
106
-        }
107
-    }
108
-
109
-    /**  
110
-     * Determine which relations we have to build proxy for, by parsing
111
-     * attributes and finding methods that aren't set.
112
-     * 
113
-     * @return array
114
-     */
115
-    protected function getRelationsToProxy()
116
-    {
117
-        $proxies = [];
118
-        $attributes = $this->getEntityAttributes();
119
-
120
-        foreach ($this->entityMap->getRelationships() as $relation) {
121
-            if (!array_key_exists($relation, $attributes)) {
122
-                $proxies[] = $relation;
123
-            }
124
-        }
125
-
126
-        return $proxies;
127
-    }
128
-
129
-    /**  
130
-     * Determine if the relation needs a proxy or not
131
-     * 
132
-     * @param  string $relation  
133
-     * @param  array $attributes 
134
-     * @return boolean
135
-     */
136
-    protected function relationNeedsProxy($relation, $attributes)
137
-    {
138
-        if(in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
139
-            return false;
140
-        }
141
-
142
-        $localKey = $this->entityMap->getLocalKeys($relation);
143
-
144
-        if(is_null($localKey)) return true;
145
-
146
-        if(is_array($localKey)) {
147
-            $localKey = $localKey['id'];
148
-        }
149
-
150
-        if(! isset($attributes[$localKey])) {
151
-            return false;
152
-        }
153
-
154
-        if(is_null($attributes[$localKey])) {
155
-            return false;
156
-        }
14
+	/**
15
+	 * Original Entity Object
16
+	 *
17
+	 * @var mixed
18
+	 */
19
+	protected $entity;
20
+
21
+	/**
22
+	 * Corresponding EntityMap
23
+	 *
24
+	 * @var \Analogue\ORM\EntityMap
25
+	 */
26
+	protected $entityMap;
27
+
28
+	/**
29
+	 * @var \Analogue\ORM\System\Proxirs\ProxyFactory
30
+	 */
31
+	protected $proxyFactory;
32
+
33
+	/**
34
+	 * Wrapper constructor.
35
+	 * @param $entity
36
+	 * @param $entityMap
37
+	 */
38
+	public function __construct($entity, $entityMap)
39
+	{
40
+		$this->entity = $entity;
41
+		$this->entityMap = $entityMap;
42
+		$this->proxyFactory = new ProxyFactory;
43
+	}
44
+
45
+	/**
46
+	 * Return the wrapped entity class
47
+	 *
48
+	 * @return mixed
49
+	 */
50
+	public function getEntityClass()
51
+	{
52
+		return get_class($this->entity);
53
+	}
54
+
55
+	/**
56
+	 * Returns the wrapped entity
57
+	 *
58
+	 * @return mixed
59
+	 */
60
+	public function getObject()
61
+	{
62
+		return $this->entity;
63
+	}
64
+
65
+	/**
66
+	 * Returns the wrapped entity's map
67
+	 *
68
+	 * @return mixed
69
+	 */
70
+	public function getMap()
71
+	{
72
+		return $this->entityMap;
73
+	}
74
+
75
+	/**
76
+	 * Set the lazyloading proxies on the wrapped entity objet
77
+	 * 
78
+	 * @param  array  $relations  list of relations to be lazy loaded
79
+	 * 
80
+	 * @return void
81
+	 */
82
+	public function setProxies(array $relations = null)
83
+	{
84
+		$attributes = $this->getEntityAttributes();
85
+		$proxies = [];
86
+
87
+		if(is_null($relations)) {
88
+			$relations = $this->getRelationsToProxy();
89
+		}
90
+
91
+		foreach ($relations as $relation) {
92
+			// We first look if we need to build the proxy on the relationship.
93
+			// If the key is handled locally and we know it not to be set,
94
+			// we'll set the relationship to null
95
+			if (! $this->relationNeedsProxy($relation, $attributes))  {
96
+				$proxies[$relation] = null;
97
+			}
98
+			else {
99
+				$targetClass = $this->entityMap->getTargettedClass($relation);
100
+				$proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
101
+			}
102
+		}
103
+
104
+		foreach ($proxies as $key => $value) {
105
+			$this->setEntityAttribute($key, $value);
106
+		}
107
+	}
108
+
109
+	/**  
110
+	 * Determine which relations we have to build proxy for, by parsing
111
+	 * attributes and finding methods that aren't set.
112
+	 * 
113
+	 * @return array
114
+	 */
115
+	protected function getRelationsToProxy()
116
+	{
117
+		$proxies = [];
118
+		$attributes = $this->getEntityAttributes();
119
+
120
+		foreach ($this->entityMap->getRelationships() as $relation) {
121
+			if (!array_key_exists($relation, $attributes)) {
122
+				$proxies[] = $relation;
123
+			}
124
+		}
125
+
126
+		return $proxies;
127
+	}
128
+
129
+	/**  
130
+	 * Determine if the relation needs a proxy or not
131
+	 * 
132
+	 * @param  string $relation  
133
+	 * @param  array $attributes 
134
+	 * @return boolean
135
+	 */
136
+	protected function relationNeedsProxy($relation, $attributes)
137
+	{
138
+		if(in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
139
+			return false;
140
+		}
141
+
142
+		$localKey = $this->entityMap->getLocalKeys($relation);
143
+
144
+		if(is_null($localKey)) return true;
145
+
146
+		if(is_array($localKey)) {
147
+			$localKey = $localKey['id'];
148
+		}
149
+
150
+		if(! isset($attributes[$localKey])) {
151
+			return false;
152
+		}
153
+
154
+		if(is_null($attributes[$localKey])) {
155
+			return false;
156
+		}
157 157
         
158
-        return true;
159
-    }
160
-
161
-    /**
162
-     * @param string $key
163
-     * @param string $value
164
-     * @return mixed
165
-     */
166
-    abstract public function setEntityAttribute($key, $value);
167
-
168
-    /**
169
-     * @param string $key
170
-     * @return mixed
171
-     */
172
-    abstract public function getEntityAttribute($key);
173
-
174
-    /**
175
-     * @param array $attributes
176
-     * @return mixed
177
-     */
178
-    abstract public function setEntityAttributes(array $attributes);
179
-
180
-    /**
181
-     * @return mixed
182
-     */
183
-    abstract public function getEntityAttributes();
184
-
185
-    /**
186
-     * @param string $key
187
-     * @return mixed
188
-     */
189
-    abstract public function hasAttribute($key);
158
+		return true;
159
+	}
160
+
161
+	/**
162
+	 * @param string $key
163
+	 * @param string $value
164
+	 * @return mixed
165
+	 */
166
+	abstract public function setEntityAttribute($key, $value);
167
+
168
+	/**
169
+	 * @param string $key
170
+	 * @return mixed
171
+	 */
172
+	abstract public function getEntityAttribute($key);
173
+
174
+	/**
175
+	 * @param array $attributes
176
+	 * @return mixed
177
+	 */
178
+	abstract public function setEntityAttributes(array $attributes);
179
+
180
+	/**
181
+	 * @return mixed
182
+	 */
183
+	abstract public function getEntityAttributes();
184
+
185
+	/**
186
+	 * @param string $key
187
+	 * @return mixed
188
+	 */
189
+	abstract public function hasAttribute($key);
190 190
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
         $attributes = $this->getEntityAttributes();
85 85
         $proxies = [];
86 86
 
87
-        if(is_null($relations)) {
87
+        if (is_null($relations)) {
88 88
             $relations = $this->getRelationsToProxy();
89 89
         }
90 90
 
@@ -92,7 +92,7 @@  discard block
 block discarded – undo
92 92
             // We first look if we need to build the proxy on the relationship.
93 93
             // If the key is handled locally and we know it not to be set,
94 94
             // we'll set the relationship to null
95
-            if (! $this->relationNeedsProxy($relation, $attributes))  {
95
+            if (!$this->relationNeedsProxy($relation, $attributes)) {
96 96
                 $proxies[$relation] = null;
97 97
             }
98 98
             else {
@@ -135,23 +135,23 @@  discard block
 block discarded – undo
135 135
      */
136 136
     protected function relationNeedsProxy($relation, $attributes)
137 137
     {
138
-        if(in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
138
+        if (in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
139 139
             return false;
140 140
         }
141 141
 
142 142
         $localKey = $this->entityMap->getLocalKeys($relation);
143 143
 
144
-        if(is_null($localKey)) return true;
144
+        if (is_null($localKey)) return true;
145 145
 
146
-        if(is_array($localKey)) {
146
+        if (is_array($localKey)) {
147 147
             $localKey = $localKey['id'];
148 148
         }
149 149
 
150
-        if(! isset($attributes[$localKey])) {
150
+        if (!isset($attributes[$localKey])) {
151 151
             return false;
152 152
         }
153 153
 
154
-        if(is_null($attributes[$localKey])) {
154
+        if (is_null($attributes[$localKey])) {
155 155
             return false;
156 156
         }
157 157
         
Please login to merge, or discard this patch.
Braces   +4 added lines, -3 removed lines patch added patch discarded remove patch
@@ -94,8 +94,7 @@  discard block
 block discarded – undo
94 94
             // we'll set the relationship to null
95 95
             if (! $this->relationNeedsProxy($relation, $attributes))  {
96 96
                 $proxies[$relation] = null;
97
-            }
98
-            else {
97
+            } else {
99 98
                 $targetClass = $this->entityMap->getTargettedClass($relation);
100 99
                 $proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
101 100
             }
@@ -141,7 +140,9 @@  discard block
 block discarded – undo
141 140
 
142 141
         $localKey = $this->entityMap->getLocalKeys($relation);
143 142
 
144
-        if(is_null($localKey)) return true;
143
+        if(is_null($localKey)) {
144
+        	return true;
145
+        }
145 146
 
146 147
         if(is_array($localKey)) {
147 148
             $localKey = $localKey['id'];
Please login to merge, or discard this patch.
src/MagicSetters.php 1 patch
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -7,28 +7,28 @@
 block discarded – undo
7 7
  */
8 8
 trait MagicSetters
9 9
 {
10
-    /**
11
-     * Dynamically set attributes on the entity.
12
-     *
13
-     * @param  string $key
14
-     * @param  mixed  $value
15
-     * @return void
16
-     */
17
-    public function __set($key, $value)
18
-    {
19
-        $this->attributes[$key] = $value;
20
-    }
10
+	/**
11
+	 * Dynamically set attributes on the entity.
12
+	 *
13
+	 * @param  string $key
14
+	 * @param  mixed  $value
15
+	 * @return void
16
+	 */
17
+	public function __set($key, $value)
18
+	{
19
+		$this->attributes[$key] = $value;
20
+	}
21 21
 
22
-    /**
23
-     * Unset an attribute on the entity.
24
-     *
25
-     * @param  string $key
26
-     * @return void
27
-     */
28
-    public function __unset($key)
29
-    {
30
-        unset($this->attributes[$key]);
31
-    }
22
+	/**
23
+	 * Unset an attribute on the entity.
24
+	 *
25
+	 * @param  string $key
26
+	 * @return void
27
+	 */
28
+	public function __unset($key)
29
+	{
30
+		unset($this->attributes[$key]);
31
+	}
32 32
 
33 33
 
34 34
 }
35 35
\ No newline at end of file
Please login to merge, or discard this patch.
src/Commands/Store.php 1 patch
Indentation   +221 added lines, -221 removed lines patch added patch discarded remove patch
@@ -14,238 +14,238 @@
 block discarded – undo
14 14
  */
15 15
 class Store extends Command
16 16
 {
17
-    /**
18
-     * Persist the entity in the database
19
-     *
20
-     * @throws \InvalidArgumentException
21
-     * @return false|mixed
22
-     */
23
-    public function execute()
24
-    {
25
-        $entity = $this->aggregate->getEntityObject();
26
-
27
-        $mapper = $this->aggregate->getMapper();
28
-
29
-        if ($mapper->fireEvent('storing', $entity) === false) {
30
-            return false;
31
-        }
32
-
33
-        $this->preStoreProcess();
34
-
35
-        /**
36
-         * We will test the entity for existence
37
-         * and run a creation if it doesn't exists
38
-         */
39
-        if (!$this->aggregate->exists()) {
40
-            if ($mapper->fireEvent('creating', $entity) === false) {
41
-                return false;
42
-            }
43
-
44
-            $this->insert();
45
-
46
-            $mapper->fireEvent('created', $entity, false);
47
-        }
48
-        else if ($this->aggregate->isDirty()) {
49
-            if ($mapper->fireEvent('updating', $entity) === false) {
50
-                return false;
51
-            }
52
-            $this->update();
53
-
54
-            $mapper->fireEvent('updated', $entity, false);
55
-        }
56
-
57
-        $this->postStoreProcess();
58
-
59
-        $mapper->fireEvent('stored', $entity, false);
60
-
61
-        return $entity;
62
-    }
63
-
64
-    /**
65
-     * Run all operations that have to occur before actually
66
-     * storing the entity
67
-     *
68
-     * @throws \InvalidArgumentException
69
-     * @return void
70
-     */
71
-    protected function preStoreProcess()
72
-    {
73
-        // Create any related object that doesn't exist in the database.
74
-        $localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
75
-
76
-        $this->createRelatedEntities($localRelationships);
77
-
78
-        // Now we can sync the related collections
79
-        $this->aggregate->syncRelationships($localRelationships);
80
-    }
81
-
82
-    /**
83
-     * Check for existence and create non-existing related entities
84
-     *
85
-     * @param  array
86
-     * @throws \InvalidArgumentException
87
-     * @return void
88
-     */
89
-    protected function createRelatedEntities($relations)
90
-    {
91
-        $entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
92
-
93
-        foreach ($entitiesToCreate as $aggregate) {
94
-            $this->createStoreCommand($aggregate)->execute();
95
-        }
17
+	/**
18
+	 * Persist the entity in the database
19
+	 *
20
+	 * @throws \InvalidArgumentException
21
+	 * @return false|mixed
22
+	 */
23
+	public function execute()
24
+	{
25
+		$entity = $this->aggregate->getEntityObject();
26
+
27
+		$mapper = $this->aggregate->getMapper();
28
+
29
+		if ($mapper->fireEvent('storing', $entity) === false) {
30
+			return false;
31
+		}
32
+
33
+		$this->preStoreProcess();
34
+
35
+		/**
36
+		 * We will test the entity for existence
37
+		 * and run a creation if it doesn't exists
38
+		 */
39
+		if (!$this->aggregate->exists()) {
40
+			if ($mapper->fireEvent('creating', $entity) === false) {
41
+				return false;
42
+			}
43
+
44
+			$this->insert();
45
+
46
+			$mapper->fireEvent('created', $entity, false);
47
+		}
48
+		else if ($this->aggregate->isDirty()) {
49
+			if ($mapper->fireEvent('updating', $entity) === false) {
50
+				return false;
51
+			}
52
+			$this->update();
53
+
54
+			$mapper->fireEvent('updated', $entity, false);
55
+		}
56
+
57
+		$this->postStoreProcess();
58
+
59
+		$mapper->fireEvent('stored', $entity, false);
60
+
61
+		return $entity;
62
+	}
63
+
64
+	/**
65
+	 * Run all operations that have to occur before actually
66
+	 * storing the entity
67
+	 *
68
+	 * @throws \InvalidArgumentException
69
+	 * @return void
70
+	 */
71
+	protected function preStoreProcess()
72
+	{
73
+		// Create any related object that doesn't exist in the database.
74
+		$localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
75
+
76
+		$this->createRelatedEntities($localRelationships);
77
+
78
+		// Now we can sync the related collections
79
+		$this->aggregate->syncRelationships($localRelationships);
80
+	}
81
+
82
+	/**
83
+	 * Check for existence and create non-existing related entities
84
+	 *
85
+	 * @param  array
86
+	 * @throws \InvalidArgumentException
87
+	 * @return void
88
+	 */
89
+	protected function createRelatedEntities($relations)
90
+	{
91
+		$entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
92
+
93
+		foreach ($entitiesToCreate as $aggregate) {
94
+			$this->createStoreCommand($aggregate)->execute();
95
+		}
96 96
         
97
-    }
98
-
99
-    /**
100
-     * Create a new store command
101
-     *
102
-     * @param  Aggregate $aggregate
103
-     * @return Store
104
-     */
105
-    protected function createStoreCommand(Aggregate $aggregate)
106
-    {
107
-        // We gotta retrieve the corresponding query adapter to use.
108
-        $mapper = $aggregate->getMapper();
109
-
110
-        return new Store($aggregate, $mapper->newQueryBuilder());
111
-    }
112
-
113
-    /**
114
-     * Run all operations that have to occur after the entity
115
-     * is stored.
116
-     *
117
-     * @throws \InvalidArgumentException
118
-     * @return void
119
-     */
120
-    protected function postStoreProcess()
121
-    {
122
-        $aggregate = $this->aggregate;
123
-
124
-        // Create any related object that doesn't exist in the database.
125
-        $foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
126
-
127
-        $this->createRelatedEntities($foreignRelationships);
128
-
129
-        // Update any pivot tables that has been modified.
130
-        $aggregate->updatePivotRecords();
131
-
132
-        // Update any dirty relationship. This include relationships that already exists, have
133
-        // dirty attributes / newly created related entities / dirty related entities.
134
-        $dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
97
+	}
98
+
99
+	/**
100
+	 * Create a new store command
101
+	 *
102
+	 * @param  Aggregate $aggregate
103
+	 * @return Store
104
+	 */
105
+	protected function createStoreCommand(Aggregate $aggregate)
106
+	{
107
+		// We gotta retrieve the corresponding query adapter to use.
108
+		$mapper = $aggregate->getMapper();
109
+
110
+		return new Store($aggregate, $mapper->newQueryBuilder());
111
+	}
112
+
113
+	/**
114
+	 * Run all operations that have to occur after the entity
115
+	 * is stored.
116
+	 *
117
+	 * @throws \InvalidArgumentException
118
+	 * @return void
119
+	 */
120
+	protected function postStoreProcess()
121
+	{
122
+		$aggregate = $this->aggregate;
123
+
124
+		// Create any related object that doesn't exist in the database.
125
+		$foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
126
+
127
+		$this->createRelatedEntities($foreignRelationships);
128
+
129
+		// Update any pivot tables that has been modified.
130
+		$aggregate->updatePivotRecords();
131
+
132
+		// Update any dirty relationship. This include relationships that already exists, have
133
+		// dirty attributes / newly created related entities / dirty related entities.
134
+		$dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
135 135
         
136
-        foreach ($dirtyRelatedAggregates as $related) {
137
-            $this->createStoreCommand($related)->execute();
138
-        }
139
-
140
-        // Now we can sync the related collections
141
-        if ($this->aggregate->exists()) {
142
-            $this->aggregate->syncRelationships($foreignRelationships);
143
-        }
136
+		foreach ($dirtyRelatedAggregates as $related) {
137
+			$this->createStoreCommand($related)->execute();
138
+		}
139
+
140
+		// Now we can sync the related collections
141
+		if ($this->aggregate->exists()) {
142
+			$this->aggregate->syncRelationships($foreignRelationships);
143
+		}
144 144
         
145
-        // TODO be move it to the wrapper class
146
-        // so it's the same code for the entity builder
147
-        $aggregate->setProxies();
148
-
149
-        // Update Entity Cache
150
-        $aggregate->getMapper()->getEntityCache()->refresh($aggregate);
151
-    }
152
-
153
-    /**
154
-     * Update Related Entities which attributes have
155
-     * been modified.
156
-     *
157
-     * @return void
158
-     */
159
-    protected function updateDirtyRelated()
160
-    {
161
-        $relations = $this->entityMap->getRelationships();
162
-        $attributes = $this->getAttributes();
163
-
164
-        foreach ($relations as $relation) {
165
-            if (!array_key_exists($relation, $attributes)) {
166
-                continue;
167
-            }
168
-
169
-            $value = $attributes[$relation];
170
-
171
-            if ($value == null) {
172
-                continue;
173
-            }
174
-
175
-            if ($value instanceof EntityProxy) {
176
-                continue;
177
-            }
178
-
179
-            if ($value instanceof CollectionProxy && $value->isLoaded()) {
180
-                $value = $value->getUnderlyingCollection();
181
-            }
182
-            if ($value instanceof CollectionProxy && !$value->isLoaded()) {
183
-                foreach ($value->getAddedItems() as $entity) {
184
-                    $this->updateEntityIfDirty($entity);
185
-                }
186
-                continue;
187
-            }
188
-
189
-            if ($value instanceof EntityCollection) {
190
-                foreach ($value as $entity) {
191
-                    if (!$this->createEntityIfNotExists($entity)) {
192
-                        $this->updateEntityIfDirty($entity);
193
-                    }
194
-                }
195
-                continue;
196
-            }
197
-            if ($value instanceof Mappable) {
198
-                $this->updateEntityIfDirty($value);
199
-                continue;
200
-            }
201
-        }
202
-    }
203
-
204
-    /**
205
-     * Execute an insert statement on the database
206
-     *
207
-     * @return void
208
-     */
209
-    protected function insert()
210
-    {
211
-        $aggregate = $this->aggregate;
212
-
213
-        $attributes = $aggregate->getRawAttributes();
145
+		// TODO be move it to the wrapper class
146
+		// so it's the same code for the entity builder
147
+		$aggregate->setProxies();
148
+
149
+		// Update Entity Cache
150
+		$aggregate->getMapper()->getEntityCache()->refresh($aggregate);
151
+	}
152
+
153
+	/**
154
+	 * Update Related Entities which attributes have
155
+	 * been modified.
156
+	 *
157
+	 * @return void
158
+	 */
159
+	protected function updateDirtyRelated()
160
+	{
161
+		$relations = $this->entityMap->getRelationships();
162
+		$attributes = $this->getAttributes();
163
+
164
+		foreach ($relations as $relation) {
165
+			if (!array_key_exists($relation, $attributes)) {
166
+				continue;
167
+			}
168
+
169
+			$value = $attributes[$relation];
170
+
171
+			if ($value == null) {
172
+				continue;
173
+			}
174
+
175
+			if ($value instanceof EntityProxy) {
176
+				continue;
177
+			}
178
+
179
+			if ($value instanceof CollectionProxy && $value->isLoaded()) {
180
+				$value = $value->getUnderlyingCollection();
181
+			}
182
+			if ($value instanceof CollectionProxy && !$value->isLoaded()) {
183
+				foreach ($value->getAddedItems() as $entity) {
184
+					$this->updateEntityIfDirty($entity);
185
+				}
186
+				continue;
187
+			}
188
+
189
+			if ($value instanceof EntityCollection) {
190
+				foreach ($value as $entity) {
191
+					if (!$this->createEntityIfNotExists($entity)) {
192
+						$this->updateEntityIfDirty($entity);
193
+					}
194
+				}
195
+				continue;
196
+			}
197
+			if ($value instanceof Mappable) {
198
+				$this->updateEntityIfDirty($value);
199
+				continue;
200
+			}
201
+		}
202
+	}
203
+
204
+	/**
205
+	 * Execute an insert statement on the database
206
+	 *
207
+	 * @return void
208
+	 */
209
+	protected function insert()
210
+	{
211
+		$aggregate = $this->aggregate;
212
+
213
+		$attributes = $aggregate->getRawAttributes();
214 214
         
215
-        $keyName = $aggregate->getEntityMap()->getKeyName();
215
+		$keyName = $aggregate->getEntityMap()->getKeyName();
216 216
 
217
-        // Check if the primary key is defined in the attributes
218
-        if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
219
-            $this->query->insert($attributes);
220
-        } else {
221
-            $sequence = $aggregate->getEntityMap()->getSequence();
217
+		// Check if the primary key is defined in the attributes
218
+		if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
219
+			$this->query->insert($attributes);
220
+		} else {
221
+			$sequence = $aggregate->getEntityMap()->getSequence();
222 222
 
223
-            $id = $this->query->insertGetId($attributes, $sequence);
223
+			$id = $this->query->insertGetId($attributes, $sequence);
224 224
 
225
-            $aggregate->setEntityAttribute($keyName, $id);
226
-        }
225
+			$aggregate->setEntityAttribute($keyName, $id);
226
+		}
227 227
 
228
-    }
228
+	}
229 229
 
230
-    /**
231
-     * Run an update statement on the entity
232
-     *
233
-     * @throws \InvalidArgumentException
234
-     *
235
-     * @return void
236
-     */
237
-    protected function update()
238
-    {
239
-        $query = $this->query;
230
+	/**
231
+	 * Run an update statement on the entity
232
+	 *
233
+	 * @throws \InvalidArgumentException
234
+	 *
235
+	 * @return void
236
+	 */
237
+	protected function update()
238
+	{
239
+		$query = $this->query;
240 240
 
241
-        $keyName = $this->aggregate->getEntityKey();
241
+		$keyName = $this->aggregate->getEntityKey();
242 242
 
243
-        $query = $query->where($keyName, '=', $this->aggregate->getEntityId());
243
+		$query = $query->where($keyName, '=', $this->aggregate->getEntityId());
244 244
 
245
-        $dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
245
+		$dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
246 246
 
247
-        if (count($dirtyAttributes) > 0) {
248
-            $query->update($dirtyAttributes);
249
-        }
250
-    }
247
+		if (count($dirtyAttributes) > 0) {
248
+			$query->update($dirtyAttributes);
249
+		}
250
+	}
251 251
 }
Please login to merge, or discard this patch.
src/MagicGetters.php 2 patches
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -12,48 +12,48 @@
 block discarded – undo
12 12
 	protected $attributes = [];
13 13
 
14 14
 	/**
15
-     * Dynamically retrieve attributes on the entity.
16
-     *
17
-     * @param  string $key
18
-     * @return mixed
19
-     */
20
-    public function __get($key)
21
-    {
22
-    	// When using mixed mapping, we will check
23
-    	// for a class property corresponding to 
24
-    	// the attribute's key first. 
25
-    	// 
26
-    	// Note : this may raise issues as we may grant
27
-    	// access to unwanted properties, like class dependencies. 
28
-    	// 
29
-    	// -> Solution would be to access the entityMap's $attributes, but we
30
-    	// have to do this in a very efficient way.
31
-    	// 
32
-    	// Manager::getEntityMap(get_class($this))->hasProperty()
33
-    	// 
34
-    	// We could do the casting to array / json the same way, and it would 
15
+	 * Dynamically retrieve attributes on the entity.
16
+	 *
17
+	 * @param  string $key
18
+	 * @return mixed
19
+	 */
20
+	public function __get($key)
21
+	{
22
+		// When using mixed mapping, we will check
23
+		// for a class property corresponding to 
24
+		// the attribute's key first. 
25
+		// 
26
+		// Note : this may raise issues as we may grant
27
+		// access to unwanted properties, like class dependencies. 
28
+		// 
29
+		// -> Solution would be to access the entityMap's $attributes, but we
30
+		// have to do this in a very efficient way.
31
+		// 
32
+		// Manager::getEntityMap(get_class($this))->hasProperty()
33
+		// 
34
+		// We could do the casting to array / json the same way, and it would 
35 35
     	
36 36
 
37
-    	if(property_exists($this, $key)) {
38
-    		return $this->$key;
39
-    	}
37
+		if(property_exists($this, $key)) {
38
+			return $this->$key;
39
+		}
40 40
 
41
-    	if(array_key_exists($key, $this->attributes))
42
-    	{
43
-        	return $this->attributes[$key];
44
-        }
41
+		if(array_key_exists($key, $this->attributes))
42
+		{
43
+			return $this->attributes[$key];
44
+		}
45 45
 
46
-        return null;
47
-    }
46
+		return null;
47
+	}
48 48
 
49
-    /**
50
-     * Determine if an attribute exists on the entity.
51
-     *
52
-     * @param  string $key
53
-     * @return bool
54
-     */
55
-    public function __isset($key)
56
-    {
57
-        return array_key_exists($key, $this->attributes) || property_exists($this, $key);
58
-    }
49
+	/**
50
+	 * Determine if an attribute exists on the entity.
51
+	 *
52
+	 * @param  string $key
53
+	 * @return bool
54
+	 */
55
+	public function __isset($key)
56
+	{
57
+		return array_key_exists($key, $this->attributes) || property_exists($this, $key);
58
+	}
59 59
 }
60 60
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -34,11 +34,11 @@
 block discarded – undo
34 34
     	// We could do the casting to array / json the same way, and it would 
35 35
     	
36 36
 
37
-    	if(property_exists($this, $key)) {
37
+    	if (property_exists($this, $key)) {
38 38
     		return $this->$key;
39 39
     	}
40 40
 
41
-    	if(array_key_exists($key, $this->attributes))
41
+    	if (array_key_exists($key, $this->attributes))
42 42
     	{
43 43
         	return $this->attributes[$key];
44 44
         }
Please login to merge, or discard this patch.
src/MagicCasting.php 1 patch
Indentation   +95 added lines, -95 removed lines patch added patch discarded remove patch
@@ -10,109 +10,109 @@
 block discarded – undo
10 10
 {
11 11
 
12 12
  	/**
13
-     * Determine if the given attribute exists.
14
-     *
15
-     * @param  mixed $offset
16
-     * @return bool
17
-     */
18
-    public function offsetExists($offset)
19
-    {
20
-        return isset($this->$offset);
21
-    }
13
+ 	 * Determine if the given attribute exists.
14
+ 	 *
15
+ 	 * @param  mixed $offset
16
+ 	 * @return bool
17
+ 	 */
18
+	public function offsetExists($offset)
19
+	{
20
+		return isset($this->$offset);
21
+	}
22 22
 
23
-    /**
24
-     * Get the value for a given offset.
25
-     *
26
-     * @param  mixed $offset
27
-     * @return mixed
28
-     */
29
-    public function offsetGet($offset)
30
-    {
31
-        return $this->$offset;
32
-    }
23
+	/**
24
+	 * Get the value for a given offset.
25
+	 *
26
+	 * @param  mixed $offset
27
+	 * @return mixed
28
+	 */
29
+	public function offsetGet($offset)
30
+	{
31
+		return $this->$offset;
32
+	}
33 33
 
34
-    /**
35
-     * Set the value for a given offset.
36
-     *
37
-     * @param  mixed $offset
38
-     * @param  mixed $value
39
-     * @return void
40
-     */
41
-    public function offsetSet($offset, $value)
42
-    {
43
-        $this->$offset = $value;
44
-    }
34
+	/**
35
+	 * Set the value for a given offset.
36
+	 *
37
+	 * @param  mixed $offset
38
+	 * @param  mixed $value
39
+	 * @return void
40
+	 */
41
+	public function offsetSet($offset, $value)
42
+	{
43
+		$this->$offset = $value;
44
+	}
45 45
 
46
-    /**
47
-     * Unset the value for a given offset.
48
-     *
49
-     * @param  mixed $offset
50
-     * @return void
51
-     */
52
-    public function offsetUnset($offset)
53
-    {
54
-        unset($this->$offset);
55
-    }
46
+	/**
47
+	 * Unset the value for a given offset.
48
+	 *
49
+	 * @param  mixed $offset
50
+	 * @return void
51
+	 */
52
+	public function offsetUnset($offset)
53
+	{
54
+		unset($this->$offset);
55
+	}
56 56
 
57
-    /**
58
-     * Convert the object into something JSON serializable.
59
-     *
60
-     * @return array
61
-     */
62
-    public function jsonSerialize()
63
-    {
64
-        return $this->toArray();
65
-    }
57
+	/**
58
+	 * Convert the object into something JSON serializable.
59
+	 *
60
+	 * @return array
61
+	 */
62
+	public function jsonSerialize()
63
+	{
64
+		return $this->toArray();
65
+	}
66 66
 
67
-    /**
68
-     * Convert the entity instance to JSON.
69
-     *
70
-     * @param  int $options
71
-     * @return string
72
-     */
73
-    public function toJson($options = 0)
74
-    {
75
-        return json_encode($this->toArray(), $options);
76
-    }
67
+	/**
68
+	 * Convert the entity instance to JSON.
69
+	 *
70
+	 * @param  int $options
71
+	 * @return string
72
+	 */
73
+	public function toJson($options = 0)
74
+	{
75
+		return json_encode($this->toArray(), $options);
76
+	}
77 77
 
78
-    /**
79
-     * Convert Mappable object to array;
80
-     *
81
-     * @return array
82
-     */
83
-    public function toArray()
84
-    {
85
-        return $this->attributesToArray($this->attributes);
86
-    }
78
+	/**
79
+	 * Convert Mappable object to array;
80
+	 *
81
+	 * @return array
82
+	 */
83
+	public function toArray()
84
+	{
85
+		return $this->attributesToArray($this->attributes);
86
+	}
87 87
 
88
-    /**
89
-     * Transform the Object to array/json,
90
-     *
91
-     * @param  array $sourceAttributes
92
-     * @return array
93
-     */
94
-    protected function attributesToArray(array $sourceAttributes)
95
-    {
96
-        $attributes = [];
88
+	/**
89
+	 * Transform the Object to array/json,
90
+	 *
91
+	 * @param  array $sourceAttributes
92
+	 * @return array
93
+	 */
94
+	protected function attributesToArray(array $sourceAttributes)
95
+	{
96
+		$attributes = [];
97 97
 
98
-        foreach ($sourceAttributes as $key => $attribute) {
99
-            // If the attribute is a proxy, and hasn't be loaded, we discard
100
-            // it from the returned set.
101
-            if ($attribute instanceof ProxyInterface && !$attribute->isProxyInitialized()) {
102
-                continue;
103
-            }
98
+		foreach ($sourceAttributes as $key => $attribute) {
99
+			// If the attribute is a proxy, and hasn't be loaded, we discard
100
+			// it from the returned set.
101
+			if ($attribute instanceof ProxyInterface && !$attribute->isProxyInitialized()) {
102
+				continue;
103
+			}
104 104
 
105
-            if ($attribute instanceof Carbon) {
106
-                $attributes[$key] = $attribute->__toString();
107
-                continue;
108
-            }
105
+			if ($attribute instanceof Carbon) {
106
+				$attributes[$key] = $attribute->__toString();
107
+				continue;
108
+			}
109 109
 
110
-            if ($attribute instanceof Arrayable) {
111
-                $attributes[$key] = $attribute->toArray();
112
-            } else {
113
-                $attributes[$key] = $attribute;
114
-            }
115
-        }
116
-        return $attributes;
117
-    }
110
+			if ($attribute instanceof Arrayable) {
111
+				$attributes[$key] = $attribute->toArray();
112
+			} else {
113
+				$attributes[$key] = $attribute;
114
+			}
115
+		}
116
+		return $attributes;
117
+	}
118 118
 }
Please login to merge, or discard this patch.
src/ValueObject.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -9,9 +9,9 @@
 block discarded – undo
9 9
 
10 10
 class ValueObject implements Mappable, ArrayAccess, Jsonable, JsonSerializable, Arrayable
11 11
 {
12
-    use MappableTrait;
13
-    use MagicGetters;
14
-    use MagicSetters;
15
-    use MagicCasting;
12
+	use MappableTrait;
13
+	use MagicGetters;
14
+	use MagicSetters;
15
+	use MagicCasting;
16 16
 }
17 17
 
Please login to merge, or discard this patch.