Completed
Branch master (5737c6)
by Rémi
02:51
created
src/System/MapperFactory.php 1 patch
Indentation   +74 added lines, -74 removed lines patch added patch discarded remove patch
@@ -13,78 +13,78 @@
 block discarded – undo
13 13
  */
14 14
 class MapperFactory
15 15
 {
16
-    /**
17
-     * Manager instance.
18
-     *
19
-     * @var \Analogue\ORM\System\Manager
20
-     */
21
-    protected $manager;
22
-
23
-    /**
24
-     * DriverManager instance.
25
-     *
26
-     * @var \Analogue\ORM\Drivers\Manager
27
-     */
28
-    protected $drivers;
29
-
30
-    /**
31
-     * Event dispatcher instance.
32
-     *
33
-     * @var \Illuminate\Contracts\Events\Dispatcher
34
-     */
35
-    protected $dispatcher;
36
-
37
-    /**
38
-     * MapperFactory constructor.
39
-     *
40
-     * @param DriverManager $drivers
41
-     * @param Dispatcher    $dispatcher
42
-     * @param Manager       $manager
43
-     */
44
-    public function __construct(DriverManager $drivers, Dispatcher $dispatcher, Manager $manager)
45
-    {
46
-        $this->drivers = $drivers;
47
-
48
-        $this->dispatcher = $dispatcher;
49
-
50
-        $this->manager = $manager;
51
-    }
52
-
53
-    /**
54
-     * Return a new Mapper instance.
55
-     *
56
-     * @param string    $entityClass
57
-     * @param EntityMap $entityMap
58
-     *
59
-     * @return Mapper
60
-     */
61
-    public function make($entityClass, EntityMap $entityMap)
62
-    {
63
-        $driver = $entityMap->getDriver();
64
-
65
-        $connection = $entityMap->getConnection();
66
-
67
-        $adapter = $this->drivers->getAdapter($driver, $connection);
68
-
69
-        $entityMap->setDateFormat($adapter->getDateFormat());
70
-
71
-        $mapper = new Mapper($entityMap, $adapter, $this->dispatcher, $this->manager);
72
-
73
-        // Fire Initializing Event
74
-        $mapper->fireEvent('initializing', $mapper);
75
-
76
-        // Proceed necessary parsing on the EntityMap object
77
-        $entityMap->initialize();
78
-
79
-        // Apply Inheritance scope, if necessary
80
-        if ($entityMap->getInheritanceType() == 'single_table') {
81
-            $scope = new SingleTableInheritanceScope($entityMap);
82
-            $mapper->addGlobalScope($scope);
83
-        }
84
-
85
-        // Fire Initialized Event
86
-        $mapper->fireEvent('initialized', $mapper);
87
-
88
-        return $mapper;
89
-    }
16
+	/**
17
+	 * Manager instance.
18
+	 *
19
+	 * @var \Analogue\ORM\System\Manager
20
+	 */
21
+	protected $manager;
22
+
23
+	/**
24
+	 * DriverManager instance.
25
+	 *
26
+	 * @var \Analogue\ORM\Drivers\Manager
27
+	 */
28
+	protected $drivers;
29
+
30
+	/**
31
+	 * Event dispatcher instance.
32
+	 *
33
+	 * @var \Illuminate\Contracts\Events\Dispatcher
34
+	 */
35
+	protected $dispatcher;
36
+
37
+	/**
38
+	 * MapperFactory constructor.
39
+	 *
40
+	 * @param DriverManager $drivers
41
+	 * @param Dispatcher    $dispatcher
42
+	 * @param Manager       $manager
43
+	 */
44
+	public function __construct(DriverManager $drivers, Dispatcher $dispatcher, Manager $manager)
45
+	{
46
+		$this->drivers = $drivers;
47
+
48
+		$this->dispatcher = $dispatcher;
49
+
50
+		$this->manager = $manager;
51
+	}
52
+
53
+	/**
54
+	 * Return a new Mapper instance.
55
+	 *
56
+	 * @param string    $entityClass
57
+	 * @param EntityMap $entityMap
58
+	 *
59
+	 * @return Mapper
60
+	 */
61
+	public function make($entityClass, EntityMap $entityMap)
62
+	{
63
+		$driver = $entityMap->getDriver();
64
+
65
+		$connection = $entityMap->getConnection();
66
+
67
+		$adapter = $this->drivers->getAdapter($driver, $connection);
68
+
69
+		$entityMap->setDateFormat($adapter->getDateFormat());
70
+
71
+		$mapper = new Mapper($entityMap, $adapter, $this->dispatcher, $this->manager);
72
+
73
+		// Fire Initializing Event
74
+		$mapper->fireEvent('initializing', $mapper);
75
+
76
+		// Proceed necessary parsing on the EntityMap object
77
+		$entityMap->initialize();
78
+
79
+		// Apply Inheritance scope, if necessary
80
+		if ($entityMap->getInheritanceType() == 'single_table') {
81
+			$scope = new SingleTableInheritanceScope($entityMap);
82
+			$mapper->addGlobalScope($scope);
83
+		}
84
+
85
+		// Fire Initialized Event
86
+		$mapper->fireEvent('initialized', $mapper);
87
+
88
+		return $mapper;
89
+	}
90 90
 }
Please login to merge, or discard this patch.
src/System/Mapper.php 2 patches
Indentation   +608 added lines, -608 removed lines patch added patch discarded remove patch
@@ -25,612 +25,612 @@
 block discarded – undo
25 25
  */
26 26
 class Mapper
27 27
 {
28
-    /**
29
-     * The Manager instance.
30
-     *
31
-     * @var \Analogue\ORM\System\Manager
32
-     */
33
-    protected $manager;
34
-
35
-    /**
36
-     * Instance of EntityMapper Object.
37
-     *
38
-     * @var \Analogue\ORM\EntityMap
39
-     */
40
-    protected $entityMap;
41
-
42
-    /**
43
-     * The instance of db adapter.
44
-     *
45
-     * @var \Analogue\ORM\Drivers\DBAdapter
46
-     */
47
-    protected $adapter;
48
-
49
-    /**
50
-     * Event dispatcher instance.
51
-     *
52
-     * @var \Illuminate\Contracts\Events\Dispatcher
53
-     */
54
-    protected $dispatcher;
55
-
56
-    /**
57
-     * Entity Cache.
58
-     *
59
-     * @var \Analogue\ORM\System\EntityCache
60
-     */
61
-    protected $cache;
62
-
63
-    /**
64
-     * Global scopes.
65
-     *
66
-     * @var array
67
-     */
68
-    protected $globalScopes = [];
69
-
70
-    /**
71
-     * Custom Commands.
72
-     *
73
-     * @var array
74
-     */
75
-    protected $customCommands = [];
76
-
77
-    /**
78
-     * @param EntityMap  $entityMap
79
-     * @param DBAdapter  $adapter
80
-     * @param Dispatcher $dispatcher
81
-     * @param Manager    $manager
82
-     */
83
-    public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher $dispatcher, Manager $manager)
84
-    {
85
-        $this->entityMap = $entityMap;
86
-
87
-        $this->adapter = $adapter;
88
-
89
-        $this->dispatcher = $dispatcher;
90
-
91
-        $this->manager = $manager;
92
-
93
-        $this->cache = new EntityCache($entityMap);
94
-    }
95
-
96
-    /**
97
-     * Map results to a Collection.
98
-     *
99
-     * @param Collection $results
100
-     *
101
-     * @return Collection
102
-     */
103
-    public function map($results)
104
-    {
105
-        // To implement, will allow decoupling result
106
-        // instantiation from Query class
107
-    }
108
-
109
-    /**
110
-     * Return all records for a mapped object.
111
-     *
112
-     * @return EntityCollection
113
-     */
114
-    public function all()
115
-    {
116
-        return $this->query()->get();
117
-    }
118
-
119
-    /**
120
-     * Persist an entity or an entity collection into the database.
121
-     *
122
-     * @param Mappable|\Traversable|array $entity
123
-     *
124
-     * @throws \InvalidArgumentException
125
-     * @throws MappingException
126
-     *
127
-     * @return Mappable|\Traversable|array
128
-     */
129
-    public function store($entity)
130
-    {
131
-        if ($this->manager->isTraversable($entity)) {
132
-            return $this->storeCollection($entity);
133
-        } else {
134
-            return $this->storeEntity($entity);
135
-        }
136
-    }
137
-
138
-    /**
139
-     * Store an entity collection inside a single DB Transaction.
140
-     *
141
-     * @param \Traversable|array $entities
142
-     *
143
-     * @throws \InvalidArgumentException
144
-     * @throws MappingException
145
-     *
146
-     * @return \Traversable|array
147
-     */
148
-    protected function storeCollection($entities)
149
-    {
150
-        $this->adapter->beginTransaction();
151
-
152
-        foreach ($entities as $entity) {
153
-            $this->storeEntity($entity);
154
-        }
155
-
156
-        $this->adapter->commit();
157
-
158
-        return $entities;
159
-    }
160
-
161
-    /**
162
-     * Store a single entity into the database.
163
-     *
164
-     * @param Mappable $entity
165
-     *
166
-     * @throws \InvalidArgumentException
167
-     * @throws MappingException
168
-     *
169
-     * @return \Analogue\ORM\Entity
170
-     */
171
-    protected function storeEntity($entity)
172
-    {
173
-        $this->checkEntityType($entity);
174
-
175
-        $store = new Store($this->aggregate($entity), $this->newQueryBuilder());
176
-
177
-        return $store->execute();
178
-    }
179
-
180
-    /**
181
-     * Check that the entity correspond to the current mapper.
182
-     *
183
-     * @param mixed $entity
184
-     *
185
-     * @throws InvalidArgumentException
186
-     *
187
-     * @return void
188
-     */
189
-    protected function checkEntityType($entity)
190
-    {
191
-        if (get_class($entity) != $this->entityMap->getClass() && !is_subclass_of($entity, $this->entityMap->getClass())) {
192
-            $expected = $this->entityMap->getClass();
193
-            $actual = get_class($entity);
194
-            throw new InvalidArgumentException("Expected : $expected, got $actual.");
195
-        }
196
-    }
197
-
198
-    /**
199
-     * Convert an entity into an aggregate root.
200
-     *
201
-     * @param mixed $entity
202
-     *
203
-     * @throws MappingException
204
-     *
205
-     * @return \Analogue\ORM\System\Aggregate
206
-     */
207
-    protected function aggregate($entity)
208
-    {
209
-        return new Aggregate($entity);
210
-    }
211
-
212
-    /**
213
-     * Get a the Underlying QueryAdapter.
214
-     *
215
-     * @return \Analogue\ORM\Drivers\QueryAdapter
216
-     */
217
-    public function newQueryBuilder()
218
-    {
219
-        return $this->adapter->getQuery();
220
-    }
221
-
222
-    /**
223
-     * Delete an entity or an entity collection from the database.
224
-     *
225
-     * @param  Mappable|\Traversable|array
226
-     *
227
-     * @throws MappingException
228
-     * @throws \InvalidArgumentException
229
-     *
230
-     * @return \Traversable|array
231
-     */
232
-    public function delete($entity)
233
-    {
234
-        if ($this->manager->isTraversable($entity)) {
235
-            return $this->deleteCollection($entity);
236
-        } else {
237
-            $this->deleteEntity($entity);
238
-        }
239
-    }
240
-
241
-    /**
242
-     * Delete an Entity Collection inside a single db transaction.
243
-     *
244
-     * @param \Traversable|array $entities
245
-     *
246
-     * @throws \InvalidArgumentException
247
-     * @throws MappingException
248
-     *
249
-     * @return \Traversable|array
250
-     */
251
-    protected function deleteCollection($entities)
252
-    {
253
-        $this->adapter->beginTransaction();
254
-
255
-        foreach ($entities as $entity) {
256
-            $this->deleteEntity($entity);
257
-        }
258
-
259
-        $this->adapter->commit();
260
-
261
-        return $entities;
262
-    }
263
-
264
-    /**
265
-     * Delete a single entity from the database.
266
-     *
267
-     * @param Mappable $entity
268
-     *
269
-     * @throws \InvalidArgumentException
270
-     * @throws MappingException
271
-     *
272
-     * @return void
273
-     */
274
-    protected function deleteEntity($entity)
275
-    {
276
-        $this->checkEntityType($entity);
277
-
278
-        $delete = new Delete($this->aggregate($entity), $this->newQueryBuilder());
279
-
280
-        $delete->execute();
281
-    }
282
-
283
-    /**
284
-     * Return the entity map for this mapper.
285
-     *
286
-     * @return EntityMap
287
-     */
288
-    public function getEntityMap()
289
-    {
290
-        return $this->entityMap;
291
-    }
292
-
293
-    /**
294
-     * Get the entity cache for the current mapper.
295
-     *
296
-     * @return EntityCache $entityCache
297
-     */
298
-    public function getEntityCache()
299
-    {
300
-        return $this->cache;
301
-    }
302
-
303
-    /**
304
-     * Fire the given event for the entity.
305
-     *
306
-     * @param string               $event
307
-     * @param \Analogue\ORM\Entity $entity
308
-     * @param bool                 $halt
309
-     *
310
-     * @throws InvalidArgumentException
311
-     *
312
-     * @return mixed
313
-     */
314
-    public function fireEvent($event, $entity, $halt = true)
315
-    {
316
-        if ($entity instanceof Wrapper) {
317
-            throw new InvalidArgumentException('Fired Event with invalid Entity Object');
318
-        }
319
-
320
-        $event = "analogue.{$event}.".$this->entityMap->getClass();
321
-
322
-        $method = $halt ? 'until' : 'fire';
323
-
324
-        return $this->dispatcher->$method($event, $entity);
325
-    }
326
-
327
-    /**
328
-     * Register an entity event with the dispatcher.
329
-     *
330
-     * @param string   $event
331
-     * @param \Closure $callback
332
-     *
333
-     * @return void
334
-     */
335
-    public function registerEvent($event, $callback)
336
-    {
337
-        $name = $this->entityMap->getClass();
338
-
339
-        $this->dispatcher->listen("analogue.{$event}.{$name}", $callback);
340
-    }
341
-
342
-    /**
343
-     * Add a global scope to this mapper query builder.
344
-     *
345
-     * @param ScopeInterface $scope
346
-     *
347
-     * @return void
348
-     */
349
-    public function addGlobalScope(ScopeInterface $scope)
350
-    {
351
-        $this->globalScopes[get_class($scope)] = $scope;
352
-    }
353
-
354
-    /**
355
-     * Determine if the mapper has a global scope.
356
-     *
357
-     * @param \Analogue\ORM\System\ScopeInterface $scope
358
-     *
359
-     * @return bool
360
-     */
361
-    public function hasGlobalScope($scope)
362
-    {
363
-        return !is_null($this->getGlobalScope($scope));
364
-    }
365
-
366
-    /**
367
-     * Get a global scope registered with the modal.
368
-     *
369
-     * @param \Analogue\ORM\System\ScopeInterface $scope
370
-     *
371
-     * @return \Analogue\ORM\System\ScopeInterface|null
372
-     */
373
-    public function getGlobalScope($scope)
374
-    {
375
-        return array_first($this->globalScopes, function ($key, $value) use ($scope) {
376
-            return $scope instanceof $value;
377
-        });
378
-    }
379
-
380
-    /**
381
-     * Get a new query instance without a given scope.
382
-     *
383
-     * @param \Analogue\ORM\System\ScopeInterface $scope
384
-     *
385
-     * @return \Analogue\ORM\System\Query
386
-     */
387
-    public function newQueryWithoutScope($scope)
388
-    {
389
-        $this->getGlobalScope($scope)->remove($query = $this->getQuery(), $this);
390
-
391
-        return $query;
392
-    }
393
-
394
-    /**
395
-     * Get the Analogue Query Builder for this instance.
396
-     *
397
-     * @return \Analogue\ORM\System\Query
398
-     */
399
-    public function getQuery()
400
-    {
401
-        $query = new Query($this, $this->adapter);
402
-
403
-        return $this->applyGlobalScopes($query);
404
-    }
405
-
406
-    /**
407
-     * Apply all of the global scopes to an Analogue Query builder.
408
-     *
409
-     * @param Query $query
410
-     *
411
-     * @return \Analogue\ORM\System\Query
412
-     */
413
-    public function applyGlobalScopes($query)
414
-    {
415
-        foreach ($this->getGlobalScopes() as $scope) {
416
-            $scope->apply($query, $this);
417
-        }
418
-
419
-        return $query;
420
-    }
421
-
422
-    /**
423
-     * Get the global scopes for this class instance.
424
-     *
425
-     * @return \Analogue\ORM\System\ScopeInterface
426
-     */
427
-    public function getGlobalScopes()
428
-    {
429
-        return $this->globalScopes;
430
-    }
431
-
432
-    /**
433
-     * Add a dynamic method that extends the mapper/repository.
434
-     *
435
-     * @param string $command
436
-     */
437
-    public function addCustomCommand($command)
438
-    {
439
-        $name = lcfirst(class_basename($command));
440
-
441
-        $this->customCommands[$name] = $command;
442
-    }
443
-
444
-    /**
445
-     * Create a new instance of the mapped entity class.
446
-     *
447
-     * @return mixed
448
-     */
449
-    public function newInstance()
450
-    {
451
-        $class = $this->entityMap->getClass();
452
-
453
-        if ($this->entityMap->useDependencyInjection()) {
454
-            return $this->newInstanceUsingDependencyInjection($class);
455
-        }
456
-
457
-        return $this->newInstanceUsingInstantiator($class);
458
-    }
459
-
460
-    /**
461
-     * Return a new object instance using dependency injection.
462
-     *
463
-     * @param string $class
464
-     *
465
-     * @return mixed
466
-     */
467
-    protected function newInstanceUsingDependencyInjection($class)
468
-    {
469
-        if (!class_exists(Container::class)) {
470
-            throw new ErrorException("Illuminate\Container\Container is required to use Dependency Injection");
471
-        }
472
-
473
-        return Container::getInstance()->make($class);
474
-    }
475
-
476
-    /**
477
-     * Return a new object instance using doctrine's instantiator.
478
-     *
479
-     * @param string $class
480
-     *
481
-     * @return mixed
482
-     */
483
-    protected function newInstanceUsingInstantiator($class)
484
-    {
485
-        $instantiator = new \Doctrine\Instantiator\Instantiator();
486
-
487
-        return $instantiator->instantiate($class);
488
-    }
489
-
490
-    /**
491
-     * Get an unscoped Analogue Query Builder for this instance.
492
-     *
493
-     * @return \Analogue\ORM\System\Query
494
-     */
495
-    public function globalQuery()
496
-    {
497
-        return $this->newQueryWithoutScopes();
498
-    }
499
-
500
-    /**
501
-     * Get a new query builder that doesn't have any global scopes.
502
-     *
503
-     * @return Query
504
-     */
505
-    public function newQueryWithoutScopes()
506
-    {
507
-        return $this->removeGlobalScopes($this->getQuery());
508
-    }
509
-
510
-    /**
511
-     * Remove all of the global scopes from an Analogue Query builder.
512
-     *
513
-     * @param Query $query
514
-     *
515
-     * @return \Analogue\ORM\System\Query
516
-     */
517
-    public function removeGlobalScopes($query)
518
-    {
519
-        foreach ($this->getGlobalScopes() as $scope) {
520
-            $scope->remove($query, $this);
521
-        }
522
-
523
-        return $query;
524
-    }
525
-
526
-    /**
527
-     * Return the manager instance.
528
-     *
529
-     * @return \Analogue\ORM\System\Manager
530
-     */
531
-    public function getManager()
532
-    {
533
-        return $this->manager;
534
-    }
535
-
536
-    /**
537
-     * Dynamically handle calls to custom commands, or Redirects to query().
538
-     *
539
-     * @param string $method
540
-     * @param array  $parameters
541
-     *
542
-     * @throws \Exception
543
-     *
544
-     * @return mixed
545
-     */
546
-    public function __call($method, $parameters)
547
-    {
548
-        // Check if method is a custom command on the mapper
549
-        if ($this->hasCustomCommand($method)) {
550
-            if (count($parameters) == 0) {
551
-                throw new \Exception("$method must at least have 1 argument");
552
-            }
553
-
554
-            return $this->executeCustomCommand($method, $parameters[0]);
555
-        }
556
-
557
-        // Redirect call on a new query instance
558
-        return call_user_func_array([$this->query(), $method], $parameters);
559
-    }
560
-
561
-    /**
562
-     * Check if this mapper supports this command.
563
-     *
564
-     * @param string $command
565
-     *
566
-     * @return bool
567
-     */
568
-    public function hasCustomCommand($command)
569
-    {
570
-        return in_array($command, $this->getCustomCommands());
571
-    }
572
-
573
-    /**
574
-     * Get all the custom commands registered on this mapper.
575
-     *
576
-     * @return array
577
-     */
578
-    public function getCustomCommands()
579
-    {
580
-        return array_keys($this->customCommands);
581
-    }
582
-
583
-    /**
584
-     * Execute a custom command on an Entity.
585
-     *
586
-     * @param string                 $command
587
-     * @param mixed|Collection|array $entity
588
-     *
589
-     * @throws \InvalidArgumentException
590
-     * @throws MappingException
591
-     *
592
-     * @return mixed
593
-     */
594
-    public function executeCustomCommand($command, $entity)
595
-    {
596
-        $commandClass = $this->customCommands[$command];
597
-
598
-        if ($this->manager->isTraversable($entity)) {
599
-            foreach ($entity as $instance) {
600
-                $this->executeSingleCustomCommand($commandClass, $instance);
601
-            }
602
-        } else {
603
-            return $this->executeSingleCustomCommand($commandClass, $entity);
604
-        }
605
-    }
606
-
607
-    /**
608
-     * Execute a single command instance.
609
-     *
610
-     * @param string $commandClass
611
-     * @param mixed  $entity
612
-     *
613
-     * @throws \InvalidArgumentException
614
-     * @throws MappingException
615
-     *
616
-     * @return mixed
617
-     */
618
-    protected function executeSingleCustomCommand($commandClass, $entity)
619
-    {
620
-        $this->checkEntityType($entity);
621
-
622
-        $instance = new $commandClass($this->aggregate($entity), $this->newQueryBuilder());
623
-
624
-        return $instance->execute();
625
-    }
626
-
627
-    /**
628
-     * Get the Analogue Query Builder for this instance.
629
-     *
630
-     * @return \Analogue\ORM\System\Query
631
-     */
632
-    public function query()
633
-    {
634
-        return $this->getQuery();
635
-    }
28
+	/**
29
+	 * The Manager instance.
30
+	 *
31
+	 * @var \Analogue\ORM\System\Manager
32
+	 */
33
+	protected $manager;
34
+
35
+	/**
36
+	 * Instance of EntityMapper Object.
37
+	 *
38
+	 * @var \Analogue\ORM\EntityMap
39
+	 */
40
+	protected $entityMap;
41
+
42
+	/**
43
+	 * The instance of db adapter.
44
+	 *
45
+	 * @var \Analogue\ORM\Drivers\DBAdapter
46
+	 */
47
+	protected $adapter;
48
+
49
+	/**
50
+	 * Event dispatcher instance.
51
+	 *
52
+	 * @var \Illuminate\Contracts\Events\Dispatcher
53
+	 */
54
+	protected $dispatcher;
55
+
56
+	/**
57
+	 * Entity Cache.
58
+	 *
59
+	 * @var \Analogue\ORM\System\EntityCache
60
+	 */
61
+	protected $cache;
62
+
63
+	/**
64
+	 * Global scopes.
65
+	 *
66
+	 * @var array
67
+	 */
68
+	protected $globalScopes = [];
69
+
70
+	/**
71
+	 * Custom Commands.
72
+	 *
73
+	 * @var array
74
+	 */
75
+	protected $customCommands = [];
76
+
77
+	/**
78
+	 * @param EntityMap  $entityMap
79
+	 * @param DBAdapter  $adapter
80
+	 * @param Dispatcher $dispatcher
81
+	 * @param Manager    $manager
82
+	 */
83
+	public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher $dispatcher, Manager $manager)
84
+	{
85
+		$this->entityMap = $entityMap;
86
+
87
+		$this->adapter = $adapter;
88
+
89
+		$this->dispatcher = $dispatcher;
90
+
91
+		$this->manager = $manager;
92
+
93
+		$this->cache = new EntityCache($entityMap);
94
+	}
95
+
96
+	/**
97
+	 * Map results to a Collection.
98
+	 *
99
+	 * @param Collection $results
100
+	 *
101
+	 * @return Collection
102
+	 */
103
+	public function map($results)
104
+	{
105
+		// To implement, will allow decoupling result
106
+		// instantiation from Query class
107
+	}
108
+
109
+	/**
110
+	 * Return all records for a mapped object.
111
+	 *
112
+	 * @return EntityCollection
113
+	 */
114
+	public function all()
115
+	{
116
+		return $this->query()->get();
117
+	}
118
+
119
+	/**
120
+	 * Persist an entity or an entity collection into the database.
121
+	 *
122
+	 * @param Mappable|\Traversable|array $entity
123
+	 *
124
+	 * @throws \InvalidArgumentException
125
+	 * @throws MappingException
126
+	 *
127
+	 * @return Mappable|\Traversable|array
128
+	 */
129
+	public function store($entity)
130
+	{
131
+		if ($this->manager->isTraversable($entity)) {
132
+			return $this->storeCollection($entity);
133
+		} else {
134
+			return $this->storeEntity($entity);
135
+		}
136
+	}
137
+
138
+	/**
139
+	 * Store an entity collection inside a single DB Transaction.
140
+	 *
141
+	 * @param \Traversable|array $entities
142
+	 *
143
+	 * @throws \InvalidArgumentException
144
+	 * @throws MappingException
145
+	 *
146
+	 * @return \Traversable|array
147
+	 */
148
+	protected function storeCollection($entities)
149
+	{
150
+		$this->adapter->beginTransaction();
151
+
152
+		foreach ($entities as $entity) {
153
+			$this->storeEntity($entity);
154
+		}
155
+
156
+		$this->adapter->commit();
157
+
158
+		return $entities;
159
+	}
160
+
161
+	/**
162
+	 * Store a single entity into the database.
163
+	 *
164
+	 * @param Mappable $entity
165
+	 *
166
+	 * @throws \InvalidArgumentException
167
+	 * @throws MappingException
168
+	 *
169
+	 * @return \Analogue\ORM\Entity
170
+	 */
171
+	protected function storeEntity($entity)
172
+	{
173
+		$this->checkEntityType($entity);
174
+
175
+		$store = new Store($this->aggregate($entity), $this->newQueryBuilder());
176
+
177
+		return $store->execute();
178
+	}
179
+
180
+	/**
181
+	 * Check that the entity correspond to the current mapper.
182
+	 *
183
+	 * @param mixed $entity
184
+	 *
185
+	 * @throws InvalidArgumentException
186
+	 *
187
+	 * @return void
188
+	 */
189
+	protected function checkEntityType($entity)
190
+	{
191
+		if (get_class($entity) != $this->entityMap->getClass() && !is_subclass_of($entity, $this->entityMap->getClass())) {
192
+			$expected = $this->entityMap->getClass();
193
+			$actual = get_class($entity);
194
+			throw new InvalidArgumentException("Expected : $expected, got $actual.");
195
+		}
196
+	}
197
+
198
+	/**
199
+	 * Convert an entity into an aggregate root.
200
+	 *
201
+	 * @param mixed $entity
202
+	 *
203
+	 * @throws MappingException
204
+	 *
205
+	 * @return \Analogue\ORM\System\Aggregate
206
+	 */
207
+	protected function aggregate($entity)
208
+	{
209
+		return new Aggregate($entity);
210
+	}
211
+
212
+	/**
213
+	 * Get a the Underlying QueryAdapter.
214
+	 *
215
+	 * @return \Analogue\ORM\Drivers\QueryAdapter
216
+	 */
217
+	public function newQueryBuilder()
218
+	{
219
+		return $this->adapter->getQuery();
220
+	}
221
+
222
+	/**
223
+	 * Delete an entity or an entity collection from the database.
224
+	 *
225
+	 * @param  Mappable|\Traversable|array
226
+	 *
227
+	 * @throws MappingException
228
+	 * @throws \InvalidArgumentException
229
+	 *
230
+	 * @return \Traversable|array
231
+	 */
232
+	public function delete($entity)
233
+	{
234
+		if ($this->manager->isTraversable($entity)) {
235
+			return $this->deleteCollection($entity);
236
+		} else {
237
+			$this->deleteEntity($entity);
238
+		}
239
+	}
240
+
241
+	/**
242
+	 * Delete an Entity Collection inside a single db transaction.
243
+	 *
244
+	 * @param \Traversable|array $entities
245
+	 *
246
+	 * @throws \InvalidArgumentException
247
+	 * @throws MappingException
248
+	 *
249
+	 * @return \Traversable|array
250
+	 */
251
+	protected function deleteCollection($entities)
252
+	{
253
+		$this->adapter->beginTransaction();
254
+
255
+		foreach ($entities as $entity) {
256
+			$this->deleteEntity($entity);
257
+		}
258
+
259
+		$this->adapter->commit();
260
+
261
+		return $entities;
262
+	}
263
+
264
+	/**
265
+	 * Delete a single entity from the database.
266
+	 *
267
+	 * @param Mappable $entity
268
+	 *
269
+	 * @throws \InvalidArgumentException
270
+	 * @throws MappingException
271
+	 *
272
+	 * @return void
273
+	 */
274
+	protected function deleteEntity($entity)
275
+	{
276
+		$this->checkEntityType($entity);
277
+
278
+		$delete = new Delete($this->aggregate($entity), $this->newQueryBuilder());
279
+
280
+		$delete->execute();
281
+	}
282
+
283
+	/**
284
+	 * Return the entity map for this mapper.
285
+	 *
286
+	 * @return EntityMap
287
+	 */
288
+	public function getEntityMap()
289
+	{
290
+		return $this->entityMap;
291
+	}
292
+
293
+	/**
294
+	 * Get the entity cache for the current mapper.
295
+	 *
296
+	 * @return EntityCache $entityCache
297
+	 */
298
+	public function getEntityCache()
299
+	{
300
+		return $this->cache;
301
+	}
302
+
303
+	/**
304
+	 * Fire the given event for the entity.
305
+	 *
306
+	 * @param string               $event
307
+	 * @param \Analogue\ORM\Entity $entity
308
+	 * @param bool                 $halt
309
+	 *
310
+	 * @throws InvalidArgumentException
311
+	 *
312
+	 * @return mixed
313
+	 */
314
+	public function fireEvent($event, $entity, $halt = true)
315
+	{
316
+		if ($entity instanceof Wrapper) {
317
+			throw new InvalidArgumentException('Fired Event with invalid Entity Object');
318
+		}
319
+
320
+		$event = "analogue.{$event}.".$this->entityMap->getClass();
321
+
322
+		$method = $halt ? 'until' : 'fire';
323
+
324
+		return $this->dispatcher->$method($event, $entity);
325
+	}
326
+
327
+	/**
328
+	 * Register an entity event with the dispatcher.
329
+	 *
330
+	 * @param string   $event
331
+	 * @param \Closure $callback
332
+	 *
333
+	 * @return void
334
+	 */
335
+	public function registerEvent($event, $callback)
336
+	{
337
+		$name = $this->entityMap->getClass();
338
+
339
+		$this->dispatcher->listen("analogue.{$event}.{$name}", $callback);
340
+	}
341
+
342
+	/**
343
+	 * Add a global scope to this mapper query builder.
344
+	 *
345
+	 * @param ScopeInterface $scope
346
+	 *
347
+	 * @return void
348
+	 */
349
+	public function addGlobalScope(ScopeInterface $scope)
350
+	{
351
+		$this->globalScopes[get_class($scope)] = $scope;
352
+	}
353
+
354
+	/**
355
+	 * Determine if the mapper has a global scope.
356
+	 *
357
+	 * @param \Analogue\ORM\System\ScopeInterface $scope
358
+	 *
359
+	 * @return bool
360
+	 */
361
+	public function hasGlobalScope($scope)
362
+	{
363
+		return !is_null($this->getGlobalScope($scope));
364
+	}
365
+
366
+	/**
367
+	 * Get a global scope registered with the modal.
368
+	 *
369
+	 * @param \Analogue\ORM\System\ScopeInterface $scope
370
+	 *
371
+	 * @return \Analogue\ORM\System\ScopeInterface|null
372
+	 */
373
+	public function getGlobalScope($scope)
374
+	{
375
+		return array_first($this->globalScopes, function ($key, $value) use ($scope) {
376
+			return $scope instanceof $value;
377
+		});
378
+	}
379
+
380
+	/**
381
+	 * Get a new query instance without a given scope.
382
+	 *
383
+	 * @param \Analogue\ORM\System\ScopeInterface $scope
384
+	 *
385
+	 * @return \Analogue\ORM\System\Query
386
+	 */
387
+	public function newQueryWithoutScope($scope)
388
+	{
389
+		$this->getGlobalScope($scope)->remove($query = $this->getQuery(), $this);
390
+
391
+		return $query;
392
+	}
393
+
394
+	/**
395
+	 * Get the Analogue Query Builder for this instance.
396
+	 *
397
+	 * @return \Analogue\ORM\System\Query
398
+	 */
399
+	public function getQuery()
400
+	{
401
+		$query = new Query($this, $this->adapter);
402
+
403
+		return $this->applyGlobalScopes($query);
404
+	}
405
+
406
+	/**
407
+	 * Apply all of the global scopes to an Analogue Query builder.
408
+	 *
409
+	 * @param Query $query
410
+	 *
411
+	 * @return \Analogue\ORM\System\Query
412
+	 */
413
+	public function applyGlobalScopes($query)
414
+	{
415
+		foreach ($this->getGlobalScopes() as $scope) {
416
+			$scope->apply($query, $this);
417
+		}
418
+
419
+		return $query;
420
+	}
421
+
422
+	/**
423
+	 * Get the global scopes for this class instance.
424
+	 *
425
+	 * @return \Analogue\ORM\System\ScopeInterface
426
+	 */
427
+	public function getGlobalScopes()
428
+	{
429
+		return $this->globalScopes;
430
+	}
431
+
432
+	/**
433
+	 * Add a dynamic method that extends the mapper/repository.
434
+	 *
435
+	 * @param string $command
436
+	 */
437
+	public function addCustomCommand($command)
438
+	{
439
+		$name = lcfirst(class_basename($command));
440
+
441
+		$this->customCommands[$name] = $command;
442
+	}
443
+
444
+	/**
445
+	 * Create a new instance of the mapped entity class.
446
+	 *
447
+	 * @return mixed
448
+	 */
449
+	public function newInstance()
450
+	{
451
+		$class = $this->entityMap->getClass();
452
+
453
+		if ($this->entityMap->useDependencyInjection()) {
454
+			return $this->newInstanceUsingDependencyInjection($class);
455
+		}
456
+
457
+		return $this->newInstanceUsingInstantiator($class);
458
+	}
459
+
460
+	/**
461
+	 * Return a new object instance using dependency injection.
462
+	 *
463
+	 * @param string $class
464
+	 *
465
+	 * @return mixed
466
+	 */
467
+	protected function newInstanceUsingDependencyInjection($class)
468
+	{
469
+		if (!class_exists(Container::class)) {
470
+			throw new ErrorException("Illuminate\Container\Container is required to use Dependency Injection");
471
+		}
472
+
473
+		return Container::getInstance()->make($class);
474
+	}
475
+
476
+	/**
477
+	 * Return a new object instance using doctrine's instantiator.
478
+	 *
479
+	 * @param string $class
480
+	 *
481
+	 * @return mixed
482
+	 */
483
+	protected function newInstanceUsingInstantiator($class)
484
+	{
485
+		$instantiator = new \Doctrine\Instantiator\Instantiator();
486
+
487
+		return $instantiator->instantiate($class);
488
+	}
489
+
490
+	/**
491
+	 * Get an unscoped Analogue Query Builder for this instance.
492
+	 *
493
+	 * @return \Analogue\ORM\System\Query
494
+	 */
495
+	public function globalQuery()
496
+	{
497
+		return $this->newQueryWithoutScopes();
498
+	}
499
+
500
+	/**
501
+	 * Get a new query builder that doesn't have any global scopes.
502
+	 *
503
+	 * @return Query
504
+	 */
505
+	public function newQueryWithoutScopes()
506
+	{
507
+		return $this->removeGlobalScopes($this->getQuery());
508
+	}
509
+
510
+	/**
511
+	 * Remove all of the global scopes from an Analogue Query builder.
512
+	 *
513
+	 * @param Query $query
514
+	 *
515
+	 * @return \Analogue\ORM\System\Query
516
+	 */
517
+	public function removeGlobalScopes($query)
518
+	{
519
+		foreach ($this->getGlobalScopes() as $scope) {
520
+			$scope->remove($query, $this);
521
+		}
522
+
523
+		return $query;
524
+	}
525
+
526
+	/**
527
+	 * Return the manager instance.
528
+	 *
529
+	 * @return \Analogue\ORM\System\Manager
530
+	 */
531
+	public function getManager()
532
+	{
533
+		return $this->manager;
534
+	}
535
+
536
+	/**
537
+	 * Dynamically handle calls to custom commands, or Redirects to query().
538
+	 *
539
+	 * @param string $method
540
+	 * @param array  $parameters
541
+	 *
542
+	 * @throws \Exception
543
+	 *
544
+	 * @return mixed
545
+	 */
546
+	public function __call($method, $parameters)
547
+	{
548
+		// Check if method is a custom command on the mapper
549
+		if ($this->hasCustomCommand($method)) {
550
+			if (count($parameters) == 0) {
551
+				throw new \Exception("$method must at least have 1 argument");
552
+			}
553
+
554
+			return $this->executeCustomCommand($method, $parameters[0]);
555
+		}
556
+
557
+		// Redirect call on a new query instance
558
+		return call_user_func_array([$this->query(), $method], $parameters);
559
+	}
560
+
561
+	/**
562
+	 * Check if this mapper supports this command.
563
+	 *
564
+	 * @param string $command
565
+	 *
566
+	 * @return bool
567
+	 */
568
+	public function hasCustomCommand($command)
569
+	{
570
+		return in_array($command, $this->getCustomCommands());
571
+	}
572
+
573
+	/**
574
+	 * Get all the custom commands registered on this mapper.
575
+	 *
576
+	 * @return array
577
+	 */
578
+	public function getCustomCommands()
579
+	{
580
+		return array_keys($this->customCommands);
581
+	}
582
+
583
+	/**
584
+	 * Execute a custom command on an Entity.
585
+	 *
586
+	 * @param string                 $command
587
+	 * @param mixed|Collection|array $entity
588
+	 *
589
+	 * @throws \InvalidArgumentException
590
+	 * @throws MappingException
591
+	 *
592
+	 * @return mixed
593
+	 */
594
+	public function executeCustomCommand($command, $entity)
595
+	{
596
+		$commandClass = $this->customCommands[$command];
597
+
598
+		if ($this->manager->isTraversable($entity)) {
599
+			foreach ($entity as $instance) {
600
+				$this->executeSingleCustomCommand($commandClass, $instance);
601
+			}
602
+		} else {
603
+			return $this->executeSingleCustomCommand($commandClass, $entity);
604
+		}
605
+	}
606
+
607
+	/**
608
+	 * Execute a single command instance.
609
+	 *
610
+	 * @param string $commandClass
611
+	 * @param mixed  $entity
612
+	 *
613
+	 * @throws \InvalidArgumentException
614
+	 * @throws MappingException
615
+	 *
616
+	 * @return mixed
617
+	 */
618
+	protected function executeSingleCustomCommand($commandClass, $entity)
619
+	{
620
+		$this->checkEntityType($entity);
621
+
622
+		$instance = new $commandClass($this->aggregate($entity), $this->newQueryBuilder());
623
+
624
+		return $instance->execute();
625
+	}
626
+
627
+	/**
628
+	 * Get the Analogue Query Builder for this instance.
629
+	 *
630
+	 * @return \Analogue\ORM\System\Query
631
+	 */
632
+	public function query()
633
+	{
634
+		return $this->getQuery();
635
+	}
636 636
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -372,7 +372,7 @@
 block discarded – undo
372 372
      */
373 373
     public function getGlobalScope($scope)
374 374
     {
375
-        return array_first($this->globalScopes, function ($key, $value) use ($scope) {
375
+        return array_first($this->globalScopes, function($key, $value) use ($scope) {
376 376
             return $scope instanceof $value;
377 377
         });
378 378
     }
Please login to merge, or discard this patch.
src/System/Query.php 2 patches
Indentation   +673 added lines, -673 removed lines patch added patch discarded remove patch
@@ -19,677 +19,677 @@
 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
-     *
124
-     * @return \Analogue\ORM\EntityCollection
125
-     */
126
-    public function get($columns = ['*'])
127
-    {
128
-        $entities = $this->getEntities($columns);
129
-
130
-        // TODO Should move the call to new Collection on the result builder
131
-        return $this->entityMap->newCollection($entities);
132
-    }
133
-
134
-    /**
135
-     * Find an entity by its primary key.
136
-     *
137
-     * @param string|int $id
138
-     * @param array      $columns
139
-     *
140
-     * @return \Analogue\ORM\Mappable
141
-     */
142
-    public function find($id, $columns = ['*'])
143
-    {
144
-        if (is_array($id)) {
145
-            return $this->findMany($id, $columns);
146
-        }
147
-
148
-        $this->query->where($this->entityMap->getKeyName(), '=', $id);
149
-
150
-        return $this->first($columns);
151
-    }
152
-
153
-    /**
154
-     * Find many entities by their primary keys.
155
-     *
156
-     * @param array $id
157
-     * @param array $columns
158
-     *
159
-     * @return EntityCollection
160
-     */
161
-    public function findMany($id, $columns = ['*'])
162
-    {
163
-        if (empty($id)) {
164
-            return new EntityCollection();
165
-        }
166
-
167
-        $this->query->whereIn($this->entityMap->getKeyName(), $id);
168
-
169
-        return $this->get($columns);
170
-    }
171
-
172
-    /**
173
-     * Find a model by its primary key or throw an exception.
174
-     *
175
-     * @param mixed $id
176
-     * @param array $columns
177
-     *
178
-     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
179
-     *
180
-     * @return mixed|self
181
-     */
182
-    public function findOrFail($id, $columns = ['*'])
183
-    {
184
-        if (!is_null($entity = $this->find($id, $columns))) {
185
-            return $entity;
186
-        }
187
-
188
-        throw (new EntityNotFoundException())->setEntity(get_class($this->entityMap));
189
-    }
190
-
191
-    /**
192
-     * Execute the query and get the first result.
193
-     *
194
-     * @param array $columns
195
-     *
196
-     * @return \Analogue\ORM\Entity
197
-     */
198
-    public function first($columns = ['*'])
199
-    {
200
-        return $this->take(1)->get($columns)->first();
201
-    }
202
-
203
-    /**
204
-     * Execute the query and get the first result or throw an exception.
205
-     *
206
-     * @param array $columns
207
-     *
208
-     * @throws EntityNotFoundException
209
-     *
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
-     *
226
-     * @return mixed
227
-     */
228
-    public function pluck($column)
229
-    {
230
-        $result = $this->first([$column]);
231
-
232
-        if ($result) {
233
-            return $result->{$column};
234
-        }
235
-    }
236
-
237
-    /**
238
-     * Chunk the results of the query.
239
-     *
240
-     * @param int      $count
241
-     * @param callable $callback
242
-     *
243
-     * @return void
244
-     */
245
-    public function chunk($count, callable $callback)
246
-    {
247
-        $results = $this->forPage($page = 1, $count)->get();
248
-
249
-        while (count($results) > 0) {
250
-            // On each chunk result set, we will pass them to the callback and then let the
251
-            // developer take care of everything within the callback, which allows us to
252
-            // keep the memory low for spinning through large result sets for working.
253
-            call_user_func($callback, $results);
254
-
255
-            $page++;
256
-
257
-            $results = $this->forPage($page, $count)->get();
258
-        }
259
-    }
260
-
261
-    /**
262
-     * Get an array with the values of a given column.
263
-     *
264
-     * @param string $column
265
-     * @param string $key
266
-     *
267
-     * @return array
268
-     */
269
-    public function lists($column, $key = null)
270
-    {
271
-        return $this->query->pluck($column, $key);
272
-    }
273
-
274
-    /**
275
-     * Get a paginator for the "select" statement.
276
-     *
277
-     * @param int   $perPage
278
-     * @param array $columns
279
-     *
280
-     * @return LengthAwarePaginator
281
-     */
282
-    public function paginate($perPage = null, $columns = ['*'])
283
-    {
284
-        $total = $this->query->getCountForPagination();
285
-
286
-        $this->query->forPage(
287
-            $page = Paginator::resolveCurrentPage(),
288
-            $perPage = $perPage ?: $this->entityMap->getPerPage()
289
-        );
290
-
291
-        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
292
-            'path' => Paginator::resolveCurrentPath(),
293
-        ]);
294
-    }
295
-
296
-    /**
297
-     * Get a paginator for a grouped statement.
298
-     *
299
-     * @param \Illuminate\Pagination\Factory $paginator
300
-     * @param int                            $perPage
301
-     * @param array                          $columns
302
-     *
303
-     * @return \Illuminate\Pagination\Paginator
304
-     */
305
-    protected function groupedPaginate($paginator, $perPage, $columns)
306
-    {
307
-        $results = $this->get($columns)->all();
308
-
309
-        return $this->query->buildRawPaginator($paginator, $results, $perPage);
310
-    }
311
-
312
-    /**
313
-     * Get a paginator for an ungrouped statement.
314
-     *
315
-     * @param \Illuminate\Pagination\Factory $paginator
316
-     * @param int                            $perPage
317
-     * @param array                          $columns
318
-     *
319
-     * @return \Illuminate\Pagination\Paginator
320
-     */
321
-    protected function ungroupedPaginate($paginator, $perPage, $columns)
322
-    {
323
-        $total = $this->query->getPaginationCount();
324
-
325
-        // Once we have the paginator we need to set the limit and offset values for
326
-        // the query so we can get the properly paginated items. Once we have an
327
-        // array of items we can create the paginator instances for the items.
328
-        $page = $paginator->getCurrentPage($total);
329
-
330
-        $this->query->forPage($page, $perPage);
331
-
332
-        return $paginator->make($this->get($columns)->all(), $total, $perPage);
333
-    }
334
-
335
-    /**
336
-     * Paginate the given query into a simple paginator.
337
-     *
338
-     * @param int   $perPage
339
-     * @param array $columns
340
-     *
341
-     * @return \Illuminate\Contracts\Pagination\Paginator
342
-     */
343
-    public function simplePaginate($perPage = null, $columns = ['*'])
344
-    {
345
-        $page = Paginator::resolveCurrentPage();
346
-
347
-        $perPage = $perPage ?: $this->entityMap->getPerPage();
348
-
349
-        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
350
-
351
-        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
352
-    }
353
-
354
-    /**
355
-     * Add a basic where clause to the query.
356
-     *
357
-     * @param string $column
358
-     * @param string $operator
359
-     * @param mixed  $value
360
-     * @param string $boolean
361
-     *
362
-     * @return $this
363
-     */
364
-    public function where($column, $operator = null, $value = null, $boolean = 'and')
365
-    {
366
-        if ($column instanceof Closure) {
367
-            $query = $this->newQueryWithoutScopes();
368
-
369
-            call_user_func($column, $query);
370
-
371
-            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
372
-        } else {
373
-            call_user_func_array([$this->query, 'where'], func_get_args());
374
-        }
375
-
376
-        return $this;
377
-    }
378
-
379
-    /**
380
-     * Add an "or where" clause to the query.
381
-     *
382
-     * @param string $column
383
-     * @param string $operator
384
-     * @param mixed  $value
385
-     *
386
-     * @return \Analogue\ORM\System\Query
387
-     */
388
-    public function orWhere($column, $operator = null, $value = null)
389
-    {
390
-        return $this->where($column, $operator, $value, 'or');
391
-    }
392
-
393
-    /**
394
-     * Add a relationship count condition to the query.
395
-     *
396
-     * @param string   $relation
397
-     * @param string   $operator
398
-     * @param int      $count
399
-     * @param string   $boolean
400
-     * @param \Closure $callback
401
-     *
402
-     * @return \Analogue\ORM\System\Query
403
-     */
404
-    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
405
-    {
406
-        $entity = $this->mapper->newInstance();
407
-
408
-        $relation = $this->getHasRelationQuery($relation, $entity);
409
-
410
-        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
411
-
412
-        if ($callback) {
413
-            call_user_func($callback, $query);
414
-        }
415
-
416
-        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
417
-    }
418
-
419
-    /**
420
-     * Add a relationship count condition to the query with where clauses.
421
-     *
422
-     * @param string   $relation
423
-     * @param \Closure $callback
424
-     * @param string   $operator
425
-     * @param int      $count
426
-     *
427
-     * @return \Analogue\ORM\System\Query
428
-     */
429
-    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
430
-    {
431
-        return $this->has($relation, $operator, $count, 'and', $callback);
432
-    }
433
-
434
-    /**
435
-     * Add a relationship count condition to the query with an "or".
436
-     *
437
-     * @param string $relation
438
-     * @param string $operator
439
-     * @param int    $count
440
-     *
441
-     * @return \Analogue\ORM\System\Query
442
-     */
443
-    public function orHas($relation, $operator = '>=', $count = 1)
444
-    {
445
-        return $this->has($relation, $operator, $count, 'or');
446
-    }
447
-
448
-    /**
449
-     * Add a relationship count condition to the query with where clauses and an "or".
450
-     *
451
-     * @param string   $relation
452
-     * @param \Closure $callback
453
-     * @param string   $operator
454
-     * @param int      $count
455
-     *
456
-     * @return \Analogue\ORM\System\Query
457
-     */
458
-    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
459
-    {
460
-        return $this->has($relation, $operator, $count, 'or', $callback);
461
-    }
462
-
463
-    /**
464
-     * Add the "has" condition where clause to the query.
465
-     *
466
-     * @param \Analogue\ORM\System\Query               $hasQuery
467
-     * @param \Analogue\ORM\Relationships\Relationship $relation
468
-     * @param string                                   $operator
469
-     * @param int                                      $count
470
-     * @param string                                   $boolean
471
-     *
472
-     * @return \Analogue\ORM\System\Query
473
-     */
474
-    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
475
-    {
476
-        $this->mergeWheresToHas($hasQuery, $relation);
477
-
478
-        if (is_numeric($count)) {
479
-            $count = new Expression($count);
480
-        }
481
-
482
-        return $this->where(new Expression('('.$hasQuery->toSql().')'), $operator, $count, $boolean);
483
-    }
484
-
485
-    /**
486
-     * Merge the "wheres" from a relation query to a has query.
487
-     *
488
-     * @param \Analogue\ORM\System\Query               $hasQuery
489
-     * @param \Analogue\ORM\Relationships\Relationship $relation
490
-     *
491
-     * @return void
492
-     */
493
-    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
494
-    {
495
-        // Here we have the "has" query and the original relation. We need to copy over any
496
-        // where clauses the developer may have put in the relationship function over to
497
-        // the has query, and then copy the bindings from the "has" query to the main.
498
-        $relationQuery = $relation->getBaseQuery();
499
-
500
-        $hasQuery->mergeWheres(
501
-            $relationQuery->wheres, $relationQuery->getBindings()
502
-        );
503
-
504
-        $this->query->mergeBindings($hasQuery->getQuery());
505
-    }
506
-
507
-    /**
508
-     * Get the "has relation" base query instance.
509
-     *
510
-     * @param string $relation
511
-     * @param        $entity
512
-     *
513
-     * @return \Analogue\ORM\System\Query
514
-     */
515
-    protected function getHasRelationQuery($relation, $entity)
516
-    {
517
-        return Relationship::noConstraints(function () use ($relation, $entity) {
518
-            return $this->entityMap->$relation($entity);
519
-        });
520
-    }
521
-
522
-    /**
523
-     * Get the table for the current query object.
524
-     *
525
-     * @return string
526
-     */
527
-    public function getTable()
528
-    {
529
-        return $this->entityMap->getTable();
530
-    }
531
-
532
-    /**
533
-     * Set the relationships that should be eager loaded.
534
-     *
535
-     * @param mixed $relations
536
-     *
537
-     * @return $this
538
-     */
539
-    public function with($relations)
540
-    {
541
-        if (is_string($relations)) {
542
-            $relations = func_get_args();
543
-        }
544
-
545
-        $this->eagerLoad = array_merge($this->eagerLoad, $relations);
546
-
547
-        return $this;
548
-    }
549
-
550
-    /**
551
-     * Get the relationships being eagerly loaded.
552
-     *
553
-     * @return array
554
-     */
555
-    public function getEagerLoads()
556
-    {
557
-        return $this->eagerLoad;
558
-    }
559
-
560
-    /**
561
-     * Add the Entity primary key if not in requested columns.
562
-     *
563
-     * @param array $columns
564
-     *
565
-     * @return array
566
-     */
567
-    protected function enforceIdColumn($columns)
568
-    {
569
-        if (!in_array($this->entityMap->getKeyName(), $columns)) {
570
-            $columns[] = $this->entityMap->getKeyName();
571
-        }
572
-
573
-        return $columns;
574
-    }
575
-
576
-    /**
577
-     * Get the hydrated models without eager loading.
578
-     *
579
-     * @param array $columns
580
-     *
581
-     * @return \Analogue\ORM\EntityCollection
582
-     */
583
-    public function getEntities($columns = ['*'])
584
-    {
585
-        // As we need the primary key to feed the
586
-        // entity cache, we need it loaded on each
587
-        // request
588
-        $columns = $this->enforceIdColumn($columns);
589
-
590
-        // Run the query
591
-        $results = $this->query->get($columns)->toArray();
592
-
593
-        // Create a result builder.
594
-        $builder = new ResultBuilder($this->mapper);
595
-
596
-        return $builder->build($results, $this->getEagerLoads());
597
-    }
598
-
599
-    /**
600
-     * Extend the builder with a given callback.
601
-     *
602
-     * @param string   $name
603
-     * @param \Closure $callback
604
-     *
605
-     * @return void
606
-     */
607
-    public function macro($name, Closure $callback)
608
-    {
609
-        $this->macros[$name] = $callback;
610
-    }
611
-
612
-    /**
613
-     * Get the given macro by name.
614
-     *
615
-     * @param string $name
616
-     *
617
-     * @return \Closure
618
-     */
619
-    public function getMacro($name)
620
-    {
621
-        return array_get($this->macros, $name);
622
-    }
623
-
624
-    /**
625
-     * Get a new query builder for the model's table.
626
-     *
627
-     * @return \Analogue\ORM\System\Query
628
-     */
629
-    public function newQuery()
630
-    {
631
-        $builder = new self($this->mapper, $this->adapter);
632
-
633
-        return $this->applyGlobalScopes($builder);
634
-    }
635
-
636
-    /**
637
-     * Get a new query builder without any scope applied.
638
-     *
639
-     * @return \Analogue\ORM\System\Query
640
-     */
641
-    public function newQueryWithoutScopes()
642
-    {
643
-        return new self($this->mapper, $this->adapter);
644
-    }
645
-
646
-    /**
647
-     * Get the Mapper instance for this Query Builder.
648
-     *
649
-     * @return \Analogue\ORM\System\Mapper
650
-     */
651
-    public function getMapper()
652
-    {
653
-        return $this->mapper;
654
-    }
655
-
656
-    /**
657
-     * Get the underlying query adapter.
658
-     *
659
-     * (REFACTOR: this method should move out, we need to provide the client classes
660
-     * with the adapter instead.)
661
-     *
662
-     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
663
-     */
664
-    public function getQuery()
665
-    {
666
-        return $this->query;
667
-    }
668
-
669
-    /**
670
-     * Dynamically handle calls into the query instance.
671
-     *
672
-     * @param string $method
673
-     * @param array  $parameters
674
-     *
675
-     * @throws Exception
676
-     *
677
-     * @return mixed
678
-     */
679
-    public function __call($method, $parameters)
680
-    {
681
-        if (isset($this->macros[$method])) {
682
-            array_unshift($parameters, $this);
683
-
684
-            return call_user_func_array($this->macros[$method], $parameters);
685
-        }
686
-
687
-        if (in_array($method, $this->blacklist)) {
688
-            throw new Exception("Method $method doesn't exist");
689
-        }
690
-
691
-        $result = call_user_func_array([$this->query, $method], $parameters);
692
-
693
-        return in_array($method, $this->passthru) ? $result : $this;
694
-    }
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
+	 *
124
+	 * @return \Analogue\ORM\EntityCollection
125
+	 */
126
+	public function get($columns = ['*'])
127
+	{
128
+		$entities = $this->getEntities($columns);
129
+
130
+		// TODO Should move the call to new Collection on the result builder
131
+		return $this->entityMap->newCollection($entities);
132
+	}
133
+
134
+	/**
135
+	 * Find an entity by its primary key.
136
+	 *
137
+	 * @param string|int $id
138
+	 * @param array      $columns
139
+	 *
140
+	 * @return \Analogue\ORM\Mappable
141
+	 */
142
+	public function find($id, $columns = ['*'])
143
+	{
144
+		if (is_array($id)) {
145
+			return $this->findMany($id, $columns);
146
+		}
147
+
148
+		$this->query->where($this->entityMap->getKeyName(), '=', $id);
149
+
150
+		return $this->first($columns);
151
+	}
152
+
153
+	/**
154
+	 * Find many entities by their primary keys.
155
+	 *
156
+	 * @param array $id
157
+	 * @param array $columns
158
+	 *
159
+	 * @return EntityCollection
160
+	 */
161
+	public function findMany($id, $columns = ['*'])
162
+	{
163
+		if (empty($id)) {
164
+			return new EntityCollection();
165
+		}
166
+
167
+		$this->query->whereIn($this->entityMap->getKeyName(), $id);
168
+
169
+		return $this->get($columns);
170
+	}
171
+
172
+	/**
173
+	 * Find a model by its primary key or throw an exception.
174
+	 *
175
+	 * @param mixed $id
176
+	 * @param array $columns
177
+	 *
178
+	 * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
179
+	 *
180
+	 * @return mixed|self
181
+	 */
182
+	public function findOrFail($id, $columns = ['*'])
183
+	{
184
+		if (!is_null($entity = $this->find($id, $columns))) {
185
+			return $entity;
186
+		}
187
+
188
+		throw (new EntityNotFoundException())->setEntity(get_class($this->entityMap));
189
+	}
190
+
191
+	/**
192
+	 * Execute the query and get the first result.
193
+	 *
194
+	 * @param array $columns
195
+	 *
196
+	 * @return \Analogue\ORM\Entity
197
+	 */
198
+	public function first($columns = ['*'])
199
+	{
200
+		return $this->take(1)->get($columns)->first();
201
+	}
202
+
203
+	/**
204
+	 * Execute the query and get the first result or throw an exception.
205
+	 *
206
+	 * @param array $columns
207
+	 *
208
+	 * @throws EntityNotFoundException
209
+	 *
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
+	 *
226
+	 * @return mixed
227
+	 */
228
+	public function pluck($column)
229
+	{
230
+		$result = $this->first([$column]);
231
+
232
+		if ($result) {
233
+			return $result->{$column};
234
+		}
235
+	}
236
+
237
+	/**
238
+	 * Chunk the results of the query.
239
+	 *
240
+	 * @param int      $count
241
+	 * @param callable $callback
242
+	 *
243
+	 * @return void
244
+	 */
245
+	public function chunk($count, callable $callback)
246
+	{
247
+		$results = $this->forPage($page = 1, $count)->get();
248
+
249
+		while (count($results) > 0) {
250
+			// On each chunk result set, we will pass them to the callback and then let the
251
+			// developer take care of everything within the callback, which allows us to
252
+			// keep the memory low for spinning through large result sets for working.
253
+			call_user_func($callback, $results);
254
+
255
+			$page++;
256
+
257
+			$results = $this->forPage($page, $count)->get();
258
+		}
259
+	}
260
+
261
+	/**
262
+	 * Get an array with the values of a given column.
263
+	 *
264
+	 * @param string $column
265
+	 * @param string $key
266
+	 *
267
+	 * @return array
268
+	 */
269
+	public function lists($column, $key = null)
270
+	{
271
+		return $this->query->pluck($column, $key);
272
+	}
273
+
274
+	/**
275
+	 * Get a paginator for the "select" statement.
276
+	 *
277
+	 * @param int   $perPage
278
+	 * @param array $columns
279
+	 *
280
+	 * @return LengthAwarePaginator
281
+	 */
282
+	public function paginate($perPage = null, $columns = ['*'])
283
+	{
284
+		$total = $this->query->getCountForPagination();
285
+
286
+		$this->query->forPage(
287
+			$page = Paginator::resolveCurrentPage(),
288
+			$perPage = $perPage ?: $this->entityMap->getPerPage()
289
+		);
290
+
291
+		return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
292
+			'path' => Paginator::resolveCurrentPath(),
293
+		]);
294
+	}
295
+
296
+	/**
297
+	 * Get a paginator for a grouped statement.
298
+	 *
299
+	 * @param \Illuminate\Pagination\Factory $paginator
300
+	 * @param int                            $perPage
301
+	 * @param array                          $columns
302
+	 *
303
+	 * @return \Illuminate\Pagination\Paginator
304
+	 */
305
+	protected function groupedPaginate($paginator, $perPage, $columns)
306
+	{
307
+		$results = $this->get($columns)->all();
308
+
309
+		return $this->query->buildRawPaginator($paginator, $results, $perPage);
310
+	}
311
+
312
+	/**
313
+	 * Get a paginator for an ungrouped statement.
314
+	 *
315
+	 * @param \Illuminate\Pagination\Factory $paginator
316
+	 * @param int                            $perPage
317
+	 * @param array                          $columns
318
+	 *
319
+	 * @return \Illuminate\Pagination\Paginator
320
+	 */
321
+	protected function ungroupedPaginate($paginator, $perPage, $columns)
322
+	{
323
+		$total = $this->query->getPaginationCount();
324
+
325
+		// Once we have the paginator we need to set the limit and offset values for
326
+		// the query so we can get the properly paginated items. Once we have an
327
+		// array of items we can create the paginator instances for the items.
328
+		$page = $paginator->getCurrentPage($total);
329
+
330
+		$this->query->forPage($page, $perPage);
331
+
332
+		return $paginator->make($this->get($columns)->all(), $total, $perPage);
333
+	}
334
+
335
+	/**
336
+	 * Paginate the given query into a simple paginator.
337
+	 *
338
+	 * @param int   $perPage
339
+	 * @param array $columns
340
+	 *
341
+	 * @return \Illuminate\Contracts\Pagination\Paginator
342
+	 */
343
+	public function simplePaginate($perPage = null, $columns = ['*'])
344
+	{
345
+		$page = Paginator::resolveCurrentPage();
346
+
347
+		$perPage = $perPage ?: $this->entityMap->getPerPage();
348
+
349
+		$this->skip(($page - 1) * $perPage)->take($perPage + 1);
350
+
351
+		return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
352
+	}
353
+
354
+	/**
355
+	 * Add a basic where clause to the query.
356
+	 *
357
+	 * @param string $column
358
+	 * @param string $operator
359
+	 * @param mixed  $value
360
+	 * @param string $boolean
361
+	 *
362
+	 * @return $this
363
+	 */
364
+	public function where($column, $operator = null, $value = null, $boolean = 'and')
365
+	{
366
+		if ($column instanceof Closure) {
367
+			$query = $this->newQueryWithoutScopes();
368
+
369
+			call_user_func($column, $query);
370
+
371
+			$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
372
+		} else {
373
+			call_user_func_array([$this->query, 'where'], func_get_args());
374
+		}
375
+
376
+		return $this;
377
+	}
378
+
379
+	/**
380
+	 * Add an "or where" clause to the query.
381
+	 *
382
+	 * @param string $column
383
+	 * @param string $operator
384
+	 * @param mixed  $value
385
+	 *
386
+	 * @return \Analogue\ORM\System\Query
387
+	 */
388
+	public function orWhere($column, $operator = null, $value = null)
389
+	{
390
+		return $this->where($column, $operator, $value, 'or');
391
+	}
392
+
393
+	/**
394
+	 * Add a relationship count condition to the query.
395
+	 *
396
+	 * @param string   $relation
397
+	 * @param string   $operator
398
+	 * @param int      $count
399
+	 * @param string   $boolean
400
+	 * @param \Closure $callback
401
+	 *
402
+	 * @return \Analogue\ORM\System\Query
403
+	 */
404
+	public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
405
+	{
406
+		$entity = $this->mapper->newInstance();
407
+
408
+		$relation = $this->getHasRelationQuery($relation, $entity);
409
+
410
+		$query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
411
+
412
+		if ($callback) {
413
+			call_user_func($callback, $query);
414
+		}
415
+
416
+		return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
417
+	}
418
+
419
+	/**
420
+	 * Add a relationship count condition to the query with where clauses.
421
+	 *
422
+	 * @param string   $relation
423
+	 * @param \Closure $callback
424
+	 * @param string   $operator
425
+	 * @param int      $count
426
+	 *
427
+	 * @return \Analogue\ORM\System\Query
428
+	 */
429
+	public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
430
+	{
431
+		return $this->has($relation, $operator, $count, 'and', $callback);
432
+	}
433
+
434
+	/**
435
+	 * Add a relationship count condition to the query with an "or".
436
+	 *
437
+	 * @param string $relation
438
+	 * @param string $operator
439
+	 * @param int    $count
440
+	 *
441
+	 * @return \Analogue\ORM\System\Query
442
+	 */
443
+	public function orHas($relation, $operator = '>=', $count = 1)
444
+	{
445
+		return $this->has($relation, $operator, $count, 'or');
446
+	}
447
+
448
+	/**
449
+	 * Add a relationship count condition to the query with where clauses and an "or".
450
+	 *
451
+	 * @param string   $relation
452
+	 * @param \Closure $callback
453
+	 * @param string   $operator
454
+	 * @param int      $count
455
+	 *
456
+	 * @return \Analogue\ORM\System\Query
457
+	 */
458
+	public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
459
+	{
460
+		return $this->has($relation, $operator, $count, 'or', $callback);
461
+	}
462
+
463
+	/**
464
+	 * Add the "has" condition where clause to the query.
465
+	 *
466
+	 * @param \Analogue\ORM\System\Query               $hasQuery
467
+	 * @param \Analogue\ORM\Relationships\Relationship $relation
468
+	 * @param string                                   $operator
469
+	 * @param int                                      $count
470
+	 * @param string                                   $boolean
471
+	 *
472
+	 * @return \Analogue\ORM\System\Query
473
+	 */
474
+	protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
475
+	{
476
+		$this->mergeWheresToHas($hasQuery, $relation);
477
+
478
+		if (is_numeric($count)) {
479
+			$count = new Expression($count);
480
+		}
481
+
482
+		return $this->where(new Expression('('.$hasQuery->toSql().')'), $operator, $count, $boolean);
483
+	}
484
+
485
+	/**
486
+	 * Merge the "wheres" from a relation query to a has query.
487
+	 *
488
+	 * @param \Analogue\ORM\System\Query               $hasQuery
489
+	 * @param \Analogue\ORM\Relationships\Relationship $relation
490
+	 *
491
+	 * @return void
492
+	 */
493
+	protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
494
+	{
495
+		// Here we have the "has" query and the original relation. We need to copy over any
496
+		// where clauses the developer may have put in the relationship function over to
497
+		// the has query, and then copy the bindings from the "has" query to the main.
498
+		$relationQuery = $relation->getBaseQuery();
499
+
500
+		$hasQuery->mergeWheres(
501
+			$relationQuery->wheres, $relationQuery->getBindings()
502
+		);
503
+
504
+		$this->query->mergeBindings($hasQuery->getQuery());
505
+	}
506
+
507
+	/**
508
+	 * Get the "has relation" base query instance.
509
+	 *
510
+	 * @param string $relation
511
+	 * @param        $entity
512
+	 *
513
+	 * @return \Analogue\ORM\System\Query
514
+	 */
515
+	protected function getHasRelationQuery($relation, $entity)
516
+	{
517
+		return Relationship::noConstraints(function () use ($relation, $entity) {
518
+			return $this->entityMap->$relation($entity);
519
+		});
520
+	}
521
+
522
+	/**
523
+	 * Get the table for the current query object.
524
+	 *
525
+	 * @return string
526
+	 */
527
+	public function getTable()
528
+	{
529
+		return $this->entityMap->getTable();
530
+	}
531
+
532
+	/**
533
+	 * Set the relationships that should be eager loaded.
534
+	 *
535
+	 * @param mixed $relations
536
+	 *
537
+	 * @return $this
538
+	 */
539
+	public function with($relations)
540
+	{
541
+		if (is_string($relations)) {
542
+			$relations = func_get_args();
543
+		}
544
+
545
+		$this->eagerLoad = array_merge($this->eagerLoad, $relations);
546
+
547
+		return $this;
548
+	}
549
+
550
+	/**
551
+	 * Get the relationships being eagerly loaded.
552
+	 *
553
+	 * @return array
554
+	 */
555
+	public function getEagerLoads()
556
+	{
557
+		return $this->eagerLoad;
558
+	}
559
+
560
+	/**
561
+	 * Add the Entity primary key if not in requested columns.
562
+	 *
563
+	 * @param array $columns
564
+	 *
565
+	 * @return array
566
+	 */
567
+	protected function enforceIdColumn($columns)
568
+	{
569
+		if (!in_array($this->entityMap->getKeyName(), $columns)) {
570
+			$columns[] = $this->entityMap->getKeyName();
571
+		}
572
+
573
+		return $columns;
574
+	}
575
+
576
+	/**
577
+	 * Get the hydrated models without eager loading.
578
+	 *
579
+	 * @param array $columns
580
+	 *
581
+	 * @return \Analogue\ORM\EntityCollection
582
+	 */
583
+	public function getEntities($columns = ['*'])
584
+	{
585
+		// As we need the primary key to feed the
586
+		// entity cache, we need it loaded on each
587
+		// request
588
+		$columns = $this->enforceIdColumn($columns);
589
+
590
+		// Run the query
591
+		$results = $this->query->get($columns)->toArray();
592
+
593
+		// Create a result builder.
594
+		$builder = new ResultBuilder($this->mapper);
595
+
596
+		return $builder->build($results, $this->getEagerLoads());
597
+	}
598
+
599
+	/**
600
+	 * Extend the builder with a given callback.
601
+	 *
602
+	 * @param string   $name
603
+	 * @param \Closure $callback
604
+	 *
605
+	 * @return void
606
+	 */
607
+	public function macro($name, Closure $callback)
608
+	{
609
+		$this->macros[$name] = $callback;
610
+	}
611
+
612
+	/**
613
+	 * Get the given macro by name.
614
+	 *
615
+	 * @param string $name
616
+	 *
617
+	 * @return \Closure
618
+	 */
619
+	public function getMacro($name)
620
+	{
621
+		return array_get($this->macros, $name);
622
+	}
623
+
624
+	/**
625
+	 * Get a new query builder for the model's table.
626
+	 *
627
+	 * @return \Analogue\ORM\System\Query
628
+	 */
629
+	public function newQuery()
630
+	{
631
+		$builder = new self($this->mapper, $this->adapter);
632
+
633
+		return $this->applyGlobalScopes($builder);
634
+	}
635
+
636
+	/**
637
+	 * Get a new query builder without any scope applied.
638
+	 *
639
+	 * @return \Analogue\ORM\System\Query
640
+	 */
641
+	public function newQueryWithoutScopes()
642
+	{
643
+		return new self($this->mapper, $this->adapter);
644
+	}
645
+
646
+	/**
647
+	 * Get the Mapper instance for this Query Builder.
648
+	 *
649
+	 * @return \Analogue\ORM\System\Mapper
650
+	 */
651
+	public function getMapper()
652
+	{
653
+		return $this->mapper;
654
+	}
655
+
656
+	/**
657
+	 * Get the underlying query adapter.
658
+	 *
659
+	 * (REFACTOR: this method should move out, we need to provide the client classes
660
+	 * with the adapter instead.)
661
+	 *
662
+	 * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
663
+	 */
664
+	public function getQuery()
665
+	{
666
+		return $this->query;
667
+	}
668
+
669
+	/**
670
+	 * Dynamically handle calls into the query instance.
671
+	 *
672
+	 * @param string $method
673
+	 * @param array  $parameters
674
+	 *
675
+	 * @throws Exception
676
+	 *
677
+	 * @return mixed
678
+	 */
679
+	public function __call($method, $parameters)
680
+	{
681
+		if (isset($this->macros[$method])) {
682
+			array_unshift($parameters, $this);
683
+
684
+			return call_user_func_array($this->macros[$method], $parameters);
685
+		}
686
+
687
+		if (in_array($method, $this->blacklist)) {
688
+			throw new Exception("Method $method doesn't exist");
689
+		}
690
+
691
+		$result = call_user_func_array([$this->query, $method], $parameters);
692
+
693
+		return in_array($method, $this->passthru) ? $result : $this;
694
+	}
695 695
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -514,7 +514,7 @@
 block discarded – undo
514 514
      */
515 515
     protected function getHasRelationQuery($relation, $entity)
516 516
     {
517
-        return Relationship::noConstraints(function () use ($relation, $entity) {
517
+        return Relationship::noConstraints(function() use ($relation, $entity) {
518 518
             return $this->entityMap->$relation($entity);
519 519
         });
520 520
     }
Please login to merge, or discard this patch.
src/System/ScopeInterface.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -4,21 +4,21 @@
 block discarded – undo
4 4
 
5 5
 interface ScopeInterface
6 6
 {
7
-    /**
8
-     * Apply the scope to a given Query builder.
9
-     *
10
-     * @param \Analogue\ORM\System\Query $builder
11
-     *
12
-     * @return void
13
-     */
14
-    public function apply(Query $builder);
7
+	/**
8
+	 * Apply the scope to a given Query builder.
9
+	 *
10
+	 * @param \Analogue\ORM\System\Query $builder
11
+	 *
12
+	 * @return void
13
+	 */
14
+	public function apply(Query $builder);
15 15
 
16
-    /**
17
-     * Remove the scope from the Query builder.
18
-     *
19
-     * @param \Analogue\ORM\System\Query $builder
20
-     *
21
-     * @return void
22
-     */
23
-    public function remove(Query $builder);
16
+	/**
17
+	 * Remove the scope from the Query builder.
18
+	 *
19
+	 * @param \Analogue\ORM\System\Query $builder
20
+	 *
21
+	 * @return void
22
+	 */
23
+	public function remove(Query $builder);
24 24
 }
Please login to merge, or discard this patch.
src/System/Aggregate.php 2 patches
Indentation   +1115 added lines, -1115 removed lines patch added patch discarded remove patch
@@ -16,1119 +16,1119 @@
 block discarded – undo
16 16
  */
17 17
 class Aggregate implements InternallyMappable
18 18
 {
19
-    /**
20
-     * The Root Entity.
21
-     *
22
-     * @var \Analogue\ORM\System\Wrappers\Wrapper
23
-     */
24
-    protected $wrappedEntity;
25
-
26
-    /**
27
-     * Class of the entity being aggregated.
28
-     *
29
-     * @var string
30
-     */
31
-    protected $class;
32
-
33
-    /**
34
-     * Parent Root Aggregate.
35
-     *
36
-     * @var \Analogue\ORM\System\Aggregate
37
-     */
38
-    protected $parent;
39
-
40
-    /**
41
-     * Parent's relationship method.
42
-     *
43
-     * @var string
44
-     */
45
-    protected $parentRelationship;
46
-
47
-    /**
48
-     * Root Entity.
49
-     *
50
-     * @var \Analogue\ORM\System\Aggregate
51
-     */
52
-    protected $root;
53
-
54
-    /**
55
-     * An associative array containing entity's
56
-     * relationships converted to Aggregates.
57
-     *
58
-     * @var array
59
-     */
60
-    protected $relationships = [];
61
-
62
-    /**
63
-     * Relationship that need post-command synchronization.
64
-     *
65
-     * @var array
66
-     */
67
-    protected $needSync = [];
68
-
69
-    /**
70
-     * Mapper.
71
-     *
72
-     * @var \Analogue\ORM\System\Mapper;
73
-     */
74
-    protected $mapper;
75
-
76
-    /**
77
-     * Entity Map.
78
-     *
79
-     * @var \Analogue\ORM\EntityMap;
80
-     */
81
-    protected $entityMap;
82
-
83
-    /**
84
-     * Create a new Aggregated Entity instance.
85
-     *
86
-     * @param mixed          $entity
87
-     * @param Aggregate|null $parent
88
-     * @param string         $parentRelationship
89
-     * @param Aggregate|null $root
90
-     *
91
-     * @throws MappingException
92
-     */
93
-    public function __construct($entity, Aggregate $parent = null, $parentRelationship = null, Aggregate $root = null)
94
-    {
95
-        $factory = new Factory();
96
-
97
-        $this->class = get_class($entity);
98
-
99
-        $this->wrappedEntity = $factory->make($entity);
100
-
101
-        $this->parent = $parent;
102
-
103
-        $this->parentRelationship = $parentRelationship;
104
-
105
-        $this->root = $root;
106
-
107
-        $mapper = $this->getMapper($entity);
108
-
109
-        $this->entityMap = $mapper->getEntityMap();
110
-
111
-        $this->parseRelationships();
112
-    }
113
-
114
-    /**
115
-     * Parse Every relationships defined on the entity.
116
-     *
117
-     * @throws MappingException
118
-     *
119
-     * @return void
120
-     */
121
-    protected function parseRelationships()
122
-    {
123
-        foreach ($this->entityMap->getSingleRelationships() as $relation) {
124
-            $this->parseSingleRelationship($relation);
125
-        }
126
-
127
-        foreach ($this->entityMap->getManyRelationships() as $relation) {
128
-            $this->parseManyRelationship($relation);
129
-        }
130
-    }
131
-
132
-    /**
133
-     * Parse for values common to single & many relations.
134
-     *
135
-     * @param string $relation
136
-     *
137
-     * @throws MappingException
138
-     *
139
-     * @return mixed|bool
140
-     */
141
-    protected function parseForCommonValues($relation)
142
-    {
143
-        if (!$this->hasAttribute($relation)) {
144
-            // If no attribute exists for this relationships
145
-            // we'll make it a simple empty array. This will
146
-            // save us from constantly checking for the attributes
147
-            // actual existence.
148
-            $this->relationships[$relation] = [];
149
-
150
-            return false;
151
-        }
152
-
153
-        $value = $this->getRelationshipValue($relation);
154
-
155
-        if (is_null($value)) {
156
-            $this->relationships[$relation] = [];
157
-
158
-            // If the relationship's content is the null value
159
-            // and the Entity's exist in DB, we'll interpret this
160
-            // as the need to detach all related Entities,
161
-            // therefore a sync operation is needed.
162
-            $this->needSync[] = $relation;
163
-
164
-            return false;
165
-        }
166
-
167
-        return $value;
168
-    }
169
-
170
-    /**
171
-     * Parse a 'single' relationship.
172
-     *
173
-     * @param string $relation
174
-     *
175
-     * @throws MappingException
176
-     *
177
-     * @return bool
178
-     */
179
-    protected function parseSingleRelationship($relation)
180
-    {
181
-        if (!$value = $this->parseForCommonValues($relation)) {
182
-            return true;
183
-        }
184
-
185
-        if ($value instanceof Collection || is_array($value) || $value instanceof CollectionProxy) {
186
-            throw new MappingException("Entity's attribute $relation should not be array, or collection");
187
-        }
188
-
189
-        if ($value instanceof LazyLoadingInterface && !$value->isProxyInitialized()) {
190
-            $this->relationships[$relation] = [];
191
-
192
-            return true;
193
-        }
194
-
195
-        // If the attribute is a loaded proxy, swap it for its
196
-        // loaded entity.
197
-        if ($value instanceof LazyLoadingInterface && $value->isProxyInitialized()) {
198
-            $value = $value->getWrappedValueHolderValue();
199
-        }
200
-
201
-        if ($this->isParentOrRoot($value)) {
202
-            $this->relationships[$relation] = [];
203
-
204
-            return true;
205
-        }
206
-
207
-        // At this point, we can assume the attribute is an Entity instance
208
-        // so we'll treat it as such.
209
-        $subAggregate = $this->createSubAggregate($value, $relation);
210
-
211
-        // Even if it's a single entity, we'll store it as an array
212
-        // just for consistency with other relationships
213
-        $this->relationships[$relation] = [$subAggregate];
214
-
215
-        // We always need to check a loaded relation is in sync
216
-        // with its local key
217
-        $this->needSync[] = $relation;
218
-
219
-        return true;
220
-    }
221
-
222
-    /**
223
-     * Check if value isn't parent or root in the aggregate.
224
-     *
225
-     * @param  mixed
226
-     *
227
-     * @return bool|null
228
-     */
229
-    protected function isParentOrRoot($value)
230
-    {
231
-        if (!is_null($this->root)) {
232
-            $rootClass = get_class($this->root->getEntityObject());
233
-            if ($rootClass == get_class($value)) {
234
-                return true;
235
-            }
236
-        }
237
-
238
-        if (!is_null($this->parent)) {
239
-            $parentClass = get_class($this->parent->getEntityObject());
240
-            if ($parentClass == get_class($value)) {
241
-                return true;
242
-            }
243
-        }
244
-    }
245
-
246
-    /**
247
-     * Parse a 'many' relationship.
248
-     *
249
-     * @param string $relation
250
-     *
251
-     * @throws MappingException
252
-     *
253
-     * @return bool
254
-     */
255
-    protected function parseManyRelationship($relation)
256
-    {
257
-        if (!$value = $this->parseForCommonValues($relation)) {
258
-            return true;
259
-        }
260
-
261
-        if (is_array($value) || (!$value instanceof CollectionProxy && $value instanceof Collection)) {
262
-            $this->needSync[] = $relation;
263
-        }
264
-
265
-        // If the relation is a proxy, we test is the relation
266
-        // has been lazy loaded, otherwise we'll just treat
267
-        // the subset of newly added items.
268
-        if ($value instanceof CollectionProxy && $value->isProxyInitialized()) {
269
-            $this->needSync[] = $relation;
270
-            //$value = $value->getUnderlyingCollection();
271
-        }
272
-
273
-        if ($value instanceof CollectionProxy && !$value->isProxyInitialized()) {
274
-            $value = $value->getAddedItems();
275
-        }
276
-
277
-        // At this point $value should be either an array or an instance
278
-        // of a collection class.
279
-        if (!is_array($value) && !$value instanceof Collection) {
280
-            throw new MappingException("'$relation' attribute should be array() or Collection");
281
-        }
282
-
283
-        $this->relationships[$relation] = $this->createSubAggregates($value, $relation);
284
-
285
-        return true;
286
-    }
287
-
288
-    /**
289
-     * Return Entity's relationship attribute.
290
-     *
291
-     * @param string $relation
292
-     *
293
-     * @throws MappingException
294
-     *
295
-     * @return mixed
296
-     */
297
-    protected function getRelationshipValue($relation)
298
-    {
299
-        $value = $this->getEntityAttribute($relation);
300
-        //if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
301
-        if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
302
-            throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
303
-        }
304
-
305
-        return $value;
306
-    }
307
-
308
-    /**
309
-     * Create a child, aggregated entity.
310
-     *
311
-     * @param mixed  $entities
312
-     * @param string $relation
313
-     *
314
-     * @return array
315
-     */
316
-    protected function createSubAggregates($entities, $relation)
317
-    {
318
-        $aggregates = [];
319
-
320
-        foreach ($entities as $entity) {
321
-            $aggregates[] = $this->createSubAggregate($entity, $relation);
322
-        }
323
-
324
-        return $aggregates;
325
-    }
326
-
327
-    /**
328
-     * Create a related subAggregate.
329
-     *
330
-     * @param mixed  $entity
331
-     * @param string $relation
332
-     *
333
-     * @throws MappingException
334
-     *
335
-     * @return self
336
-     */
337
-    protected function createSubAggregate($entity, $relation)
338
-    {
339
-        // If root isn't defined, then this is the Aggregate Root
340
-        if (is_null($this->root)) {
341
-            $root = $this;
342
-        } else {
343
-            $root = $this->root;
344
-        }
345
-
346
-        return new self($entity, $this, $relation, $root);
347
-    }
348
-
349
-    /**
350
-     * Get the Entity's primary key attribute.
351
-     *
352
-     * @return string|int
353
-     */
354
-    public function getEntityId()
355
-    {
356
-        return $this->wrappedEntity->getEntityKey();
357
-    }
358
-
359
-    /**
360
-     * Get the name of the primary key.
361
-     *
362
-     * @return string
363
-     */
364
-    public function getEntityKey()
365
-    {
366
-        return $this->entityMap->getKeyName();
367
-    }
368
-
369
-    /**
370
-     * Return the entity map for the current entity.
371
-     *
372
-     * @return \Analogue\ORM\EntityMap
373
-     */
374
-    public function getEntityMap()
375
-    {
376
-        return $this->entityMap;
377
-    }
378
-
379
-    /**
380
-     * Return the Entity's hash $class.$id.
381
-     *
382
-     * @return string
383
-     */
384
-    public function getEntityHash()
385
-    {
386
-        return $this->getEntityClass().'.'.$this->getEntityId();
387
-    }
388
-
389
-    /**
390
-     * Get wrapped entity class.
391
-     *
392
-     * @return string
393
-     */
394
-    public function getEntityClass()
395
-    {
396
-        return $this->entityMap->getClass();
397
-    }
398
-
399
-    /**
400
-     * Return the Mapper's entity cache.
401
-     *
402
-     * @return \Analogue\ORM\System\EntityCache
403
-     */
404
-    protected function getEntityCache()
405
-    {
406
-        return $this->getMapper()->getEntityCache();
407
-    }
408
-
409
-    /**
410
-     * Get a relationship as an aggregated entities' array.
411
-     *
412
-     * @param string $name
413
-     *
414
-     * @return array
415
-     */
416
-    public function getRelationship($name)
417
-    {
418
-        if (array_key_exists($name, $this->relationships)) {
419
-            return $this->relationships[$name];
420
-        } else {
421
-            return [];
422
-        }
423
-    }
424
-
425
-    /**
426
-     * [TO IMPLEMENT].
427
-     *
428
-     * @return array
429
-     */
430
-    public function getPivotAttributes()
431
-    {
432
-        return [];
433
-    }
434
-
435
-    /**
436
-     * Get Non existing related entities from several relationships.
437
-     *
438
-     * @param array $relationships
439
-     *
440
-     * @return array
441
-     */
442
-    public function getNonExistingRelated(array $relationships)
443
-    {
444
-        $nonExisting = [];
445
-
446
-        foreach ($relationships as $relation) {
447
-            if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
448
-                $nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
449
-            }
450
-        }
451
-
452
-        return $nonExisting;
453
-    }
454
-
455
-    /**
456
-     * Get non-existing related entities from a single relation.
457
-     *
458
-     * @param string $relation
459
-     *
460
-     * @return array
461
-     */
462
-    protected function getNonExistingFromRelation($relation)
463
-    {
464
-        $nonExisting = [];
465
-
466
-        foreach ($this->relationships[$relation] as $aggregate) {
467
-            if (!$aggregate->exists()) {
468
-                $nonExisting[] = $aggregate;
469
-            }
470
-        }
471
-
472
-        return $nonExisting;
473
-    }
474
-
475
-    /**
476
-     * Synchronize relationships if needed.
477
-     *
478
-     * @param array
479
-     *
480
-     * @return void
481
-     */
482
-    public function syncRelationships(array $relationships)
483
-    {
484
-        foreach ($relationships as $relation) {
485
-            if (in_array($relation, $this->needSync)) {
486
-                $this->synchronize($relation);
487
-            }
488
-        }
489
-    }
490
-
491
-    /**
492
-     * Synchronize a relationship attribute.
493
-     *
494
-     * @param $relation
495
-     *
496
-     * @return void
497
-     */
498
-    protected function synchronize($relation)
499
-    {
500
-        $actualContent = $this->relationships[$relation];
501
-
502
-        $relationshipObject = $this->entityMap->$relation($this->getEntityObject());
503
-        $relationshipObject->setParent($this->wrappedEntity);
504
-        $relationshipObject->sync($actualContent);
505
-    }
506
-
507
-    /**
508
-     * Returns an array of Missing related Entities for the
509
-     * given $relation.
510
-     *
511
-     * @param string $relation
512
-     *
513
-     * @return array
514
-     */
515
-    public function getMissingEntities($relation)
516
-    {
517
-        $cachedRelations = $this->getCachedAttribute($relation);
518
-
519
-        if (!is_null($cachedRelations)) {
520
-            $missing = [];
521
-
522
-            foreach ($cachedRelations as $hash) {
523
-                if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
524
-                    $missing[] = $hash;
525
-                }
526
-            }
527
-
528
-            return $missing;
529
-        } else {
530
-            return [];
531
-        }
532
-    }
533
-
534
-    /**
535
-     * Get Relationships who have dirty attributes / dirty relationships.
536
-     *
537
-     * @return array
538
-     */
539
-    public function getDirtyRelationships()
540
-    {
541
-        $dirtyAggregates = [];
542
-
543
-        foreach ($this->relationships as $relation) {
544
-            foreach ($relation as $aggregate) {
545
-                if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships()) > 0) {
546
-                    $dirtyAggregates[] = $aggregate;
547
-                }
548
-            }
549
-        }
550
-
551
-        return $dirtyAggregates;
552
-    }
553
-
554
-    /**
555
-     * Compare the object's raw attributes with the record in cache.
556
-     *
557
-     * @return bool
558
-     */
559
-    public function isDirty()
560
-    {
561
-        if (count($this->getDirtyRawAttributes()) > 0) {
562
-            return true;
563
-        } else {
564
-            return false;
565
-        }
566
-    }
567
-
568
-    /**
569
-     * Get Raw Entity's attributes, as they are represented
570
-     * in the database, including value objects, foreign keys,
571
-     * and discriminator column.
572
-     *
573
-     * @return array
574
-     */
575
-    public function getRawAttributes()
576
-    {
577
-        $attributes = $this->wrappedEntity->getEntityAttributes();
578
-
579
-        foreach ($this->entityMap->getRelationships() as $relation) {
580
-            unset($attributes[$relation]);
581
-        }
582
-
583
-        if ($this->entityMap->getInheritanceType() == 'single_table') {
584
-            $attributes = $this->addDiscriminatorColumn($attributes);
585
-        }
586
-
587
-        $attributes = $this->flattenEmbeddables($attributes);
588
-
589
-        $foreignKeys = $this->getForeignKeyAttributes();
590
-
591
-        return $attributes + $foreignKeys;
592
-    }
593
-
594
-    /**
595
-     * Add Discriminator Column if it doesn't exist on the actual entity.
596
-     *
597
-     * @param array $attributes
598
-     *
599
-     * @return array
600
-     */
601
-    protected function addDiscriminatorColumn($attributes)
602
-    {
603
-        $discriminatorColumn = $this->entityMap->getDiscriminatorColumn();
604
-        $entityClass = $this->entityMap->getClass();
605
-
606
-        if (!array_key_exists($discriminatorColumn, $attributes)) {
607
-
608
-            // Use key if present in discriminatorMap
609
-            $map = $this->entityMap->getDiscriminatorColumnMap();
610
-
611
-            $type = array_search($entityClass, $map);
612
-
613
-            if ($type === false) {
614
-                // Use entity FQDN if no corresponding key is set
615
-                $attributes[$discriminatorColumn] = $entityClass;
616
-            } else {
617
-                $attributes[$discriminatorColumn] = $type;
618
-            }
619
-        }
620
-
621
-        return $attributes;
622
-    }
623
-
624
-    /**
625
-     * Convert Value Objects to raw db attributes.
626
-     *
627
-     * @param array $attributes
628
-     *
629
-     * @return array
630
-     */
631
-    protected function flattenEmbeddables($attributes)
632
-    {
633
-        $embeddables = $this->entityMap->getEmbeddables();
634
-
635
-        foreach ($embeddables as $localKey => $embed) {
636
-            // Retrieve the value object from the entity's attributes
637
-            $valueObject = $attributes[$localKey];
638
-
639
-            // Unset the corresponding key
640
-            unset($attributes[$localKey]);
641
-
642
-            // TODO Make wrapper object compatible with value objects
643
-            $valueObjectAttributes = $valueObject->getEntityAttributes();
644
-
645
-            // Now (if setup in the entity map) we prefix the value object's
646
-            // attributes with the snake_case name of the embedded class.
647
-            $prefix = snake_case(class_basename($embed));
648
-
649
-            foreach ($valueObjectAttributes as $key=>$value) {
650
-                $valueObjectAttributes[$prefix.'_'.$key] = $value;
651
-                unset($valueObjectAttributes[$key]);
652
-            }
653
-
654
-            $attributes = array_merge($attributes, $valueObjectAttributes);
655
-        }
656
-
657
-        return $attributes;
658
-    }
659
-
660
-    /**
661
-     * Return's entity raw attributes in the state they were at last
662
-     * query.
663
-     *
664
-     * @param array|null $columns
665
-     *
666
-     * @return array
667
-     */
668
-    protected function getCachedRawAttributes(array $columns = null)
669
-    {
670
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
671
-
672
-        if (is_null($columns)) {
673
-            return $cachedAttributes;
674
-        } else {
675
-            return array_only($cachedAttributes, $columns);
676
-        }
677
-    }
678
-
679
-    /**
680
-     * Return a single attribute from the cache.
681
-     *
682
-     * @param string $key
683
-     *
684
-     * @return mixed
685
-     */
686
-    protected function getCachedAttribute($key)
687
-    {
688
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
689
-
690
-        if (!array_key_exists($key, $cachedAttributes)) {
691
-            return;
692
-        } else {
693
-            return $cachedAttributes[$key];
694
-        }
695
-    }
696
-
697
-    /**
698
-     * Convert related Entity's attributes to foreign keys.
699
-     *
700
-     * @return array
701
-     */
702
-    protected function getForeignKeyAttributes()
703
-    {
704
-        $foreignKeys = [];
705
-
706
-        foreach ($this->entityMap->getLocalRelationships() as $relation) {
707
-            // check if relationship has been parsed, meaning it has an actual object
708
-            // in the entity's attributes
709
-            if ($this->isActualRelationships($relation)) {
710
-                $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
711
-            }
712
-        }
713
-
714
-        if (!is_null($this->parent)) {
715
-            $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
716
-        }
717
-
718
-        return $foreignKeys;
719
-    }
720
-
721
-    /**
722
-     * Return an associative array containing the key-value pair(s) from
723
-     * the related entity.
724
-     *
725
-     * @param string $relation
726
-     *
727
-     * @return array
728
-     */
729
-    protected function getForeignKeyAttributesFromRelation($relation)
730
-    {
731
-        $localRelations = $this->entityMap->getLocalRelationships();
732
-
733
-        if (in_array($relation, $localRelations)) {
734
-            // Call Relationship's method
735
-            $relationship = $this->entityMap->$relation($this->getEntityObject());
736
-
737
-            $relatedAggregate = $this->relationships[$relation][0];
738
-
739
-            return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
740
-        } else {
741
-            return [];
742
-        }
743
-    }
744
-
745
-    /**
746
-     * Get foreign key attribute(s) from a parent entity in this
747
-     * aggregate context.
748
-     *
749
-     * @return array
750
-     */
751
-    protected function getForeignKeyAttributesFromParent()
752
-    {
753
-        $parentMap = $this->parent->getEntityMap();
754
-
755
-        $parentForeignRelations = $parentMap->getForeignRelationships();
756
-        $parentPivotRelations = $parentMap->getPivotRelationships();
757
-
758
-        // The parentRelation is the name of the relationship
759
-        // methods on the parent entity map
760
-        $parentRelation = $this->parentRelationship;
761
-
762
-        if (in_array($parentRelation, $parentForeignRelations)
763
-            && !in_array($parentRelation, $parentPivotRelations)
764
-        ) {
765
-            $parentObject = $this->parent->getEntityObject();
766
-
767
-            // Call Relationship's method on parent map
768
-            $relationship = $parentMap->$parentRelation($parentObject);
769
-
770
-            return $relationship->getForeignKeyValuePair();
771
-        } else {
772
-            return [];
773
-        }
774
-    }
775
-
776
-    /**
777
-     * Update Pivot records on loaded relationships, by comparing the
778
-     * values from the Entity Cache to the actual relationship inside
779
-     * the aggregated entity.
780
-     *
781
-     * @return void
782
-     */
783
-    public function updatePivotRecords()
784
-    {
785
-        $pivots = $this->entityMap->getPivotRelationships();
786
-
787
-        foreach ($pivots as $pivot) {
788
-            if (array_key_exists($pivot, $this->relationships)) {
789
-                $this->updatePivotRelation($pivot);
790
-            }
791
-        }
792
-    }
793
-
794
-    /**
795
-     * Update Single pivot relationship.
796
-     *
797
-     * @param string $relation
798
-     *
799
-     * @return void
800
-     */
801
-    protected function updatePivotRelation($relation)
802
-    {
803
-        $hashes = $this->getEntityHashesFromRelation($relation);
804
-
805
-        $cachedAttributes = $this->getCachedRawAttributes();
806
-
807
-        if (array_key_exists($relation, $cachedAttributes)) {
808
-            // Compare the two array of hashes to find out existing
809
-            // pivot records, and the ones to be created.
810
-            $new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
811
-            $existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
812
-        } else {
813
-            $existing = [];
814
-            $new = $hashes;
815
-        }
816
-
817
-        if (count($new) > 0) {
818
-            $pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
819
-
820
-            $this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
821
-        }
822
-
823
-        if (count($existing) > 0) {
824
-            foreach ($existing as $pivotHash) {
825
-                $this->updatePivotIfDirty($pivotHash, $relation);
826
-            }
827
-        }
828
-    }
829
-
830
-    /**
831
-     * Compare existing pivot record in cache and update it
832
-     * if the pivot attributes are dirty.
833
-     *
834
-     * @param string $pivotHash
835
-     * @param string $relation
836
-     *
837
-     * @return void
838
-     */
839
-    protected function updatePivotIfDirty($pivotHash, $relation)
840
-    {
841
-        $aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
842
-
843
-        if ($aggregate->hasAttribute('pivot')) {
844
-            $pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
845
-
846
-            $cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
847
-
848
-            $actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
849
-
850
-            $dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
851
-
852
-            if (count($dirty) > 0) {
853
-                $id = $aggregate->getEntityId();
854
-
855
-                $this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
856
-            }
857
-        }
858
-    }
859
-
860
-    /**
861
-     * Compare two attributes array and return dirty attributes.
862
-     *
863
-     * @param array $actual
864
-     * @param array $cached
865
-     *
866
-     * @return array
867
-     */
868
-    protected function getDirtyAttributes(array $actual, array $cached)
869
-    {
870
-        $dirty = [];
871
-
872
-        foreach ($actual as $key => $value) {
873
-            if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
874
-                $dirty[$key] = $actual[$key];
875
-            }
876
-        }
877
-
878
-        return $dirty;
879
-    }
880
-
881
-    /**
882
-     * @param string $pivotHash
883
-     * @param string $relation
884
-     *
885
-     * @return array
886
-     */
887
-    protected function getPivotAttributesFromCache($pivotHash, $relation)
888
-    {
889
-        $cachedAttributes = $this->getCachedRawAttributes();
890
-
891
-        $cachedRelations = $cachedAttributes[$relation];
892
-
893
-        foreach ($cachedRelations as $cachedRelation) {
894
-            if ($cachedRelation == $pivotHash) {
895
-                return $cachedRelation->getPivotAttributes();
896
-            }
897
-        }
898
-    }
899
-
900
-    /**
901
-     * Returns an array of related Aggregates from its entity hashes.
902
-     *
903
-     * @param array  $hashes
904
-     * @param string $relation
905
-     *
906
-     * @return array
907
-     */
908
-    protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
909
-    {
910
-        $related = [];
911
-
912
-        foreach ($hashes as $hash) {
913
-            $aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
914
-
915
-            if (!is_null($aggregate)) {
916
-                $related[] = $aggregate;
917
-            }
918
-        }
919
-
920
-        return $related;
921
-    }
922
-
923
-    /**
924
-     * Get related aggregate from its hash.
925
-     *
926
-     * @param string $hash
927
-     * @param string $relation
928
-     *
929
-     * @return \Analogue\ORM\System\Aggregate|null
930
-     */
931
-    protected function getRelatedAggregateFromHash($hash, $relation)
932
-    {
933
-        foreach ($this->relationships[$relation] as $aggregate) {
934
-            if ($aggregate->getEntityHash() == $hash) {
935
-                return $aggregate;
936
-            }
937
-        }
938
-    }
939
-
940
-    /**
941
-     * Return an array of Entity Hashes from a specific relation.
942
-     *
943
-     * @param string $relation
944
-     *
945
-     * @return array
946
-     */
947
-    protected function getEntityHashesFromRelation($relation)
948
-    {
949
-        return array_map(function ($aggregate) {
950
-            return $aggregate->getEntityHash();
951
-        }, $this->relationships[$relation]);
952
-    }
953
-
954
-    /**
955
-     * Check the existence of an actual relationship.
956
-     *
957
-     * @param string $relation
958
-     *
959
-     * @return bool
960
-     */
961
-    protected function isActualRelationships($relation)
962
-    {
963
-        return array_key_exists($relation, $this->relationships)
964
-            && count($this->relationships[$relation]) > 0;
965
-    }
966
-
967
-    /**
968
-     * Return cache instance for the current entity type.
969
-     *
970
-     * @return \Analogue\ORM\System\EntityCache
971
-     */
972
-    protected function getCache()
973
-    {
974
-        return $this->getMapper()->getEntityCache();
975
-    }
976
-
977
-    /**
978
-     * Get Only Raw Entiy's attributes which have been modified
979
-     * since last query.
980
-     *
981
-     * @return array
982
-     */
983
-    public function getDirtyRawAttributes()
984
-    {
985
-        $attributes = $this->getRawAttributes();
986
-        $cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
987
-
988
-        $dirty = [];
989
-
990
-        foreach ($attributes as $key => $value) {
991
-            if ($this->isRelation($key) || $key == 'pivot') {
992
-                continue;
993
-            }
994
-
995
-            if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
996
-                $dirty[$key] = $value;
997
-            } elseif ($value !== $cachedAttributes[$key] &&
998
-                !$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
999
-                $dirty[$key] = $value;
1000
-            }
1001
-        }
1002
-
1003
-        return $dirty;
1004
-    }
1005
-
1006
-    /**
1007
-     * @param $key
1008
-     *
1009
-     * @return bool
1010
-     */
1011
-    protected function isRelation($key)
1012
-    {
1013
-        return in_array($key, $this->entityMap->getRelationships());
1014
-    }
1015
-
1016
-    /**
1017
-     * Determine if the new and old values for a given key are numerically equivalent.
1018
-     *
1019
-     * @param $current
1020
-     * @param $original
1021
-     *
1022
-     * @return bool
1023
-     */
1024
-    protected function originalIsNumericallyEquivalent($current, $original)
1025
-    {
1026
-        return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
1027
-    }
1028
-
1029
-    /**
1030
-     * Get the underlying entity object.
1031
-     *
1032
-     * @return mixed
1033
-     */
1034
-    public function getEntityObject()
1035
-    {
1036
-        return $this->wrappedEntity->getObject();
1037
-    }
1038
-
1039
-    /**
1040
-     * Return the Mapper instance for the current Entity Type.
1041
-     *
1042
-     * @return \Analogue\ORM\System\Mapper
1043
-     */
1044
-    public function getMapper()
1045
-    {
1046
-        return Manager::getMapper($this->class);
1047
-    }
1048
-
1049
-    /**
1050
-     * Check that the entity already exists in the database, by checking
1051
-     * if it has an EntityCache record.
1052
-     *
1053
-     * @return bool
1054
-     */
1055
-    public function exists()
1056
-    {
1057
-        return $this->getCache()->has($this->getEntityId());
1058
-    }
1059
-
1060
-    /**
1061
-     * Set the object attribute raw values (hydration).
1062
-     *
1063
-     * @param array $attributes
1064
-     */
1065
-    public function setEntityAttributes(array $attributes)
1066
-    {
1067
-        $this->wrappedEntity->setEntityAttributes($attributes);
1068
-    }
1069
-
1070
-    /**
1071
-     * Get the raw object's values.
1072
-     *
1073
-     * @return array
1074
-     */
1075
-    public function getEntityAttributes()
1076
-    {
1077
-        return $this->wrappedEntity->getEntityAttributes();
1078
-    }
1079
-
1080
-    /**
1081
-     * Set the raw entity attributes.
1082
-     *
1083
-     * @param string $key
1084
-     * @param string $value
1085
-     */
1086
-    public function setEntityAttribute($key, $value)
1087
-    {
1088
-        $this->wrappedEntity->setEntityAttribute($key, $value);
1089
-    }
1090
-
1091
-    /**
1092
-     * Return the entity's attribute.
1093
-     *
1094
-     * @param string $key
1095
-     *
1096
-     * @return mixed
1097
-     */
1098
-    public function getEntityAttribute($key)
1099
-    {
1100
-        return $this->wrappedEntity->getEntityAttribute($key);
1101
-    }
1102
-
1103
-    /**
1104
-     * Does the attribute exists on the entity.
1105
-     *
1106
-     * @param string $key
1107
-     *
1108
-     * @return bool
1109
-     */
1110
-    public function hasAttribute($key)
1111
-    {
1112
-        return $this->wrappedEntity->hasAttribute($key);
1113
-    }
1114
-
1115
-    /**
1116
-     * Set the lazyloading proxies on the wrapped entity.
1117
-     *
1118
-     * @return void
1119
-     */
1120
-    public function setProxies()
1121
-    {
1122
-        $this->wrappedEntity->setProxies();
1123
-    }
1124
-
1125
-    /**
1126
-     * Hydrate the actual entity.
1127
-     *
1128
-     * @return void
1129
-     */
1130
-    public function hydrate()
1131
-    {
1132
-        $this->wrappedEntity->hydrate();
1133
-    }
19
+	/**
20
+	 * The Root Entity.
21
+	 *
22
+	 * @var \Analogue\ORM\System\Wrappers\Wrapper
23
+	 */
24
+	protected $wrappedEntity;
25
+
26
+	/**
27
+	 * Class of the entity being aggregated.
28
+	 *
29
+	 * @var string
30
+	 */
31
+	protected $class;
32
+
33
+	/**
34
+	 * Parent Root Aggregate.
35
+	 *
36
+	 * @var \Analogue\ORM\System\Aggregate
37
+	 */
38
+	protected $parent;
39
+
40
+	/**
41
+	 * Parent's relationship method.
42
+	 *
43
+	 * @var string
44
+	 */
45
+	protected $parentRelationship;
46
+
47
+	/**
48
+	 * Root Entity.
49
+	 *
50
+	 * @var \Analogue\ORM\System\Aggregate
51
+	 */
52
+	protected $root;
53
+
54
+	/**
55
+	 * An associative array containing entity's
56
+	 * relationships converted to Aggregates.
57
+	 *
58
+	 * @var array
59
+	 */
60
+	protected $relationships = [];
61
+
62
+	/**
63
+	 * Relationship that need post-command synchronization.
64
+	 *
65
+	 * @var array
66
+	 */
67
+	protected $needSync = [];
68
+
69
+	/**
70
+	 * Mapper.
71
+	 *
72
+	 * @var \Analogue\ORM\System\Mapper;
73
+	 */
74
+	protected $mapper;
75
+
76
+	/**
77
+	 * Entity Map.
78
+	 *
79
+	 * @var \Analogue\ORM\EntityMap;
80
+	 */
81
+	protected $entityMap;
82
+
83
+	/**
84
+	 * Create a new Aggregated Entity instance.
85
+	 *
86
+	 * @param mixed          $entity
87
+	 * @param Aggregate|null $parent
88
+	 * @param string         $parentRelationship
89
+	 * @param Aggregate|null $root
90
+	 *
91
+	 * @throws MappingException
92
+	 */
93
+	public function __construct($entity, Aggregate $parent = null, $parentRelationship = null, Aggregate $root = null)
94
+	{
95
+		$factory = new Factory();
96
+
97
+		$this->class = get_class($entity);
98
+
99
+		$this->wrappedEntity = $factory->make($entity);
100
+
101
+		$this->parent = $parent;
102
+
103
+		$this->parentRelationship = $parentRelationship;
104
+
105
+		$this->root = $root;
106
+
107
+		$mapper = $this->getMapper($entity);
108
+
109
+		$this->entityMap = $mapper->getEntityMap();
110
+
111
+		$this->parseRelationships();
112
+	}
113
+
114
+	/**
115
+	 * Parse Every relationships defined on the entity.
116
+	 *
117
+	 * @throws MappingException
118
+	 *
119
+	 * @return void
120
+	 */
121
+	protected function parseRelationships()
122
+	{
123
+		foreach ($this->entityMap->getSingleRelationships() as $relation) {
124
+			$this->parseSingleRelationship($relation);
125
+		}
126
+
127
+		foreach ($this->entityMap->getManyRelationships() as $relation) {
128
+			$this->parseManyRelationship($relation);
129
+		}
130
+	}
131
+
132
+	/**
133
+	 * Parse for values common to single & many relations.
134
+	 *
135
+	 * @param string $relation
136
+	 *
137
+	 * @throws MappingException
138
+	 *
139
+	 * @return mixed|bool
140
+	 */
141
+	protected function parseForCommonValues($relation)
142
+	{
143
+		if (!$this->hasAttribute($relation)) {
144
+			// If no attribute exists for this relationships
145
+			// we'll make it a simple empty array. This will
146
+			// save us from constantly checking for the attributes
147
+			// actual existence.
148
+			$this->relationships[$relation] = [];
149
+
150
+			return false;
151
+		}
152
+
153
+		$value = $this->getRelationshipValue($relation);
154
+
155
+		if (is_null($value)) {
156
+			$this->relationships[$relation] = [];
157
+
158
+			// If the relationship's content is the null value
159
+			// and the Entity's exist in DB, we'll interpret this
160
+			// as the need to detach all related Entities,
161
+			// therefore a sync operation is needed.
162
+			$this->needSync[] = $relation;
163
+
164
+			return false;
165
+		}
166
+
167
+		return $value;
168
+	}
169
+
170
+	/**
171
+	 * Parse a 'single' relationship.
172
+	 *
173
+	 * @param string $relation
174
+	 *
175
+	 * @throws MappingException
176
+	 *
177
+	 * @return bool
178
+	 */
179
+	protected function parseSingleRelationship($relation)
180
+	{
181
+		if (!$value = $this->parseForCommonValues($relation)) {
182
+			return true;
183
+		}
184
+
185
+		if ($value instanceof Collection || is_array($value) || $value instanceof CollectionProxy) {
186
+			throw new MappingException("Entity's attribute $relation should not be array, or collection");
187
+		}
188
+
189
+		if ($value instanceof LazyLoadingInterface && !$value->isProxyInitialized()) {
190
+			$this->relationships[$relation] = [];
191
+
192
+			return true;
193
+		}
194
+
195
+		// If the attribute is a loaded proxy, swap it for its
196
+		// loaded entity.
197
+		if ($value instanceof LazyLoadingInterface && $value->isProxyInitialized()) {
198
+			$value = $value->getWrappedValueHolderValue();
199
+		}
200
+
201
+		if ($this->isParentOrRoot($value)) {
202
+			$this->relationships[$relation] = [];
203
+
204
+			return true;
205
+		}
206
+
207
+		// At this point, we can assume the attribute is an Entity instance
208
+		// so we'll treat it as such.
209
+		$subAggregate = $this->createSubAggregate($value, $relation);
210
+
211
+		// Even if it's a single entity, we'll store it as an array
212
+		// just for consistency with other relationships
213
+		$this->relationships[$relation] = [$subAggregate];
214
+
215
+		// We always need to check a loaded relation is in sync
216
+		// with its local key
217
+		$this->needSync[] = $relation;
218
+
219
+		return true;
220
+	}
221
+
222
+	/**
223
+	 * Check if value isn't parent or root in the aggregate.
224
+	 *
225
+	 * @param  mixed
226
+	 *
227
+	 * @return bool|null
228
+	 */
229
+	protected function isParentOrRoot($value)
230
+	{
231
+		if (!is_null($this->root)) {
232
+			$rootClass = get_class($this->root->getEntityObject());
233
+			if ($rootClass == get_class($value)) {
234
+				return true;
235
+			}
236
+		}
237
+
238
+		if (!is_null($this->parent)) {
239
+			$parentClass = get_class($this->parent->getEntityObject());
240
+			if ($parentClass == get_class($value)) {
241
+				return true;
242
+			}
243
+		}
244
+	}
245
+
246
+	/**
247
+	 * Parse a 'many' relationship.
248
+	 *
249
+	 * @param string $relation
250
+	 *
251
+	 * @throws MappingException
252
+	 *
253
+	 * @return bool
254
+	 */
255
+	protected function parseManyRelationship($relation)
256
+	{
257
+		if (!$value = $this->parseForCommonValues($relation)) {
258
+			return true;
259
+		}
260
+
261
+		if (is_array($value) || (!$value instanceof CollectionProxy && $value instanceof Collection)) {
262
+			$this->needSync[] = $relation;
263
+		}
264
+
265
+		// If the relation is a proxy, we test is the relation
266
+		// has been lazy loaded, otherwise we'll just treat
267
+		// the subset of newly added items.
268
+		if ($value instanceof CollectionProxy && $value->isProxyInitialized()) {
269
+			$this->needSync[] = $relation;
270
+			//$value = $value->getUnderlyingCollection();
271
+		}
272
+
273
+		if ($value instanceof CollectionProxy && !$value->isProxyInitialized()) {
274
+			$value = $value->getAddedItems();
275
+		}
276
+
277
+		// At this point $value should be either an array or an instance
278
+		// of a collection class.
279
+		if (!is_array($value) && !$value instanceof Collection) {
280
+			throw new MappingException("'$relation' attribute should be array() or Collection");
281
+		}
282
+
283
+		$this->relationships[$relation] = $this->createSubAggregates($value, $relation);
284
+
285
+		return true;
286
+	}
287
+
288
+	/**
289
+	 * Return Entity's relationship attribute.
290
+	 *
291
+	 * @param string $relation
292
+	 *
293
+	 * @throws MappingException
294
+	 *
295
+	 * @return mixed
296
+	 */
297
+	protected function getRelationshipValue($relation)
298
+	{
299
+		$value = $this->getEntityAttribute($relation);
300
+		//if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
301
+		if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
302
+			throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
303
+		}
304
+
305
+		return $value;
306
+	}
307
+
308
+	/**
309
+	 * Create a child, aggregated entity.
310
+	 *
311
+	 * @param mixed  $entities
312
+	 * @param string $relation
313
+	 *
314
+	 * @return array
315
+	 */
316
+	protected function createSubAggregates($entities, $relation)
317
+	{
318
+		$aggregates = [];
319
+
320
+		foreach ($entities as $entity) {
321
+			$aggregates[] = $this->createSubAggregate($entity, $relation);
322
+		}
323
+
324
+		return $aggregates;
325
+	}
326
+
327
+	/**
328
+	 * Create a related subAggregate.
329
+	 *
330
+	 * @param mixed  $entity
331
+	 * @param string $relation
332
+	 *
333
+	 * @throws MappingException
334
+	 *
335
+	 * @return self
336
+	 */
337
+	protected function createSubAggregate($entity, $relation)
338
+	{
339
+		// If root isn't defined, then this is the Aggregate Root
340
+		if (is_null($this->root)) {
341
+			$root = $this;
342
+		} else {
343
+			$root = $this->root;
344
+		}
345
+
346
+		return new self($entity, $this, $relation, $root);
347
+	}
348
+
349
+	/**
350
+	 * Get the Entity's primary key attribute.
351
+	 *
352
+	 * @return string|int
353
+	 */
354
+	public function getEntityId()
355
+	{
356
+		return $this->wrappedEntity->getEntityKey();
357
+	}
358
+
359
+	/**
360
+	 * Get the name of the primary key.
361
+	 *
362
+	 * @return string
363
+	 */
364
+	public function getEntityKey()
365
+	{
366
+		return $this->entityMap->getKeyName();
367
+	}
368
+
369
+	/**
370
+	 * Return the entity map for the current entity.
371
+	 *
372
+	 * @return \Analogue\ORM\EntityMap
373
+	 */
374
+	public function getEntityMap()
375
+	{
376
+		return $this->entityMap;
377
+	}
378
+
379
+	/**
380
+	 * Return the Entity's hash $class.$id.
381
+	 *
382
+	 * @return string
383
+	 */
384
+	public function getEntityHash()
385
+	{
386
+		return $this->getEntityClass().'.'.$this->getEntityId();
387
+	}
388
+
389
+	/**
390
+	 * Get wrapped entity class.
391
+	 *
392
+	 * @return string
393
+	 */
394
+	public function getEntityClass()
395
+	{
396
+		return $this->entityMap->getClass();
397
+	}
398
+
399
+	/**
400
+	 * Return the Mapper's entity cache.
401
+	 *
402
+	 * @return \Analogue\ORM\System\EntityCache
403
+	 */
404
+	protected function getEntityCache()
405
+	{
406
+		return $this->getMapper()->getEntityCache();
407
+	}
408
+
409
+	/**
410
+	 * Get a relationship as an aggregated entities' array.
411
+	 *
412
+	 * @param string $name
413
+	 *
414
+	 * @return array
415
+	 */
416
+	public function getRelationship($name)
417
+	{
418
+		if (array_key_exists($name, $this->relationships)) {
419
+			return $this->relationships[$name];
420
+		} else {
421
+			return [];
422
+		}
423
+	}
424
+
425
+	/**
426
+	 * [TO IMPLEMENT].
427
+	 *
428
+	 * @return array
429
+	 */
430
+	public function getPivotAttributes()
431
+	{
432
+		return [];
433
+	}
434
+
435
+	/**
436
+	 * Get Non existing related entities from several relationships.
437
+	 *
438
+	 * @param array $relationships
439
+	 *
440
+	 * @return array
441
+	 */
442
+	public function getNonExistingRelated(array $relationships)
443
+	{
444
+		$nonExisting = [];
445
+
446
+		foreach ($relationships as $relation) {
447
+			if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
448
+				$nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
449
+			}
450
+		}
451
+
452
+		return $nonExisting;
453
+	}
454
+
455
+	/**
456
+	 * Get non-existing related entities from a single relation.
457
+	 *
458
+	 * @param string $relation
459
+	 *
460
+	 * @return array
461
+	 */
462
+	protected function getNonExistingFromRelation($relation)
463
+	{
464
+		$nonExisting = [];
465
+
466
+		foreach ($this->relationships[$relation] as $aggregate) {
467
+			if (!$aggregate->exists()) {
468
+				$nonExisting[] = $aggregate;
469
+			}
470
+		}
471
+
472
+		return $nonExisting;
473
+	}
474
+
475
+	/**
476
+	 * Synchronize relationships if needed.
477
+	 *
478
+	 * @param array
479
+	 *
480
+	 * @return void
481
+	 */
482
+	public function syncRelationships(array $relationships)
483
+	{
484
+		foreach ($relationships as $relation) {
485
+			if (in_array($relation, $this->needSync)) {
486
+				$this->synchronize($relation);
487
+			}
488
+		}
489
+	}
490
+
491
+	/**
492
+	 * Synchronize a relationship attribute.
493
+	 *
494
+	 * @param $relation
495
+	 *
496
+	 * @return void
497
+	 */
498
+	protected function synchronize($relation)
499
+	{
500
+		$actualContent = $this->relationships[$relation];
501
+
502
+		$relationshipObject = $this->entityMap->$relation($this->getEntityObject());
503
+		$relationshipObject->setParent($this->wrappedEntity);
504
+		$relationshipObject->sync($actualContent);
505
+	}
506
+
507
+	/**
508
+	 * Returns an array of Missing related Entities for the
509
+	 * given $relation.
510
+	 *
511
+	 * @param string $relation
512
+	 *
513
+	 * @return array
514
+	 */
515
+	public function getMissingEntities($relation)
516
+	{
517
+		$cachedRelations = $this->getCachedAttribute($relation);
518
+
519
+		if (!is_null($cachedRelations)) {
520
+			$missing = [];
521
+
522
+			foreach ($cachedRelations as $hash) {
523
+				if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
524
+					$missing[] = $hash;
525
+				}
526
+			}
527
+
528
+			return $missing;
529
+		} else {
530
+			return [];
531
+		}
532
+	}
533
+
534
+	/**
535
+	 * Get Relationships who have dirty attributes / dirty relationships.
536
+	 *
537
+	 * @return array
538
+	 */
539
+	public function getDirtyRelationships()
540
+	{
541
+		$dirtyAggregates = [];
542
+
543
+		foreach ($this->relationships as $relation) {
544
+			foreach ($relation as $aggregate) {
545
+				if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships()) > 0) {
546
+					$dirtyAggregates[] = $aggregate;
547
+				}
548
+			}
549
+		}
550
+
551
+		return $dirtyAggregates;
552
+	}
553
+
554
+	/**
555
+	 * Compare the object's raw attributes with the record in cache.
556
+	 *
557
+	 * @return bool
558
+	 */
559
+	public function isDirty()
560
+	{
561
+		if (count($this->getDirtyRawAttributes()) > 0) {
562
+			return true;
563
+		} else {
564
+			return false;
565
+		}
566
+	}
567
+
568
+	/**
569
+	 * Get Raw Entity's attributes, as they are represented
570
+	 * in the database, including value objects, foreign keys,
571
+	 * and discriminator column.
572
+	 *
573
+	 * @return array
574
+	 */
575
+	public function getRawAttributes()
576
+	{
577
+		$attributes = $this->wrappedEntity->getEntityAttributes();
578
+
579
+		foreach ($this->entityMap->getRelationships() as $relation) {
580
+			unset($attributes[$relation]);
581
+		}
582
+
583
+		if ($this->entityMap->getInheritanceType() == 'single_table') {
584
+			$attributes = $this->addDiscriminatorColumn($attributes);
585
+		}
586
+
587
+		$attributes = $this->flattenEmbeddables($attributes);
588
+
589
+		$foreignKeys = $this->getForeignKeyAttributes();
590
+
591
+		return $attributes + $foreignKeys;
592
+	}
593
+
594
+	/**
595
+	 * Add Discriminator Column if it doesn't exist on the actual entity.
596
+	 *
597
+	 * @param array $attributes
598
+	 *
599
+	 * @return array
600
+	 */
601
+	protected function addDiscriminatorColumn($attributes)
602
+	{
603
+		$discriminatorColumn = $this->entityMap->getDiscriminatorColumn();
604
+		$entityClass = $this->entityMap->getClass();
605
+
606
+		if (!array_key_exists($discriminatorColumn, $attributes)) {
607
+
608
+			// Use key if present in discriminatorMap
609
+			$map = $this->entityMap->getDiscriminatorColumnMap();
610
+
611
+			$type = array_search($entityClass, $map);
612
+
613
+			if ($type === false) {
614
+				// Use entity FQDN if no corresponding key is set
615
+				$attributes[$discriminatorColumn] = $entityClass;
616
+			} else {
617
+				$attributes[$discriminatorColumn] = $type;
618
+			}
619
+		}
620
+
621
+		return $attributes;
622
+	}
623
+
624
+	/**
625
+	 * Convert Value Objects to raw db attributes.
626
+	 *
627
+	 * @param array $attributes
628
+	 *
629
+	 * @return array
630
+	 */
631
+	protected function flattenEmbeddables($attributes)
632
+	{
633
+		$embeddables = $this->entityMap->getEmbeddables();
634
+
635
+		foreach ($embeddables as $localKey => $embed) {
636
+			// Retrieve the value object from the entity's attributes
637
+			$valueObject = $attributes[$localKey];
638
+
639
+			// Unset the corresponding key
640
+			unset($attributes[$localKey]);
641
+
642
+			// TODO Make wrapper object compatible with value objects
643
+			$valueObjectAttributes = $valueObject->getEntityAttributes();
644
+
645
+			// Now (if setup in the entity map) we prefix the value object's
646
+			// attributes with the snake_case name of the embedded class.
647
+			$prefix = snake_case(class_basename($embed));
648
+
649
+			foreach ($valueObjectAttributes as $key=>$value) {
650
+				$valueObjectAttributes[$prefix.'_'.$key] = $value;
651
+				unset($valueObjectAttributes[$key]);
652
+			}
653
+
654
+			$attributes = array_merge($attributes, $valueObjectAttributes);
655
+		}
656
+
657
+		return $attributes;
658
+	}
659
+
660
+	/**
661
+	 * Return's entity raw attributes in the state they were at last
662
+	 * query.
663
+	 *
664
+	 * @param array|null $columns
665
+	 *
666
+	 * @return array
667
+	 */
668
+	protected function getCachedRawAttributes(array $columns = null)
669
+	{
670
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
671
+
672
+		if (is_null($columns)) {
673
+			return $cachedAttributes;
674
+		} else {
675
+			return array_only($cachedAttributes, $columns);
676
+		}
677
+	}
678
+
679
+	/**
680
+	 * Return a single attribute from the cache.
681
+	 *
682
+	 * @param string $key
683
+	 *
684
+	 * @return mixed
685
+	 */
686
+	protected function getCachedAttribute($key)
687
+	{
688
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
689
+
690
+		if (!array_key_exists($key, $cachedAttributes)) {
691
+			return;
692
+		} else {
693
+			return $cachedAttributes[$key];
694
+		}
695
+	}
696
+
697
+	/**
698
+	 * Convert related Entity's attributes to foreign keys.
699
+	 *
700
+	 * @return array
701
+	 */
702
+	protected function getForeignKeyAttributes()
703
+	{
704
+		$foreignKeys = [];
705
+
706
+		foreach ($this->entityMap->getLocalRelationships() as $relation) {
707
+			// check if relationship has been parsed, meaning it has an actual object
708
+			// in the entity's attributes
709
+			if ($this->isActualRelationships($relation)) {
710
+				$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
711
+			}
712
+		}
713
+
714
+		if (!is_null($this->parent)) {
715
+			$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
716
+		}
717
+
718
+		return $foreignKeys;
719
+	}
720
+
721
+	/**
722
+	 * Return an associative array containing the key-value pair(s) from
723
+	 * the related entity.
724
+	 *
725
+	 * @param string $relation
726
+	 *
727
+	 * @return array
728
+	 */
729
+	protected function getForeignKeyAttributesFromRelation($relation)
730
+	{
731
+		$localRelations = $this->entityMap->getLocalRelationships();
732
+
733
+		if (in_array($relation, $localRelations)) {
734
+			// Call Relationship's method
735
+			$relationship = $this->entityMap->$relation($this->getEntityObject());
736
+
737
+			$relatedAggregate = $this->relationships[$relation][0];
738
+
739
+			return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
740
+		} else {
741
+			return [];
742
+		}
743
+	}
744
+
745
+	/**
746
+	 * Get foreign key attribute(s) from a parent entity in this
747
+	 * aggregate context.
748
+	 *
749
+	 * @return array
750
+	 */
751
+	protected function getForeignKeyAttributesFromParent()
752
+	{
753
+		$parentMap = $this->parent->getEntityMap();
754
+
755
+		$parentForeignRelations = $parentMap->getForeignRelationships();
756
+		$parentPivotRelations = $parentMap->getPivotRelationships();
757
+
758
+		// The parentRelation is the name of the relationship
759
+		// methods on the parent entity map
760
+		$parentRelation = $this->parentRelationship;
761
+
762
+		if (in_array($parentRelation, $parentForeignRelations)
763
+			&& !in_array($parentRelation, $parentPivotRelations)
764
+		) {
765
+			$parentObject = $this->parent->getEntityObject();
766
+
767
+			// Call Relationship's method on parent map
768
+			$relationship = $parentMap->$parentRelation($parentObject);
769
+
770
+			return $relationship->getForeignKeyValuePair();
771
+		} else {
772
+			return [];
773
+		}
774
+	}
775
+
776
+	/**
777
+	 * Update Pivot records on loaded relationships, by comparing the
778
+	 * values from the Entity Cache to the actual relationship inside
779
+	 * the aggregated entity.
780
+	 *
781
+	 * @return void
782
+	 */
783
+	public function updatePivotRecords()
784
+	{
785
+		$pivots = $this->entityMap->getPivotRelationships();
786
+
787
+		foreach ($pivots as $pivot) {
788
+			if (array_key_exists($pivot, $this->relationships)) {
789
+				$this->updatePivotRelation($pivot);
790
+			}
791
+		}
792
+	}
793
+
794
+	/**
795
+	 * Update Single pivot relationship.
796
+	 *
797
+	 * @param string $relation
798
+	 *
799
+	 * @return void
800
+	 */
801
+	protected function updatePivotRelation($relation)
802
+	{
803
+		$hashes = $this->getEntityHashesFromRelation($relation);
804
+
805
+		$cachedAttributes = $this->getCachedRawAttributes();
806
+
807
+		if (array_key_exists($relation, $cachedAttributes)) {
808
+			// Compare the two array of hashes to find out existing
809
+			// pivot records, and the ones to be created.
810
+			$new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
811
+			$existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
812
+		} else {
813
+			$existing = [];
814
+			$new = $hashes;
815
+		}
816
+
817
+		if (count($new) > 0) {
818
+			$pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
819
+
820
+			$this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
821
+		}
822
+
823
+		if (count($existing) > 0) {
824
+			foreach ($existing as $pivotHash) {
825
+				$this->updatePivotIfDirty($pivotHash, $relation);
826
+			}
827
+		}
828
+	}
829
+
830
+	/**
831
+	 * Compare existing pivot record in cache and update it
832
+	 * if the pivot attributes are dirty.
833
+	 *
834
+	 * @param string $pivotHash
835
+	 * @param string $relation
836
+	 *
837
+	 * @return void
838
+	 */
839
+	protected function updatePivotIfDirty($pivotHash, $relation)
840
+	{
841
+		$aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
842
+
843
+		if ($aggregate->hasAttribute('pivot')) {
844
+			$pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
845
+
846
+			$cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
847
+
848
+			$actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
849
+
850
+			$dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
851
+
852
+			if (count($dirty) > 0) {
853
+				$id = $aggregate->getEntityId();
854
+
855
+				$this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
856
+			}
857
+		}
858
+	}
859
+
860
+	/**
861
+	 * Compare two attributes array and return dirty attributes.
862
+	 *
863
+	 * @param array $actual
864
+	 * @param array $cached
865
+	 *
866
+	 * @return array
867
+	 */
868
+	protected function getDirtyAttributes(array $actual, array $cached)
869
+	{
870
+		$dirty = [];
871
+
872
+		foreach ($actual as $key => $value) {
873
+			if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
874
+				$dirty[$key] = $actual[$key];
875
+			}
876
+		}
877
+
878
+		return $dirty;
879
+	}
880
+
881
+	/**
882
+	 * @param string $pivotHash
883
+	 * @param string $relation
884
+	 *
885
+	 * @return array
886
+	 */
887
+	protected function getPivotAttributesFromCache($pivotHash, $relation)
888
+	{
889
+		$cachedAttributes = $this->getCachedRawAttributes();
890
+
891
+		$cachedRelations = $cachedAttributes[$relation];
892
+
893
+		foreach ($cachedRelations as $cachedRelation) {
894
+			if ($cachedRelation == $pivotHash) {
895
+				return $cachedRelation->getPivotAttributes();
896
+			}
897
+		}
898
+	}
899
+
900
+	/**
901
+	 * Returns an array of related Aggregates from its entity hashes.
902
+	 *
903
+	 * @param array  $hashes
904
+	 * @param string $relation
905
+	 *
906
+	 * @return array
907
+	 */
908
+	protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
909
+	{
910
+		$related = [];
911
+
912
+		foreach ($hashes as $hash) {
913
+			$aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
914
+
915
+			if (!is_null($aggregate)) {
916
+				$related[] = $aggregate;
917
+			}
918
+		}
919
+
920
+		return $related;
921
+	}
922
+
923
+	/**
924
+	 * Get related aggregate from its hash.
925
+	 *
926
+	 * @param string $hash
927
+	 * @param string $relation
928
+	 *
929
+	 * @return \Analogue\ORM\System\Aggregate|null
930
+	 */
931
+	protected function getRelatedAggregateFromHash($hash, $relation)
932
+	{
933
+		foreach ($this->relationships[$relation] as $aggregate) {
934
+			if ($aggregate->getEntityHash() == $hash) {
935
+				return $aggregate;
936
+			}
937
+		}
938
+	}
939
+
940
+	/**
941
+	 * Return an array of Entity Hashes from a specific relation.
942
+	 *
943
+	 * @param string $relation
944
+	 *
945
+	 * @return array
946
+	 */
947
+	protected function getEntityHashesFromRelation($relation)
948
+	{
949
+		return array_map(function ($aggregate) {
950
+			return $aggregate->getEntityHash();
951
+		}, $this->relationships[$relation]);
952
+	}
953
+
954
+	/**
955
+	 * Check the existence of an actual relationship.
956
+	 *
957
+	 * @param string $relation
958
+	 *
959
+	 * @return bool
960
+	 */
961
+	protected function isActualRelationships($relation)
962
+	{
963
+		return array_key_exists($relation, $this->relationships)
964
+			&& count($this->relationships[$relation]) > 0;
965
+	}
966
+
967
+	/**
968
+	 * Return cache instance for the current entity type.
969
+	 *
970
+	 * @return \Analogue\ORM\System\EntityCache
971
+	 */
972
+	protected function getCache()
973
+	{
974
+		return $this->getMapper()->getEntityCache();
975
+	}
976
+
977
+	/**
978
+	 * Get Only Raw Entiy's attributes which have been modified
979
+	 * since last query.
980
+	 *
981
+	 * @return array
982
+	 */
983
+	public function getDirtyRawAttributes()
984
+	{
985
+		$attributes = $this->getRawAttributes();
986
+		$cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
987
+
988
+		$dirty = [];
989
+
990
+		foreach ($attributes as $key => $value) {
991
+			if ($this->isRelation($key) || $key == 'pivot') {
992
+				continue;
993
+			}
994
+
995
+			if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
996
+				$dirty[$key] = $value;
997
+			} elseif ($value !== $cachedAttributes[$key] &&
998
+				!$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
999
+				$dirty[$key] = $value;
1000
+			}
1001
+		}
1002
+
1003
+		return $dirty;
1004
+	}
1005
+
1006
+	/**
1007
+	 * @param $key
1008
+	 *
1009
+	 * @return bool
1010
+	 */
1011
+	protected function isRelation($key)
1012
+	{
1013
+		return in_array($key, $this->entityMap->getRelationships());
1014
+	}
1015
+
1016
+	/**
1017
+	 * Determine if the new and old values for a given key are numerically equivalent.
1018
+	 *
1019
+	 * @param $current
1020
+	 * @param $original
1021
+	 *
1022
+	 * @return bool
1023
+	 */
1024
+	protected function originalIsNumericallyEquivalent($current, $original)
1025
+	{
1026
+		return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
1027
+	}
1028
+
1029
+	/**
1030
+	 * Get the underlying entity object.
1031
+	 *
1032
+	 * @return mixed
1033
+	 */
1034
+	public function getEntityObject()
1035
+	{
1036
+		return $this->wrappedEntity->getObject();
1037
+	}
1038
+
1039
+	/**
1040
+	 * Return the Mapper instance for the current Entity Type.
1041
+	 *
1042
+	 * @return \Analogue\ORM\System\Mapper
1043
+	 */
1044
+	public function getMapper()
1045
+	{
1046
+		return Manager::getMapper($this->class);
1047
+	}
1048
+
1049
+	/**
1050
+	 * Check that the entity already exists in the database, by checking
1051
+	 * if it has an EntityCache record.
1052
+	 *
1053
+	 * @return bool
1054
+	 */
1055
+	public function exists()
1056
+	{
1057
+		return $this->getCache()->has($this->getEntityId());
1058
+	}
1059
+
1060
+	/**
1061
+	 * Set the object attribute raw values (hydration).
1062
+	 *
1063
+	 * @param array $attributes
1064
+	 */
1065
+	public function setEntityAttributes(array $attributes)
1066
+	{
1067
+		$this->wrappedEntity->setEntityAttributes($attributes);
1068
+	}
1069
+
1070
+	/**
1071
+	 * Get the raw object's values.
1072
+	 *
1073
+	 * @return array
1074
+	 */
1075
+	public function getEntityAttributes()
1076
+	{
1077
+		return $this->wrappedEntity->getEntityAttributes();
1078
+	}
1079
+
1080
+	/**
1081
+	 * Set the raw entity attributes.
1082
+	 *
1083
+	 * @param string $key
1084
+	 * @param string $value
1085
+	 */
1086
+	public function setEntityAttribute($key, $value)
1087
+	{
1088
+		$this->wrappedEntity->setEntityAttribute($key, $value);
1089
+	}
1090
+
1091
+	/**
1092
+	 * Return the entity's attribute.
1093
+	 *
1094
+	 * @param string $key
1095
+	 *
1096
+	 * @return mixed
1097
+	 */
1098
+	public function getEntityAttribute($key)
1099
+	{
1100
+		return $this->wrappedEntity->getEntityAttribute($key);
1101
+	}
1102
+
1103
+	/**
1104
+	 * Does the attribute exists on the entity.
1105
+	 *
1106
+	 * @param string $key
1107
+	 *
1108
+	 * @return bool
1109
+	 */
1110
+	public function hasAttribute($key)
1111
+	{
1112
+		return $this->wrappedEntity->hasAttribute($key);
1113
+	}
1114
+
1115
+	/**
1116
+	 * Set the lazyloading proxies on the wrapped entity.
1117
+	 *
1118
+	 * @return void
1119
+	 */
1120
+	public function setProxies()
1121
+	{
1122
+		$this->wrappedEntity->setProxies();
1123
+	}
1124
+
1125
+	/**
1126
+	 * Hydrate the actual entity.
1127
+	 *
1128
+	 * @return void
1129
+	 */
1130
+	public function hydrate()
1131
+	{
1132
+		$this->wrappedEntity->hydrate();
1133
+	}
1134 1134
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -946,7 +946,7 @@
 block discarded – undo
946 946
      */
947 947
     protected function getEntityHashesFromRelation($relation)
948 948
     {
949
-        return array_map(function ($aggregate) {
949
+        return array_map(function($aggregate) {
950 950
             return $aggregate->getEntityHash();
951 951
         }, $this->relationships[$relation]);
952 952
     }
Please login to merge, or discard this patch.
src/System/InternallyMappable.php 1 patch
Indentation   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -4,43 +4,43 @@
 block discarded – undo
4 4
 
5 5
 interface InternallyMappable
6 6
 {
7
-    /**
8
-     * Set the object attribute raw values (hydration).
9
-     *
10
-     * @param array $attributes
11
-     */
12
-    public function setEntityAttributes(array $attributes);
7
+	/**
8
+	 * Set the object attribute raw values (hydration).
9
+	 *
10
+	 * @param array $attributes
11
+	 */
12
+	public function setEntityAttributes(array $attributes);
13 13
 
14
-    /**
15
-     * Get the raw object's values.
16
-     *
17
-     * @return array
18
-     */
19
-    public function getEntityAttributes();
14
+	/**
15
+	 * Get the raw object's values.
16
+	 *
17
+	 * @return array
18
+	 */
19
+	public function getEntityAttributes();
20 20
 
21
-    /**
22
-     * Set the raw entity attributes.
23
-     *
24
-     * @param string $key
25
-     * @param string $value
26
-     */
27
-    public function setEntityAttribute($key, $value);
21
+	/**
22
+	 * Set the raw entity attributes.
23
+	 *
24
+	 * @param string $key
25
+	 * @param string $value
26
+	 */
27
+	public function setEntityAttribute($key, $value);
28 28
 
29
-    /**
30
-     * Return the entity's attribute.
31
-     *
32
-     * @param string $key
33
-     *
34
-     * @return mixed
35
-     */
36
-    public function getEntityAttribute($key);
29
+	/**
30
+	 * Return the entity's attribute.
31
+	 *
32
+	 * @param string $key
33
+	 *
34
+	 * @return mixed
35
+	 */
36
+	public function getEntityAttribute($key);
37 37
 
38
-    /**
39
-     * Does the entity posses the given attribute.
40
-     *
41
-     * @param string $key
42
-     *
43
-     * @return bool
44
-     */
45
-    public function hasAttribute($key);
38
+	/**
39
+	 * Does the entity posses the given attribute.
40
+	 *
41
+	 * @param string $key
42
+	 *
43
+	 * @return bool
44
+	 */
45
+	public function hasAttribute($key);
46 46
 }
Please login to merge, or discard this patch.
src/System/Proxies/ProxyFactory.php 2 patches
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -14,47 +14,47 @@
 block discarded – undo
14 14
  */
15 15
 class ProxyFactory
16 16
 {
17
-    public function make($entity, $relation, $class)
18
-    {
19
-        $entityMap = Manager::getMapper($entity)->getEntityMap();
20
-
21
-        $singleRelations = $entityMap->getSingleRelationships();
22
-        $manyRelations = $entityMap->getManyRelationships();
23
-
24
-        if (in_array($relation, $singleRelations)) {
25
-            return $this->makeEntityProxy($entity, $relation, $class);
26
-        }
27
-
28
-        if (in_array($relation, $manyRelations)) {
29
-            return new CollectionProxy($entity, $relation);
30
-        }
31
-
32
-        throw new MappingException('Could not identity relation '.$relation);
33
-    }
34
-
35
-    /**
36
-     * Create an instance of a proxy object, extending the actual
37
-     * related class.
38
-     *
39
-     * @param mixed  $entity   parent object
40
-     * @param string $relation the name of the relationship method
41
-     * @param string $class    the class name of the related object
42
-     *
43
-     * @return mixed
44
-     */
45
-    protected function makeEntityProxy($entity, $relation, $class)
46
-    {
47
-        $factory = new LazyLoadingValueHolderFactory();
48
-
49
-        $initializer = function (&$wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, &$initializer) use ($entity, $relation) {
50
-            $entityMap = Manager::getMapper($entity)->getEntityMap();
51
-
52
-            $wrappedObject = $entityMap->$relation($entity)->getResults($relation);
53
-
54
-            $initializer = null; // disable initialization
55
-            return true; // confirm that initialization occurred correctly
56
-        };
57
-
58
-        return $factory->createProxy($class, $initializer);
59
-    }
17
+	public function make($entity, $relation, $class)
18
+	{
19
+		$entityMap = Manager::getMapper($entity)->getEntityMap();
20
+
21
+		$singleRelations = $entityMap->getSingleRelationships();
22
+		$manyRelations = $entityMap->getManyRelationships();
23
+
24
+		if (in_array($relation, $singleRelations)) {
25
+			return $this->makeEntityProxy($entity, $relation, $class);
26
+		}
27
+
28
+		if (in_array($relation, $manyRelations)) {
29
+			return new CollectionProxy($entity, $relation);
30
+		}
31
+
32
+		throw new MappingException('Could not identity relation '.$relation);
33
+	}
34
+
35
+	/**
36
+	 * Create an instance of a proxy object, extending the actual
37
+	 * related class.
38
+	 *
39
+	 * @param mixed  $entity   parent object
40
+	 * @param string $relation the name of the relationship method
41
+	 * @param string $class    the class name of the related object
42
+	 *
43
+	 * @return mixed
44
+	 */
45
+	protected function makeEntityProxy($entity, $relation, $class)
46
+	{
47
+		$factory = new LazyLoadingValueHolderFactory();
48
+
49
+		$initializer = function (&$wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, &$initializer) use ($entity, $relation) {
50
+			$entityMap = Manager::getMapper($entity)->getEntityMap();
51
+
52
+			$wrappedObject = $entityMap->$relation($entity)->getResults($relation);
53
+
54
+			$initializer = null; // disable initialization
55
+			return true; // confirm that initialization occurred correctly
56
+		};
57
+
58
+		return $factory->createProxy($class, $initializer);
59
+	}
60 60
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -46,7 +46,7 @@
 block discarded – undo
46 46
     {
47 47
         $factory = new LazyLoadingValueHolderFactory();
48 48
 
49
-        $initializer = function (&$wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, &$initializer) use ($entity, $relation) {
49
+        $initializer = function(&$wrappedObject, LazyLoadingInterface $proxy, $method, array $parameters, &$initializer) use ($entity, $relation) {
50 50
             $entityMap = Manager::getMapper($entity)->getEntityMap();
51 51
 
52 52
             $wrappedObject = $entityMap->$relation($entity)->getResults($relation);
Please login to merge, or discard this patch.
src/System/Proxies/CollectionProxy.php 1 patch
Indentation   +1253 added lines, -1253 removed lines patch added patch discarded remove patch
@@ -9,1257 +9,1257 @@
 block discarded – undo
9 9
 
10 10
 class CollectionProxy extends EntityCollection implements ProxyInterface
11 11
 {
12
-    /**
13
-     * Indicate if the relationship has been lazy loaded.
14
-     *
15
-     * @var bool
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()) {
54
-            return true;
55
-        }
56
-
57
-        $relation = $this->relationshipMethod;
58
-        $entity = $this->parentEntity;
59
-
60
-        $entityMap = Manager::getMapper($entity)->getEntityMap();
61
-
62
-        $this->items = $entityMap->$relation($entity)->getResults($relation)->all() + $this->addedItems;
63
-
64
-        $this->relationshipLoaded = true;
65
-
66
-        return true;
67
-    }
68
-
69
-    /**
70
-     * Retrieves current initialization status of the proxy.
71
-     *
72
-     * @return bool
73
-     */
74
-    public function isProxyInitialized() : bool
75
-    {
76
-        return $this->relationshipLoaded;
77
-    }
78
-
79
-    /**
80
-     * Get all of the items in the collection.
81
-     *
82
-     * @return array
83
-     */
84
-    public function all()
85
-    {
86
-        $this->initializeProxy();
87
-
88
-        return parent::all();
89
-    }
90
-
91
-    /**
92
-     * Get the average value of a given key.
93
-     *
94
-     * @param callable|string|null $callback
95
-     *
96
-     * @return mixed
97
-     */
98
-    public function avg($callback = null)
99
-    {
100
-        $this->initializeProxy();
101
-
102
-        return parent::avg($callback);
103
-    }
104
-
105
-    /**
106
-     * Get the median of a given key.
107
-     *
108
-     * @param null $key
109
-     *
110
-     * @return mixed|null
111
-     */
112
-    public function median($key = null)
113
-    {
114
-        $this->initializeProxy();
115
-
116
-        return parent::median($key);
117
-    }
118
-
119
-    /**
120
-     * Get the mode of a given key.
121
-     *
122
-     * @param mixed $key
123
-     *
124
-     * @return array
125
-     */
126
-    public function mode($key = null)
127
-    {
128
-        $this->initializeProxy();
129
-
130
-        return parent::mode($key);
131
-    }
132
-
133
-    /**
134
-     * Collapse the collection of items into a single array.
135
-     *
136
-     * @return static
137
-     */
138
-    public function collapse()
139
-    {
140
-        $this->initializeProxy();
141
-
142
-        return parent::collapse();
143
-    }
144
-
145
-    /**
146
-     * Determine if an item exists in the collection.
147
-     *
148
-     * @param mixed $key
149
-     * @param mixed $value
150
-     *
151
-     * @return bool
152
-     */
153
-    public function contains($key, $value = null)
154
-    {
155
-        $this->initializeProxy();
156
-
157
-        return parent::contains($key, $value);
158
-    }
159
-
160
-    /**
161
-     * Determine if an item exists in the collection using strict comparison.
162
-     *
163
-     * @param mixed $key
164
-     * @param mixed $value
165
-     *
166
-     * @return bool
167
-     */
168
-    public function containsStrict($key, $value = null)
169
-    {
170
-        $this->initializeProxy();
171
-
172
-        return parent::containsStrict($key, $value);
173
-    }
174
-
175
-    /**
176
-     * Get the items in the collection that are not present in the given items.
177
-     *
178
-     * @param mixed $items
179
-     *
180
-     * @return static
181
-     */
182
-    public function diff($items)
183
-    {
184
-        $this->initializeProxy();
185
-
186
-        return parent::diff($items);
187
-    }
188
-
189
-    /**
190
-     * Get the items in the collection whose keys are not present in the given items.
191
-     *
192
-     * @param mixed $items
193
-     *
194
-     * @return static
195
-     */
196
-    public function diffKeys($items)
197
-    {
198
-        $this->initializeProxy();
199
-
200
-        return parent::diffKeys($items);
201
-    }
202
-
203
-    /**
204
-     * Execute a callback over each item.
205
-     *
206
-     * @param callable $callback
207
-     *
208
-     * @return $this
209
-     */
210
-    public function each(callable $callback)
211
-    {
212
-        $this->initializeProxy();
213
-
214
-        return parent::each($callback);
215
-    }
216
-
217
-    /**
218
-     * Create a new collection consisting of every n-th element.
219
-     *
220
-     * @param int $step
221
-     * @param int $offset
222
-     *
223
-     * @return static
224
-     */
225
-    public function every($step, $offset = 0)
226
-    {
227
-        $this->initializeProxy();
228
-
229
-        return parent::every($step, $offset);
230
-    }
231
-
232
-    /**
233
-     * Get all items except for those with the specified keys.
234
-     *
235
-     * @param mixed $keys
236
-     *
237
-     * @return static
238
-     */
239
-    public function except($keys)
240
-    {
241
-        $this->initializeProxy();
242
-
243
-        return parent::except($keys);
244
-    }
245
-
246
-    /**
247
-     * Run a filter over each of the items.
248
-     *
249
-     * @param callable|null $callback
250
-     *
251
-     * @return static
252
-     */
253
-    public function filter(callable $callback = null)
254
-    {
255
-        $this->initializeProxy();
256
-
257
-        return parent::filter($callback);
258
-    }
259
-
260
-    /**
261
-     * Filter items by the given key value pair.
262
-     *
263
-     * @param string $key
264
-     * @param mixed  $operator
265
-     * @param mixed  $value
266
-     *
267
-     * @return static
268
-     */
269
-    public function where($key, $operator, $value = null)
270
-    {
271
-        $this->initializeProxy();
272
-
273
-        return parent::where($key, $operator, $value);
274
-    }
275
-
276
-    /**
277
-     * Filter items by the given key value pair using strict comparison.
278
-     *
279
-     * @param string $key
280
-     * @param mixed  $value
281
-     *
282
-     * @return static
283
-     */
284
-    public function whereStrict($key, $value)
285
-    {
286
-        $this->initializeProxy();
287
-
288
-        return parent::whereStrict($key, $value);
289
-    }
290
-
291
-    /**
292
-     * Filter items by the given key value pair.
293
-     *
294
-     * @param string $key
295
-     * @param mixed  $values
296
-     * @param bool   $strict
297
-     *
298
-     * @return static
299
-     */
300
-    public function whereIn($key, $values, $strict = false)
301
-    {
302
-        $this->initializeProxy();
303
-
304
-        return parent::whereIn($key, $values, $strict);
305
-    }
306
-
307
-    /**
308
-     * Filter items by the given key value pair using strict comparison.
309
-     *
310
-     * @param string $key
311
-     * @param mixed  $values
312
-     *
313
-     * @return static
314
-     */
315
-    public function whereInStrict($key, $values)
316
-    {
317
-        $this->initializeProxy();
318
-
319
-        return parent::whereInStrict($key, $values);
320
-    }
321
-
322
-    /**
323
-     * Get the first item from the collection.
324
-     *
325
-     * @param callable|null $callback
326
-     * @param mixed         $default
327
-     *
328
-     * @return mixed
329
-     */
330
-    public function first(callable $callback = null, $default = null)
331
-    {
332
-        // TODO Consider partial loading
333
-        $this->initializeProxy();
334
-
335
-        return parent::first($callback, $default);
336
-    }
337
-
338
-    /**
339
-     * Get a flattened array of the items in the collection.
340
-     *
341
-     * @param int $depth
342
-     *
343
-     * @return static
344
-     */
345
-    public function flatten($depth = INF)
346
-    {
347
-        $this->initializeProxy();
348
-
349
-        return parent::flatten($depth);
350
-    }
351
-
352
-    /**
353
-     * Flip the items in the collection.
354
-     *
355
-     * @return static
356
-     */
357
-    public function flip()
358
-    {
359
-        $this->initializeProxy();
360
-
361
-        return parent::flip();
362
-    }
363
-
364
-    /**
365
-     * Remove an item from the collection by key.
366
-     *
367
-     * @param string|array $keys
368
-     *
369
-     * @return $this
370
-     */
371
-    public function forget($keys)
372
-    {
373
-        // TODO, we could consider these as
374
-        // 'pending deletion', the same way that
375
-        // we treat added items
376
-        $this->initializeProxy();
377
-
378
-        return parent::forget($keys);
379
-    }
380
-
381
-    /**
382
-     * Get an item from the collection by key.
383
-     *
384
-     * @param mixed $key
385
-     * @param mixed $default
386
-     *
387
-     * @return mixed
388
-     */
389
-    public function get($key, $default = null)
390
-    {
391
-        // TODO : We could also consider partial loading
392
-        // here
393
-        $this->initializeProxy();
394
-
395
-        return parent::get($key, $default);
396
-    }
397
-
398
-    /**
399
-     * Group an associative array by a field or using a callback.
400
-     *
401
-     * @param callable|string $groupBy
402
-     * @param bool            $preserveKeys
403
-     *
404
-     * @return static
405
-     */
406
-    public function groupBy($groupBy, $preserveKeys = false)
407
-    {
408
-        $this->initializeProxy();
409
-
410
-        return parent::groupBy($groupBy, $preserveKeys);
411
-    }
412
-
413
-    /**
414
-     * Key an associative array by a field or using a callback.
415
-     *
416
-     * @param callable|string $keyBy
417
-     *
418
-     * @return static
419
-     */
420
-    public function keyBy($keyBy)
421
-    {
422
-        $this->initializeProxy();
423
-
424
-        return parent::keyBy($keyBy);
425
-    }
426
-
427
-    /**
428
-     * Determine if an item exists in the collection by key.
429
-     *
430
-     * @param mixed $key
431
-     *
432
-     * @return bool
433
-     */
434
-    public function has($key)
435
-    {
436
-        // TODO : we could do automagic here by directly
437
-        // calling the database if the collection hasn't
438
-        // been initialized yet.
439
-        // Potential issue is that several calls to this
440
-        // could cause a lot queries vs a single get query.
441
-        $this->initializeProxy();
442
-
443
-        return parent::has($key);
444
-    }
445
-
446
-    /**
447
-     * Concatenate values of a given key as a string.
448
-     *
449
-     * @param string $value
450
-     * @param string $glue
451
-     *
452
-     * @return string
453
-     */
454
-    public function implode($value, $glue = null)
455
-    {
456
-        $this->initializeProxy();
457
-
458
-        return parent::implode($value, $glue);
459
-    }
460
-
461
-    /**
462
-     * Intersect the collection with the given items.
463
-     *
464
-     * @param mixed $items
465
-     *
466
-     * @return static
467
-     */
468
-    public function intersect($items)
469
-    {
470
-        $this->initializeProxy();
471
-
472
-        return parent::intersect($items);
473
-    }
474
-
475
-    /**
476
-     * Determine if the collection is empty or not.
477
-     *
478
-     * @return bool
479
-     */
480
-    public function isEmpty()
481
-    {
482
-        $this->initializeProxy();
483
-
484
-        return parent::isEmpty();
485
-    }
486
-
487
-    /**
488
-     * Get the keys of the collection items.
489
-     *
490
-     * @return static
491
-     */
492
-    public function keys()
493
-    {
494
-        $this->initializeProxy();
495
-
496
-        return parent::keys();
497
-    }
498
-
499
-    /**
500
-     * Get the last item from the collection.
501
-     *
502
-     * @param callable|null $callback
503
-     * @param mixed         $default
504
-     *
505
-     * @return mixed
506
-     */
507
-    public function last(callable $callback = null, $default = null)
508
-    {
509
-        // TODO : we could do partial loading there as well
510
-        $this->initializeProxy();
511
-
512
-        return parent::last($callback, $default);
513
-    }
514
-
515
-    /**
516
-     * Get the values of a given key.
517
-     *
518
-     * @param string      $value
519
-     * @param string|null $key
520
-     *
521
-     * @return static
522
-     */
523
-    public function pluck($value, $key = null)
524
-    {
525
-        // TODO : automagic call to QB if not initialized
526
-        $this->initializeProxy();
527
-
528
-        return parent::pluck($value, $key);
529
-    }
530
-
531
-    /**
532
-     * Run a map over each of the items.
533
-     *
534
-     * @param callable $callback
535
-     *
536
-     * @return static
537
-     */
538
-    public function map(callable $callback)
539
-    {
540
-        $this->initializeProxy();
541
-
542
-        return parent::map($callback);
543
-    }
544
-
545
-    /**
546
-     * Run an associative map over each of the items.
547
-     *
548
-     * The callback should return an associative array with a single key/value pair.
549
-     *
550
-     * @param callable $callback
551
-     *
552
-     * @return static
553
-     */
554
-    public function mapWithKeys(callable $callback)
555
-    {
556
-        $this->initializeProxy();
557
-
558
-        return parent::mapWithKeys($callback);
559
-    }
560
-
561
-    /**
562
-     * Map a collection and flatten the result by a single level.
563
-     *
564
-     * @param callable $callback
565
-     *
566
-     * @return static
567
-     */
568
-    public function flatMap(callable $callback)
569
-    {
570
-        $this->initializeProxy();
571
-
572
-        return parent::flatMap($callback);
573
-    }
574
-
575
-    /**
576
-     * Get the max value of a given key.
577
-     *
578
-     * @param callable|string|null $callback
579
-     *
580
-     * @return mixed
581
-     */
582
-    public function max($callback = null)
583
-    {
584
-        $this->initializeProxy();
585
-
586
-        return parent::max($callback);
587
-    }
588
-
589
-    /**
590
-     * Merge the collection with the given items.
591
-     *
592
-     * @param mixed $items
593
-     *
594
-     * @return static
595
-     */
596
-    public function merge($items)
597
-    {
598
-        // TODO : Check if the EntityCollection
599
-        // returns a native Collection, as it
600
-        // is what we want here
601
-        $this->initializeProxy();
602
-
603
-        return parent::merge($items);
604
-    }
605
-
606
-    /**
607
-     * Create a collection by using this collection for keys and another for its values.
608
-     *
609
-     * @param mixed $values
610
-     *
611
-     * @return static
612
-     */
613
-    public function combine($values)
614
-    {
615
-        // TODO : Check if the EntityCollection
616
-        // returns a native Collection, as it
617
-        // is what we want here
618
-        $this->initializeProxy();
619
-
620
-        return parent::combine($values);
621
-    }
622
-
623
-    /**
624
-     * Union the collection with the given items.
625
-     *
626
-     * @param mixed $items
627
-     *
628
-     * @return static
629
-     */
630
-    public function union($items)
631
-    {
632
-        // TODO : Check if the EntityCollection
633
-        // returns a native Collection, as it
634
-        // is what we want here
635
-        $this->initializeProxy();
636
-
637
-        return parent::union($items);
638
-    }
639
-
640
-    /**
641
-     * Get the min value of a given key.
642
-     *
643
-     * @param callable|string|null $callback
644
-     *
645
-     * @return mixed
646
-     */
647
-    public function min($callback = null)
648
-    {
649
-        // TODO : we could rely on the QB
650
-        // for thos, if initialization has not
651
-        // take place yet
652
-        $this->initializeProxy();
653
-
654
-        return parent::min($callback);
655
-    }
656
-
657
-    /**
658
-     * Get the items with the specified keys.
659
-     *
660
-     * @param mixed $keys
661
-     *
662
-     * @return static
663
-     */
664
-    public function only($keys)
665
-    {
666
-        // TODO : we could rely on the QB if
667
-        // the collection hasn't been initialized yet
668
-        $this->initializeProxy();
669
-
670
-        return parent::only($keys);
671
-    }
672
-
673
-    /**
674
-     * "Paginate" the collection by slicing it into a smaller collection.
675
-     *
676
-     * @param int $page
677
-     * @param int $perPage
678
-     *
679
-     * @return static
680
-     */
681
-    public function forPage($page, $perPage)
682
-    {
683
-        // TODO : check possibility of partial loading
684
-        // if not initialized
685
-        $this->initializeProxy();
686
-
687
-        return parent::forPage($page, $perPage);
688
-    }
689
-
690
-    /**
691
-     * Partition the collection into two arrays using the given callback or key.
692
-     *
693
-     * @param callable|string $callback
694
-     *
695
-     * @return static
696
-     */
697
-    public function partition($callback)
698
-    {
699
-        $this->initializeProxy();
700
-
701
-        return parent::partition($callback);
702
-    }
703
-
704
-    /**
705
-     * Pass the collection to the given callback and return the result.
706
-     *
707
-     * @param callable $callback
708
-     *
709
-     * @return mixed
710
-     */
711
-    public function pipe(callable $callback)
712
-    {
713
-        $this->initializeProxy();
714
-
715
-        return parent::pipe($callback);
716
-    }
717
-
718
-    /**
719
-     * Get and remove the last item from the collection.
720
-     *
721
-     * @return mixed
722
-     */
723
-    public function pop()
724
-    {
725
-        $this->initializeProxy();
726
-
727
-        return parent::pop();
728
-    }
729
-
730
-    /**
731
-     * Push an item onto the beginning of the collection.
732
-     *
733
-     * @param mixed $value
734
-     * @param mixed $key
735
-     *
736
-     * @return $this
737
-     */
738
-    public function prepend($value, $key = null)
739
-    {
740
-        // TODO : partial adding of values.
741
-        // we could have a $prepended , and $pushed arrays
742
-        // which we would combine at full initialization
743
-
744
-        $this->initializeProxy();
745
-
746
-        return parent::prepend($value, $key);
747
-    }
748
-
749
-    /**
750
-     * Push an item onto the end of the collection.
751
-     *
752
-     * @param mixed $value
753
-     *
754
-     * @return $this
755
-     */
756
-    public function push($value)
757
-    {
758
-        // TODO : partial adding of values.
759
-        // we could have a $prepended , and $pushed arrays
760
-        // which we would combine at full initialization
761
-
762
-        $this->initializeProxy();
763
-
764
-        return parent::push($value);
765
-    }
766
-
767
-    /**
768
-     * Get and remove an item from the collection.
769
-     *
770
-     * @param mixed $key
771
-     * @param mixed $default
772
-     *
773
-     * @return mixed
774
-     */
775
-    public function pull($key, $default = null)
776
-    {
777
-        // TODO : QB query if the collection
778
-        // hasn't been initialized yet
779
-
780
-        $this->initializeProxy();
781
-
782
-        return parent::pull($key, $default);
783
-    }
784
-
785
-    /**
786
-     * Put an item in the collection by key.
787
-     *
788
-     * @param mixed $key
789
-     * @param mixed $value
790
-     *
791
-     * @return $this
792
-     */
793
-    public function put($key, $value)
794
-    {
795
-        // TODO : Partial loading ?
796
-
797
-        $this->initializeProxy();
798
-
799
-        return parent::put($key, $value);
800
-    }
801
-
802
-    /**
803
-     * Get one or more items randomly from the collection.
804
-     *
805
-     * @param int $amount
806
-     *
807
-     * @throws \InvalidArgumentException
808
-     *
809
-     * @return mixed
810
-     */
811
-    public function random($amount = 1)
812
-    {
813
-        // TODO : we could optimize this by only
814
-        // fetching the keys from the database
815
-        // and performing partial loading
816
-
817
-        $this->initializeProxy();
818
-
819
-        return parent::random($amount);
820
-    }
821
-
822
-    /**
823
-     * Reduce the collection to a single value.
824
-     *
825
-     * @param callable $callback
826
-     * @param mixed    $initial
827
-     *
828
-     * @return mixed
829
-     */
830
-    public function reduce(callable $callback, $initial = null)
831
-    {
832
-        $this->initializeProxy();
833
-
834
-        return parent::reduce($callback, $initial);
835
-    }
836
-
837
-    /**
838
-     * Create a collection of all elements that do not pass a given truth test.
839
-     *
840
-     * @param callable|mixed $callback
841
-     *
842
-     * @return static
843
-     */
844
-    public function reject($callback)
845
-    {
846
-        $this->initializeProxy();
847
-
848
-        return parent::reject($callback);
849
-    }
850
-
851
-    /**
852
-     * Reverse items order.
853
-     *
854
-     * @return static
855
-     */
856
-    public function reverse()
857
-    {
858
-        $this->initializeProxy();
859
-
860
-        return parent::reverse();
861
-    }
862
-
863
-    /**
864
-     * Search the collection for a given value and return the corresponding key if successful.
865
-     *
866
-     * @param mixed $value
867
-     * @param bool  $strict
868
-     *
869
-     * @return mixed
870
-     */
871
-    public function search($value, $strict = false)
872
-    {
873
-        $this->initializeProxy();
874
-
875
-        return parent::search($value, $strict);
876
-    }
877
-
878
-    /**
879
-     * Get and remove the first item from the collection.
880
-     *
881
-     * @return mixed
882
-     */
883
-    public function shift()
884
-    {
885
-        // Todo : Partial Removing
886
-        // we could have a pending removal array
887
-        $this->initializeProxy();
888
-
889
-        return parent::shift();
890
-    }
891
-
892
-    /**
893
-     * Shuffle the items in the collection.
894
-     *
895
-     * @param int $seed
896
-     *
897
-     * @return static
898
-     */
899
-    public function shuffle($seed = null)
900
-    {
901
-        $this->initializeProxy();
902
-
903
-        return parent::shuffle($seed);
904
-    }
905
-
906
-    /**
907
-     * Slice the underlying collection array.
908
-     *
909
-     * @param int $offset
910
-     * @param int $length
911
-     *
912
-     * @return static
913
-     */
914
-    public function slice($offset, $length = null)
915
-    {
916
-        $this->initializeProxy();
917
-
918
-        return parent::slice($offset, $length);
919
-    }
920
-
921
-    /**
922
-     * Split a collection into a certain number of groups.
923
-     *
924
-     * @param int $numberOfGroups
925
-     *
926
-     * @return static
927
-     */
928
-    public function split($numberOfGroups)
929
-    {
930
-        $this->initializeProxy();
931
-
932
-        return parent::split($numberOfGroups);
933
-    }
934
-
935
-    /**
936
-     * Chunk the underlying collection array.
937
-     *
938
-     * @param int $size
939
-     *
940
-     * @return static
941
-     */
942
-    public function chunk($size)
943
-    {
944
-        // TODO : partial loading ?
945
-        $this->initializeProxy();
946
-
947
-        return parent::chunk($size);
948
-    }
949
-
950
-    /**
951
-     * Sort through each item with a callback.
952
-     *
953
-     * @param callable|null $callback
954
-     *
955
-     * @return static
956
-     */
957
-    public function sort(callable $callback = null)
958
-    {
959
-        $this->initializeProxy();
960
-
961
-        return parent::sort($callback);
962
-    }
963
-
964
-    /**
965
-     * Sort the collection using the given callback.
966
-     *
967
-     * @param callable|string $callback
968
-     * @param int             $options
969
-     * @param bool            $descending
970
-     *
971
-     * @return static
972
-     */
973
-    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
974
-    {
975
-        $this->initializeProxy();
976
-
977
-        return parent::sort($callback, $options, $descending);
978
-    }
979
-
980
-    /**
981
-     * Splice a portion of the underlying collection array.
982
-     *
983
-     * @param int      $offset
984
-     * @param int|null $length
985
-     * @param mixed    $replacement
986
-     *
987
-     * @return static
988
-     */
989
-    public function splice($offset, $length = null, $replacement = [])
990
-    {
991
-        $this->initializeProxy();
992
-
993
-        return parent::splice($offset, $length, $replacement);
994
-    }
995
-
996
-    /**
997
-     * Get the sum of the given values.
998
-     *
999
-     * @param callable|string|null $callback
1000
-     *
1001
-     * @return mixed
1002
-     */
1003
-    public function sum($callback = null)
1004
-    {
1005
-        $this->initializeProxy();
1006
-
1007
-        return parent::sum($callback);
1008
-    }
1009
-
1010
-    /**
1011
-     * Take the first or last {$limit} items.
1012
-     *
1013
-     * @param int $limit
1014
-     *
1015
-     * @return static
1016
-     */
1017
-    public function take($limit)
1018
-    {
1019
-        // TODO: partial loading
1020
-        $this->initializeProxy();
1021
-
1022
-        return parent::take($limit);
1023
-    }
1024
-
1025
-    /**
1026
-     * Transform each item in the collection using a callback.
1027
-     *
1028
-     * @param callable $callback
1029
-     *
1030
-     * @return $this
1031
-     */
1032
-    public function transform(callable $callback)
1033
-    {
1034
-        $this->initializeProxy();
1035
-
1036
-        return parent::transform($callback);
1037
-    }
1038
-
1039
-    /**
1040
-     * Return only unique items from the collection array.
1041
-     *
1042
-     * @param string|callable|null $key
1043
-     * @param bool                 $strict
1044
-     *
1045
-     * @return static
1046
-     */
1047
-    public function unique($key = null, $strict = false)
1048
-    {
1049
-        $this->initializeProxy();
1050
-
1051
-        return parent::unique($key, $strict);
1052
-    }
1053
-
1054
-    /**
1055
-     * Reset the keys on the underlying array.
1056
-     *
1057
-     * @return static
1058
-     */
1059
-    public function values()
1060
-    {
1061
-        $this->initializeProxy();
1062
-
1063
-        return parent::values();
1064
-    }
1065
-
1066
-    /**
1067
-     * Zip the collection together with one or more arrays.
1068
-     *
1069
-     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1070
-     *      => [[1, 4], [2, 5], [3, 6]]
1071
-     *
1072
-     * @param mixed ...$items
1073
-     *
1074
-     * @return static
1075
-     */
1076
-    public function zip($items)
1077
-    {
1078
-        $this->initializeProxy();
1079
-
1080
-        return parent::zip($items);
1081
-    }
1082
-
1083
-    /**
1084
-     * Get the collection of items as a plain array.
1085
-     *
1086
-     * @return array
1087
-     */
1088
-    public function toArray()
1089
-    {
1090
-        // If this is called on all subsequent proxy,
1091
-        // this would eventually trigger all lazy loading,
1092
-        // which is NOT what we would expect...
1093
-        // TODO : must think of this.
1094
-        $this->initializeProxy();
1095
-
1096
-        return parent::toArray();
1097
-    }
1098
-
1099
-    /**
1100
-     * Convert the object into something JSON serializable.
1101
-     *
1102
-     * @return array
1103
-     */
1104
-    public function jsonSerialize()
1105
-    {
1106
-        // If this is called on all subsequent proxy,
1107
-        // this would eventually trigger all lazy loading,
1108
-        // which is NOT what we would expect...
1109
-        // TODO : must think of this.
1110
-        $this->initializeProxy();
1111
-
1112
-        return parent::jsonSerialize();
1113
-    }
1114
-
1115
-    /**
1116
-     * Get the collection of items as JSON.
1117
-     *
1118
-     * @param int $options
1119
-     *
1120
-     * @return string
1121
-     */
1122
-    public function toJson($options = 0)
1123
-    {
1124
-        // If this is called on all subsequent proxy,
1125
-        // this would eventually trigger all lazy loading,
1126
-        // which is NOT what we would expect...
1127
-        // TODO : must think of this.
1128
-        $this->initializeProxy();
1129
-
1130
-        return parent::toJson($options);
1131
-    }
1132
-
1133
-    /**
1134
-     * Get an iterator for the items.
1135
-     *
1136
-     * @return \ArrayIterator
1137
-     */
1138
-    public function getIterator()
1139
-    {
1140
-        $this->initializeProxy();
1141
-
1142
-        return parent::getIterator();
1143
-    }
1144
-
1145
-    /**
1146
-     * Get a CachingIterator instance.
1147
-     *
1148
-     * @param int $flags
1149
-     *
1150
-     * @return \CachingIterator
1151
-     */
1152
-    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1153
-    {
1154
-        $this->initializeProxy();
1155
-
1156
-        return parent::getCachingIterator($flags);
1157
-    }
1158
-
1159
-    /**
1160
-     * Count the number of items in the collection.
1161
-     *
1162
-     * @return int
1163
-     */
1164
-    public function count()
1165
-    {
1166
-        // TODO rely on QB if not initialized
1167
-        $this->initializeProxy();
1168
-
1169
-        return parent::count();
1170
-    }
1171
-
1172
-    /**
1173
-     * Get a base Support collection instance from this collection.
1174
-     *
1175
-     * @return \Illuminate\Support\Collection
1176
-     */
1177
-    public function toBase()
1178
-    {
1179
-        $this->initializeProxy();
1180
-
1181
-        return parent::toBase();
1182
-    }
1183
-
1184
-    /**
1185
-     * Determine if an item exists at an offset.
1186
-     *
1187
-     * @param mixed $key
1188
-     *
1189
-     * @return bool
1190
-     */
1191
-    public function offsetExists($key)
1192
-    {
1193
-        // TODO rely on QB if no collection
1194
-        // initialized
1195
-        $this->initializeProxy();
1196
-
1197
-        return parent::offsetExists($key);
1198
-    }
1199
-
1200
-    /**
1201
-     * Get an item at a given offset.
1202
-     *
1203
-     * @param mixed $key
1204
-     *
1205
-     * @return mixed
1206
-     */
1207
-    public function offsetGet($key)
1208
-    {
1209
-        // TODO rely on partial init if no collection
1210
-        // initialized
1211
-        $this->initializeProxy();
1212
-
1213
-        return parent::offsetGet($key);
1214
-    }
1215
-
1216
-    /**
1217
-     * Set the item at a given offset.
1218
-     *
1219
-     * @param mixed $key
1220
-     * @param mixed $value
1221
-     *
1222
-     * @return void
1223
-     */
1224
-    public function offsetSet($key, $value)
1225
-    {
1226
-        // TODO : think of the use of it into a ProxyCollection
1227
-        // context
1228
-        $this->initializeProxy();
1229
-
1230
-        return parent::offsetSet($key, $value);
1231
-    }
1232
-
1233
-    /**
1234
-     * Unset the item at a given offset.
1235
-     *
1236
-     * @param string $key
1237
-     *
1238
-     * @return void
1239
-     */
1240
-    public function offsetUnset($key)
1241
-    {
1242
-        // TODO : think of the use of it into a ProxyCollection
1243
-        // context
1244
-        $this->initializeProxy();
1245
-
1246
-        return parent::offsetUnset($key);
1247
-    }
1248
-
1249
-    /**
1250
-     * Dynamically handle calls to the class.
1251
-     *
1252
-     * @param string $method
1253
-     * @param array  $parameters
1254
-     *
1255
-     * @throws \BadMethodCallException
1256
-     *
1257
-     * @return mixed
1258
-     */
1259
-    public function __call($method, $parameters)
1260
-    {
1261
-        $this->initializeProxy();
1262
-
1263
-        return parent::__call($method, $parameters);
1264
-    }
12
+	/**
13
+	 * Indicate if the relationship has been lazy loaded.
14
+	 *
15
+	 * @var bool
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()) {
54
+			return true;
55
+		}
56
+
57
+		$relation = $this->relationshipMethod;
58
+		$entity = $this->parentEntity;
59
+
60
+		$entityMap = Manager::getMapper($entity)->getEntityMap();
61
+
62
+		$this->items = $entityMap->$relation($entity)->getResults($relation)->all() + $this->addedItems;
63
+
64
+		$this->relationshipLoaded = true;
65
+
66
+		return true;
67
+	}
68
+
69
+	/**
70
+	 * Retrieves current initialization status of the proxy.
71
+	 *
72
+	 * @return bool
73
+	 */
74
+	public function isProxyInitialized() : bool
75
+	{
76
+		return $this->relationshipLoaded;
77
+	}
78
+
79
+	/**
80
+	 * Get all of the items in the collection.
81
+	 *
82
+	 * @return array
83
+	 */
84
+	public function all()
85
+	{
86
+		$this->initializeProxy();
87
+
88
+		return parent::all();
89
+	}
90
+
91
+	/**
92
+	 * Get the average value of a given key.
93
+	 *
94
+	 * @param callable|string|null $callback
95
+	 *
96
+	 * @return mixed
97
+	 */
98
+	public function avg($callback = null)
99
+	{
100
+		$this->initializeProxy();
101
+
102
+		return parent::avg($callback);
103
+	}
104
+
105
+	/**
106
+	 * Get the median of a given key.
107
+	 *
108
+	 * @param null $key
109
+	 *
110
+	 * @return mixed|null
111
+	 */
112
+	public function median($key = null)
113
+	{
114
+		$this->initializeProxy();
115
+
116
+		return parent::median($key);
117
+	}
118
+
119
+	/**
120
+	 * Get the mode of a given key.
121
+	 *
122
+	 * @param mixed $key
123
+	 *
124
+	 * @return array
125
+	 */
126
+	public function mode($key = null)
127
+	{
128
+		$this->initializeProxy();
129
+
130
+		return parent::mode($key);
131
+	}
132
+
133
+	/**
134
+	 * Collapse the collection of items into a single array.
135
+	 *
136
+	 * @return static
137
+	 */
138
+	public function collapse()
139
+	{
140
+		$this->initializeProxy();
141
+
142
+		return parent::collapse();
143
+	}
144
+
145
+	/**
146
+	 * Determine if an item exists in the collection.
147
+	 *
148
+	 * @param mixed $key
149
+	 * @param mixed $value
150
+	 *
151
+	 * @return bool
152
+	 */
153
+	public function contains($key, $value = null)
154
+	{
155
+		$this->initializeProxy();
156
+
157
+		return parent::contains($key, $value);
158
+	}
159
+
160
+	/**
161
+	 * Determine if an item exists in the collection using strict comparison.
162
+	 *
163
+	 * @param mixed $key
164
+	 * @param mixed $value
165
+	 *
166
+	 * @return bool
167
+	 */
168
+	public function containsStrict($key, $value = null)
169
+	{
170
+		$this->initializeProxy();
171
+
172
+		return parent::containsStrict($key, $value);
173
+	}
174
+
175
+	/**
176
+	 * Get the items in the collection that are not present in the given items.
177
+	 *
178
+	 * @param mixed $items
179
+	 *
180
+	 * @return static
181
+	 */
182
+	public function diff($items)
183
+	{
184
+		$this->initializeProxy();
185
+
186
+		return parent::diff($items);
187
+	}
188
+
189
+	/**
190
+	 * Get the items in the collection whose keys are not present in the given items.
191
+	 *
192
+	 * @param mixed $items
193
+	 *
194
+	 * @return static
195
+	 */
196
+	public function diffKeys($items)
197
+	{
198
+		$this->initializeProxy();
199
+
200
+		return parent::diffKeys($items);
201
+	}
202
+
203
+	/**
204
+	 * Execute a callback over each item.
205
+	 *
206
+	 * @param callable $callback
207
+	 *
208
+	 * @return $this
209
+	 */
210
+	public function each(callable $callback)
211
+	{
212
+		$this->initializeProxy();
213
+
214
+		return parent::each($callback);
215
+	}
216
+
217
+	/**
218
+	 * Create a new collection consisting of every n-th element.
219
+	 *
220
+	 * @param int $step
221
+	 * @param int $offset
222
+	 *
223
+	 * @return static
224
+	 */
225
+	public function every($step, $offset = 0)
226
+	{
227
+		$this->initializeProxy();
228
+
229
+		return parent::every($step, $offset);
230
+	}
231
+
232
+	/**
233
+	 * Get all items except for those with the specified keys.
234
+	 *
235
+	 * @param mixed $keys
236
+	 *
237
+	 * @return static
238
+	 */
239
+	public function except($keys)
240
+	{
241
+		$this->initializeProxy();
242
+
243
+		return parent::except($keys);
244
+	}
245
+
246
+	/**
247
+	 * Run a filter over each of the items.
248
+	 *
249
+	 * @param callable|null $callback
250
+	 *
251
+	 * @return static
252
+	 */
253
+	public function filter(callable $callback = null)
254
+	{
255
+		$this->initializeProxy();
256
+
257
+		return parent::filter($callback);
258
+	}
259
+
260
+	/**
261
+	 * Filter items by the given key value pair.
262
+	 *
263
+	 * @param string $key
264
+	 * @param mixed  $operator
265
+	 * @param mixed  $value
266
+	 *
267
+	 * @return static
268
+	 */
269
+	public function where($key, $operator, $value = null)
270
+	{
271
+		$this->initializeProxy();
272
+
273
+		return parent::where($key, $operator, $value);
274
+	}
275
+
276
+	/**
277
+	 * Filter items by the given key value pair using strict comparison.
278
+	 *
279
+	 * @param string $key
280
+	 * @param mixed  $value
281
+	 *
282
+	 * @return static
283
+	 */
284
+	public function whereStrict($key, $value)
285
+	{
286
+		$this->initializeProxy();
287
+
288
+		return parent::whereStrict($key, $value);
289
+	}
290
+
291
+	/**
292
+	 * Filter items by the given key value pair.
293
+	 *
294
+	 * @param string $key
295
+	 * @param mixed  $values
296
+	 * @param bool   $strict
297
+	 *
298
+	 * @return static
299
+	 */
300
+	public function whereIn($key, $values, $strict = false)
301
+	{
302
+		$this->initializeProxy();
303
+
304
+		return parent::whereIn($key, $values, $strict);
305
+	}
306
+
307
+	/**
308
+	 * Filter items by the given key value pair using strict comparison.
309
+	 *
310
+	 * @param string $key
311
+	 * @param mixed  $values
312
+	 *
313
+	 * @return static
314
+	 */
315
+	public function whereInStrict($key, $values)
316
+	{
317
+		$this->initializeProxy();
318
+
319
+		return parent::whereInStrict($key, $values);
320
+	}
321
+
322
+	/**
323
+	 * Get the first item from the collection.
324
+	 *
325
+	 * @param callable|null $callback
326
+	 * @param mixed         $default
327
+	 *
328
+	 * @return mixed
329
+	 */
330
+	public function first(callable $callback = null, $default = null)
331
+	{
332
+		// TODO Consider partial loading
333
+		$this->initializeProxy();
334
+
335
+		return parent::first($callback, $default);
336
+	}
337
+
338
+	/**
339
+	 * Get a flattened array of the items in the collection.
340
+	 *
341
+	 * @param int $depth
342
+	 *
343
+	 * @return static
344
+	 */
345
+	public function flatten($depth = INF)
346
+	{
347
+		$this->initializeProxy();
348
+
349
+		return parent::flatten($depth);
350
+	}
351
+
352
+	/**
353
+	 * Flip the items in the collection.
354
+	 *
355
+	 * @return static
356
+	 */
357
+	public function flip()
358
+	{
359
+		$this->initializeProxy();
360
+
361
+		return parent::flip();
362
+	}
363
+
364
+	/**
365
+	 * Remove an item from the collection by key.
366
+	 *
367
+	 * @param string|array $keys
368
+	 *
369
+	 * @return $this
370
+	 */
371
+	public function forget($keys)
372
+	{
373
+		// TODO, we could consider these as
374
+		// 'pending deletion', the same way that
375
+		// we treat added items
376
+		$this->initializeProxy();
377
+
378
+		return parent::forget($keys);
379
+	}
380
+
381
+	/**
382
+	 * Get an item from the collection by key.
383
+	 *
384
+	 * @param mixed $key
385
+	 * @param mixed $default
386
+	 *
387
+	 * @return mixed
388
+	 */
389
+	public function get($key, $default = null)
390
+	{
391
+		// TODO : We could also consider partial loading
392
+		// here
393
+		$this->initializeProxy();
394
+
395
+		return parent::get($key, $default);
396
+	}
397
+
398
+	/**
399
+	 * Group an associative array by a field or using a callback.
400
+	 *
401
+	 * @param callable|string $groupBy
402
+	 * @param bool            $preserveKeys
403
+	 *
404
+	 * @return static
405
+	 */
406
+	public function groupBy($groupBy, $preserveKeys = false)
407
+	{
408
+		$this->initializeProxy();
409
+
410
+		return parent::groupBy($groupBy, $preserveKeys);
411
+	}
412
+
413
+	/**
414
+	 * Key an associative array by a field or using a callback.
415
+	 *
416
+	 * @param callable|string $keyBy
417
+	 *
418
+	 * @return static
419
+	 */
420
+	public function keyBy($keyBy)
421
+	{
422
+		$this->initializeProxy();
423
+
424
+		return parent::keyBy($keyBy);
425
+	}
426
+
427
+	/**
428
+	 * Determine if an item exists in the collection by key.
429
+	 *
430
+	 * @param mixed $key
431
+	 *
432
+	 * @return bool
433
+	 */
434
+	public function has($key)
435
+	{
436
+		// TODO : we could do automagic here by directly
437
+		// calling the database if the collection hasn't
438
+		// been initialized yet.
439
+		// Potential issue is that several calls to this
440
+		// could cause a lot queries vs a single get query.
441
+		$this->initializeProxy();
442
+
443
+		return parent::has($key);
444
+	}
445
+
446
+	/**
447
+	 * Concatenate values of a given key as a string.
448
+	 *
449
+	 * @param string $value
450
+	 * @param string $glue
451
+	 *
452
+	 * @return string
453
+	 */
454
+	public function implode($value, $glue = null)
455
+	{
456
+		$this->initializeProxy();
457
+
458
+		return parent::implode($value, $glue);
459
+	}
460
+
461
+	/**
462
+	 * Intersect the collection with the given items.
463
+	 *
464
+	 * @param mixed $items
465
+	 *
466
+	 * @return static
467
+	 */
468
+	public function intersect($items)
469
+	{
470
+		$this->initializeProxy();
471
+
472
+		return parent::intersect($items);
473
+	}
474
+
475
+	/**
476
+	 * Determine if the collection is empty or not.
477
+	 *
478
+	 * @return bool
479
+	 */
480
+	public function isEmpty()
481
+	{
482
+		$this->initializeProxy();
483
+
484
+		return parent::isEmpty();
485
+	}
486
+
487
+	/**
488
+	 * Get the keys of the collection items.
489
+	 *
490
+	 * @return static
491
+	 */
492
+	public function keys()
493
+	{
494
+		$this->initializeProxy();
495
+
496
+		return parent::keys();
497
+	}
498
+
499
+	/**
500
+	 * Get the last item from the collection.
501
+	 *
502
+	 * @param callable|null $callback
503
+	 * @param mixed         $default
504
+	 *
505
+	 * @return mixed
506
+	 */
507
+	public function last(callable $callback = null, $default = null)
508
+	{
509
+		// TODO : we could do partial loading there as well
510
+		$this->initializeProxy();
511
+
512
+		return parent::last($callback, $default);
513
+	}
514
+
515
+	/**
516
+	 * Get the values of a given key.
517
+	 *
518
+	 * @param string      $value
519
+	 * @param string|null $key
520
+	 *
521
+	 * @return static
522
+	 */
523
+	public function pluck($value, $key = null)
524
+	{
525
+		// TODO : automagic call to QB if not initialized
526
+		$this->initializeProxy();
527
+
528
+		return parent::pluck($value, $key);
529
+	}
530
+
531
+	/**
532
+	 * Run a map over each of the items.
533
+	 *
534
+	 * @param callable $callback
535
+	 *
536
+	 * @return static
537
+	 */
538
+	public function map(callable $callback)
539
+	{
540
+		$this->initializeProxy();
541
+
542
+		return parent::map($callback);
543
+	}
544
+
545
+	/**
546
+	 * Run an associative map over each of the items.
547
+	 *
548
+	 * The callback should return an associative array with a single key/value pair.
549
+	 *
550
+	 * @param callable $callback
551
+	 *
552
+	 * @return static
553
+	 */
554
+	public function mapWithKeys(callable $callback)
555
+	{
556
+		$this->initializeProxy();
557
+
558
+		return parent::mapWithKeys($callback);
559
+	}
560
+
561
+	/**
562
+	 * Map a collection and flatten the result by a single level.
563
+	 *
564
+	 * @param callable $callback
565
+	 *
566
+	 * @return static
567
+	 */
568
+	public function flatMap(callable $callback)
569
+	{
570
+		$this->initializeProxy();
571
+
572
+		return parent::flatMap($callback);
573
+	}
574
+
575
+	/**
576
+	 * Get the max value of a given key.
577
+	 *
578
+	 * @param callable|string|null $callback
579
+	 *
580
+	 * @return mixed
581
+	 */
582
+	public function max($callback = null)
583
+	{
584
+		$this->initializeProxy();
585
+
586
+		return parent::max($callback);
587
+	}
588
+
589
+	/**
590
+	 * Merge the collection with the given items.
591
+	 *
592
+	 * @param mixed $items
593
+	 *
594
+	 * @return static
595
+	 */
596
+	public function merge($items)
597
+	{
598
+		// TODO : Check if the EntityCollection
599
+		// returns a native Collection, as it
600
+		// is what we want here
601
+		$this->initializeProxy();
602
+
603
+		return parent::merge($items);
604
+	}
605
+
606
+	/**
607
+	 * Create a collection by using this collection for keys and another for its values.
608
+	 *
609
+	 * @param mixed $values
610
+	 *
611
+	 * @return static
612
+	 */
613
+	public function combine($values)
614
+	{
615
+		// TODO : Check if the EntityCollection
616
+		// returns a native Collection, as it
617
+		// is what we want here
618
+		$this->initializeProxy();
619
+
620
+		return parent::combine($values);
621
+	}
622
+
623
+	/**
624
+	 * Union the collection with the given items.
625
+	 *
626
+	 * @param mixed $items
627
+	 *
628
+	 * @return static
629
+	 */
630
+	public function union($items)
631
+	{
632
+		// TODO : Check if the EntityCollection
633
+		// returns a native Collection, as it
634
+		// is what we want here
635
+		$this->initializeProxy();
636
+
637
+		return parent::union($items);
638
+	}
639
+
640
+	/**
641
+	 * Get the min value of a given key.
642
+	 *
643
+	 * @param callable|string|null $callback
644
+	 *
645
+	 * @return mixed
646
+	 */
647
+	public function min($callback = null)
648
+	{
649
+		// TODO : we could rely on the QB
650
+		// for thos, if initialization has not
651
+		// take place yet
652
+		$this->initializeProxy();
653
+
654
+		return parent::min($callback);
655
+	}
656
+
657
+	/**
658
+	 * Get the items with the specified keys.
659
+	 *
660
+	 * @param mixed $keys
661
+	 *
662
+	 * @return static
663
+	 */
664
+	public function only($keys)
665
+	{
666
+		// TODO : we could rely on the QB if
667
+		// the collection hasn't been initialized yet
668
+		$this->initializeProxy();
669
+
670
+		return parent::only($keys);
671
+	}
672
+
673
+	/**
674
+	 * "Paginate" the collection by slicing it into a smaller collection.
675
+	 *
676
+	 * @param int $page
677
+	 * @param int $perPage
678
+	 *
679
+	 * @return static
680
+	 */
681
+	public function forPage($page, $perPage)
682
+	{
683
+		// TODO : check possibility of partial loading
684
+		// if not initialized
685
+		$this->initializeProxy();
686
+
687
+		return parent::forPage($page, $perPage);
688
+	}
689
+
690
+	/**
691
+	 * Partition the collection into two arrays using the given callback or key.
692
+	 *
693
+	 * @param callable|string $callback
694
+	 *
695
+	 * @return static
696
+	 */
697
+	public function partition($callback)
698
+	{
699
+		$this->initializeProxy();
700
+
701
+		return parent::partition($callback);
702
+	}
703
+
704
+	/**
705
+	 * Pass the collection to the given callback and return the result.
706
+	 *
707
+	 * @param callable $callback
708
+	 *
709
+	 * @return mixed
710
+	 */
711
+	public function pipe(callable $callback)
712
+	{
713
+		$this->initializeProxy();
714
+
715
+		return parent::pipe($callback);
716
+	}
717
+
718
+	/**
719
+	 * Get and remove the last item from the collection.
720
+	 *
721
+	 * @return mixed
722
+	 */
723
+	public function pop()
724
+	{
725
+		$this->initializeProxy();
726
+
727
+		return parent::pop();
728
+	}
729
+
730
+	/**
731
+	 * Push an item onto the beginning of the collection.
732
+	 *
733
+	 * @param mixed $value
734
+	 * @param mixed $key
735
+	 *
736
+	 * @return $this
737
+	 */
738
+	public function prepend($value, $key = null)
739
+	{
740
+		// TODO : partial adding of values.
741
+		// we could have a $prepended , and $pushed arrays
742
+		// which we would combine at full initialization
743
+
744
+		$this->initializeProxy();
745
+
746
+		return parent::prepend($value, $key);
747
+	}
748
+
749
+	/**
750
+	 * Push an item onto the end of the collection.
751
+	 *
752
+	 * @param mixed $value
753
+	 *
754
+	 * @return $this
755
+	 */
756
+	public function push($value)
757
+	{
758
+		// TODO : partial adding of values.
759
+		// we could have a $prepended , and $pushed arrays
760
+		// which we would combine at full initialization
761
+
762
+		$this->initializeProxy();
763
+
764
+		return parent::push($value);
765
+	}
766
+
767
+	/**
768
+	 * Get and remove an item from the collection.
769
+	 *
770
+	 * @param mixed $key
771
+	 * @param mixed $default
772
+	 *
773
+	 * @return mixed
774
+	 */
775
+	public function pull($key, $default = null)
776
+	{
777
+		// TODO : QB query if the collection
778
+		// hasn't been initialized yet
779
+
780
+		$this->initializeProxy();
781
+
782
+		return parent::pull($key, $default);
783
+	}
784
+
785
+	/**
786
+	 * Put an item in the collection by key.
787
+	 *
788
+	 * @param mixed $key
789
+	 * @param mixed $value
790
+	 *
791
+	 * @return $this
792
+	 */
793
+	public function put($key, $value)
794
+	{
795
+		// TODO : Partial loading ?
796
+
797
+		$this->initializeProxy();
798
+
799
+		return parent::put($key, $value);
800
+	}
801
+
802
+	/**
803
+	 * Get one or more items randomly from the collection.
804
+	 *
805
+	 * @param int $amount
806
+	 *
807
+	 * @throws \InvalidArgumentException
808
+	 *
809
+	 * @return mixed
810
+	 */
811
+	public function random($amount = 1)
812
+	{
813
+		// TODO : we could optimize this by only
814
+		// fetching the keys from the database
815
+		// and performing partial loading
816
+
817
+		$this->initializeProxy();
818
+
819
+		return parent::random($amount);
820
+	}
821
+
822
+	/**
823
+	 * Reduce the collection to a single value.
824
+	 *
825
+	 * @param callable $callback
826
+	 * @param mixed    $initial
827
+	 *
828
+	 * @return mixed
829
+	 */
830
+	public function reduce(callable $callback, $initial = null)
831
+	{
832
+		$this->initializeProxy();
833
+
834
+		return parent::reduce($callback, $initial);
835
+	}
836
+
837
+	/**
838
+	 * Create a collection of all elements that do not pass a given truth test.
839
+	 *
840
+	 * @param callable|mixed $callback
841
+	 *
842
+	 * @return static
843
+	 */
844
+	public function reject($callback)
845
+	{
846
+		$this->initializeProxy();
847
+
848
+		return parent::reject($callback);
849
+	}
850
+
851
+	/**
852
+	 * Reverse items order.
853
+	 *
854
+	 * @return static
855
+	 */
856
+	public function reverse()
857
+	{
858
+		$this->initializeProxy();
859
+
860
+		return parent::reverse();
861
+	}
862
+
863
+	/**
864
+	 * Search the collection for a given value and return the corresponding key if successful.
865
+	 *
866
+	 * @param mixed $value
867
+	 * @param bool  $strict
868
+	 *
869
+	 * @return mixed
870
+	 */
871
+	public function search($value, $strict = false)
872
+	{
873
+		$this->initializeProxy();
874
+
875
+		return parent::search($value, $strict);
876
+	}
877
+
878
+	/**
879
+	 * Get and remove the first item from the collection.
880
+	 *
881
+	 * @return mixed
882
+	 */
883
+	public function shift()
884
+	{
885
+		// Todo : Partial Removing
886
+		// we could have a pending removal array
887
+		$this->initializeProxy();
888
+
889
+		return parent::shift();
890
+	}
891
+
892
+	/**
893
+	 * Shuffle the items in the collection.
894
+	 *
895
+	 * @param int $seed
896
+	 *
897
+	 * @return static
898
+	 */
899
+	public function shuffle($seed = null)
900
+	{
901
+		$this->initializeProxy();
902
+
903
+		return parent::shuffle($seed);
904
+	}
905
+
906
+	/**
907
+	 * Slice the underlying collection array.
908
+	 *
909
+	 * @param int $offset
910
+	 * @param int $length
911
+	 *
912
+	 * @return static
913
+	 */
914
+	public function slice($offset, $length = null)
915
+	{
916
+		$this->initializeProxy();
917
+
918
+		return parent::slice($offset, $length);
919
+	}
920
+
921
+	/**
922
+	 * Split a collection into a certain number of groups.
923
+	 *
924
+	 * @param int $numberOfGroups
925
+	 *
926
+	 * @return static
927
+	 */
928
+	public function split($numberOfGroups)
929
+	{
930
+		$this->initializeProxy();
931
+
932
+		return parent::split($numberOfGroups);
933
+	}
934
+
935
+	/**
936
+	 * Chunk the underlying collection array.
937
+	 *
938
+	 * @param int $size
939
+	 *
940
+	 * @return static
941
+	 */
942
+	public function chunk($size)
943
+	{
944
+		// TODO : partial loading ?
945
+		$this->initializeProxy();
946
+
947
+		return parent::chunk($size);
948
+	}
949
+
950
+	/**
951
+	 * Sort through each item with a callback.
952
+	 *
953
+	 * @param callable|null $callback
954
+	 *
955
+	 * @return static
956
+	 */
957
+	public function sort(callable $callback = null)
958
+	{
959
+		$this->initializeProxy();
960
+
961
+		return parent::sort($callback);
962
+	}
963
+
964
+	/**
965
+	 * Sort the collection using the given callback.
966
+	 *
967
+	 * @param callable|string $callback
968
+	 * @param int             $options
969
+	 * @param bool            $descending
970
+	 *
971
+	 * @return static
972
+	 */
973
+	public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
974
+	{
975
+		$this->initializeProxy();
976
+
977
+		return parent::sort($callback, $options, $descending);
978
+	}
979
+
980
+	/**
981
+	 * Splice a portion of the underlying collection array.
982
+	 *
983
+	 * @param int      $offset
984
+	 * @param int|null $length
985
+	 * @param mixed    $replacement
986
+	 *
987
+	 * @return static
988
+	 */
989
+	public function splice($offset, $length = null, $replacement = [])
990
+	{
991
+		$this->initializeProxy();
992
+
993
+		return parent::splice($offset, $length, $replacement);
994
+	}
995
+
996
+	/**
997
+	 * Get the sum of the given values.
998
+	 *
999
+	 * @param callable|string|null $callback
1000
+	 *
1001
+	 * @return mixed
1002
+	 */
1003
+	public function sum($callback = null)
1004
+	{
1005
+		$this->initializeProxy();
1006
+
1007
+		return parent::sum($callback);
1008
+	}
1009
+
1010
+	/**
1011
+	 * Take the first or last {$limit} items.
1012
+	 *
1013
+	 * @param int $limit
1014
+	 *
1015
+	 * @return static
1016
+	 */
1017
+	public function take($limit)
1018
+	{
1019
+		// TODO: partial loading
1020
+		$this->initializeProxy();
1021
+
1022
+		return parent::take($limit);
1023
+	}
1024
+
1025
+	/**
1026
+	 * Transform each item in the collection using a callback.
1027
+	 *
1028
+	 * @param callable $callback
1029
+	 *
1030
+	 * @return $this
1031
+	 */
1032
+	public function transform(callable $callback)
1033
+	{
1034
+		$this->initializeProxy();
1035
+
1036
+		return parent::transform($callback);
1037
+	}
1038
+
1039
+	/**
1040
+	 * Return only unique items from the collection array.
1041
+	 *
1042
+	 * @param string|callable|null $key
1043
+	 * @param bool                 $strict
1044
+	 *
1045
+	 * @return static
1046
+	 */
1047
+	public function unique($key = null, $strict = false)
1048
+	{
1049
+		$this->initializeProxy();
1050
+
1051
+		return parent::unique($key, $strict);
1052
+	}
1053
+
1054
+	/**
1055
+	 * Reset the keys on the underlying array.
1056
+	 *
1057
+	 * @return static
1058
+	 */
1059
+	public function values()
1060
+	{
1061
+		$this->initializeProxy();
1062
+
1063
+		return parent::values();
1064
+	}
1065
+
1066
+	/**
1067
+	 * Zip the collection together with one or more arrays.
1068
+	 *
1069
+	 * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1070
+	 *      => [[1, 4], [2, 5], [3, 6]]
1071
+	 *
1072
+	 * @param mixed ...$items
1073
+	 *
1074
+	 * @return static
1075
+	 */
1076
+	public function zip($items)
1077
+	{
1078
+		$this->initializeProxy();
1079
+
1080
+		return parent::zip($items);
1081
+	}
1082
+
1083
+	/**
1084
+	 * Get the collection of items as a plain array.
1085
+	 *
1086
+	 * @return array
1087
+	 */
1088
+	public function toArray()
1089
+	{
1090
+		// If this is called on all subsequent proxy,
1091
+		// this would eventually trigger all lazy loading,
1092
+		// which is NOT what we would expect...
1093
+		// TODO : must think of this.
1094
+		$this->initializeProxy();
1095
+
1096
+		return parent::toArray();
1097
+	}
1098
+
1099
+	/**
1100
+	 * Convert the object into something JSON serializable.
1101
+	 *
1102
+	 * @return array
1103
+	 */
1104
+	public function jsonSerialize()
1105
+	{
1106
+		// If this is called on all subsequent proxy,
1107
+		// this would eventually trigger all lazy loading,
1108
+		// which is NOT what we would expect...
1109
+		// TODO : must think of this.
1110
+		$this->initializeProxy();
1111
+
1112
+		return parent::jsonSerialize();
1113
+	}
1114
+
1115
+	/**
1116
+	 * Get the collection of items as JSON.
1117
+	 *
1118
+	 * @param int $options
1119
+	 *
1120
+	 * @return string
1121
+	 */
1122
+	public function toJson($options = 0)
1123
+	{
1124
+		// If this is called on all subsequent proxy,
1125
+		// this would eventually trigger all lazy loading,
1126
+		// which is NOT what we would expect...
1127
+		// TODO : must think of this.
1128
+		$this->initializeProxy();
1129
+
1130
+		return parent::toJson($options);
1131
+	}
1132
+
1133
+	/**
1134
+	 * Get an iterator for the items.
1135
+	 *
1136
+	 * @return \ArrayIterator
1137
+	 */
1138
+	public function getIterator()
1139
+	{
1140
+		$this->initializeProxy();
1141
+
1142
+		return parent::getIterator();
1143
+	}
1144
+
1145
+	/**
1146
+	 * Get a CachingIterator instance.
1147
+	 *
1148
+	 * @param int $flags
1149
+	 *
1150
+	 * @return \CachingIterator
1151
+	 */
1152
+	public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1153
+	{
1154
+		$this->initializeProxy();
1155
+
1156
+		return parent::getCachingIterator($flags);
1157
+	}
1158
+
1159
+	/**
1160
+	 * Count the number of items in the collection.
1161
+	 *
1162
+	 * @return int
1163
+	 */
1164
+	public function count()
1165
+	{
1166
+		// TODO rely on QB if not initialized
1167
+		$this->initializeProxy();
1168
+
1169
+		return parent::count();
1170
+	}
1171
+
1172
+	/**
1173
+	 * Get a base Support collection instance from this collection.
1174
+	 *
1175
+	 * @return \Illuminate\Support\Collection
1176
+	 */
1177
+	public function toBase()
1178
+	{
1179
+		$this->initializeProxy();
1180
+
1181
+		return parent::toBase();
1182
+	}
1183
+
1184
+	/**
1185
+	 * Determine if an item exists at an offset.
1186
+	 *
1187
+	 * @param mixed $key
1188
+	 *
1189
+	 * @return bool
1190
+	 */
1191
+	public function offsetExists($key)
1192
+	{
1193
+		// TODO rely on QB if no collection
1194
+		// initialized
1195
+		$this->initializeProxy();
1196
+
1197
+		return parent::offsetExists($key);
1198
+	}
1199
+
1200
+	/**
1201
+	 * Get an item at a given offset.
1202
+	 *
1203
+	 * @param mixed $key
1204
+	 *
1205
+	 * @return mixed
1206
+	 */
1207
+	public function offsetGet($key)
1208
+	{
1209
+		// TODO rely on partial init if no collection
1210
+		// initialized
1211
+		$this->initializeProxy();
1212
+
1213
+		return parent::offsetGet($key);
1214
+	}
1215
+
1216
+	/**
1217
+	 * Set the item at a given offset.
1218
+	 *
1219
+	 * @param mixed $key
1220
+	 * @param mixed $value
1221
+	 *
1222
+	 * @return void
1223
+	 */
1224
+	public function offsetSet($key, $value)
1225
+	{
1226
+		// TODO : think of the use of it into a ProxyCollection
1227
+		// context
1228
+		$this->initializeProxy();
1229
+
1230
+		return parent::offsetSet($key, $value);
1231
+	}
1232
+
1233
+	/**
1234
+	 * Unset the item at a given offset.
1235
+	 *
1236
+	 * @param string $key
1237
+	 *
1238
+	 * @return void
1239
+	 */
1240
+	public function offsetUnset($key)
1241
+	{
1242
+		// TODO : think of the use of it into a ProxyCollection
1243
+		// context
1244
+		$this->initializeProxy();
1245
+
1246
+		return parent::offsetUnset($key);
1247
+	}
1248
+
1249
+	/**
1250
+	 * Dynamically handle calls to the class.
1251
+	 *
1252
+	 * @param string $method
1253
+	 * @param array  $parameters
1254
+	 *
1255
+	 * @throws \BadMethodCallException
1256
+	 *
1257
+	 * @return mixed
1258
+	 */
1259
+	public function __call($method, $parameters)
1260
+	{
1261
+		$this->initializeProxy();
1262
+
1263
+		return parent::__call($method, $parameters);
1264
+	}
1265 1265
 }
Please login to merge, or discard this patch.
src/MappableTrait.php 1 patch
Indentation   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -8,65 +8,65 @@
 block discarded – undo
8 8
  */
9 9
 trait MappableTrait
10 10
 {
11
-    /**
12
-     * The Entity's Attributes.
13
-     *
14
-     * @var array
15
-     */
16
-    //protected $attributes = [];
11
+	/**
12
+	 * The Entity's Attributes.
13
+	 *
14
+	 * @var array
15
+	 */
16
+	//protected $attributes = [];
17 17
 
18
-    /**
19
-     * Method used by the mapper to set the object
20
-     * attribute raw values (hydration).
21
-     *
22
-     * @param array $attributes
23
-     *
24
-     * @return void
25
-     */
26
-    public function setEntityAttributes(array $attributes)
27
-    {
28
-        $this->attributes = $attributes;
29
-    }
18
+	/**
19
+	 * Method used by the mapper to set the object
20
+	 * attribute raw values (hydration).
21
+	 *
22
+	 * @param array $attributes
23
+	 *
24
+	 * @return void
25
+	 */
26
+	public function setEntityAttributes(array $attributes)
27
+	{
28
+		$this->attributes = $attributes;
29
+	}
30 30
 
31
-    /**
32
-     * Method used by the mapper to get the
33
-     * raw object's values.
34
-     *
35
-     * @return array
36
-     */
37
-    public function getEntityAttributes()
38
-    {
39
-        return $this->attributes;
40
-    }
31
+	/**
32
+	 * Method used by the mapper to get the
33
+	 * raw object's values.
34
+	 *
35
+	 * @return array
36
+	 */
37
+	public function getEntityAttributes()
38
+	{
39
+		return $this->attributes;
40
+	}
41 41
 
42
-    /**
43
-     * Method used by the mapper to set raw
44
-     * key-value pair.
45
-     *
46
-     * @param string $key
47
-     * @param string $value
48
-     *
49
-     * @return void
50
-     */
51
-    public function setEntityAttribute($key, $value)
52
-    {
53
-        $this->attributes[$key] = $value;
54
-    }
42
+	/**
43
+	 * Method used by the mapper to set raw
44
+	 * key-value pair.
45
+	 *
46
+	 * @param string $key
47
+	 * @param string $value
48
+	 *
49
+	 * @return void
50
+	 */
51
+	public function setEntityAttribute($key, $value)
52
+	{
53
+		$this->attributes[$key] = $value;
54
+	}
55 55
 
56
-    /**
57
-     * Method used by the mapper to get single
58
-     * key-value pair.
59
-     *
60
-     * @param string $key
61
-     *
62
-     * @return mixed|null
63
-     */
64
-    public function getEntityAttribute($key)
65
-    {
66
-        if (array_key_exists($key, $this->attributes)) {
67
-            return $this->attributes[$key];
68
-        } else {
69
-            return;
70
-        }
71
-    }
56
+	/**
57
+	 * Method used by the mapper to get single
58
+	 * key-value pair.
59
+	 *
60
+	 * @param string $key
61
+	 *
62
+	 * @return mixed|null
63
+	 */
64
+	public function getEntityAttribute($key)
65
+	{
66
+		if (array_key_exists($key, $this->attributes)) {
67
+			return $this->attributes[$key];
68
+		} else {
69
+			return;
70
+		}
71
+	}
72 72
 }
Please login to merge, or discard this patch.