Completed
Branch master (411345)
by Rémi
11:20
created
src/System/Query.php 2 patches
Indentation   +642 added lines, -642 removed lines patch added patch discarded remove patch
@@ -19,646 +19,646 @@
 block discarded – undo
19 19
  */
20 20
 class Query
21 21
 {
22
-    /**
23
-     * Mapper Instance
24
-     *
25
-     * @var \Analogue\ORM\System\Mapper
26
-     */
27
-    protected $mapper;
28
-
29
-    /**
30
-     * DB Adatper
31
-     *
32
-     * @var \Analogue\ORM\Drivers\DBAdapter
33
-     */
34
-    protected $adapter;
35
-
36
-    /**
37
-     * Query Builder Instance
38
-     *
39
-     * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
-     */
41
-    protected $query;
42
-
43
-    /**
44
-     * Entity Map Instance
45
-     *
46
-     * @var \Analogue\ORM\EntityMap
47
-     */
48
-    protected $entityMap;
49
-
50
-    /**
51
-     * The relationships that should be eager loaded.
52
-     *
53
-     * @var array
54
-     */
55
-    protected $eagerLoad = [];
56
-
57
-    /**
58
-     * All of the registered builder macros.
59
-     *
60
-     * @var array
61
-     */
62
-    protected $macros = [];
63
-
64
-    /**
65
-     * The methods that should be returned from query builder.
66
-     *
67
-     * @var array
68
-     */
69
-    protected $passthru = [
70
-        'toSql',
71
-        'lists',
72
-        'pluck',
73
-        'count',
74
-        'min',
75
-        'max',
76
-        'avg',
77
-        'sum',
78
-        'exists',
79
-        'getBindings',
80
-    ];
81
-
82
-    /**
83
-     * Query Builder Blacklist
84
-     */
85
-    protected $blacklist = [
86
-        'insert',
87
-        'insertGetId',
88
-        'lock',
89
-        'lockForUpdate',
90
-        'sharedLock',
91
-        'update',
92
-        'increment',
93
-        'decrement',
94
-        'delete',
95
-        'truncate',
96
-        'raw',
97
-    ];
98
-
99
-    /**
100
-     * Create a new Analogue Query Builder instance.
101
-     *
102
-     * @param  Mapper    $mapper
103
-     * @param  DBAdapter $adapter
104
-     */
105
-    public function __construct(Mapper $mapper, DBAdapter $adapter)
106
-    {
107
-        $this->mapper = $mapper;
108
-
109
-        $this->adapter = $adapter;
110
-
111
-        $this->entityMap = $mapper->getEntityMap();
112
-
113
-        // Specify the table to work on
114
-        $this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
-
116
-        $this->with($this->entityMap->getEagerloadedRelationships());
117
-    }
118
-
119
-    /**
120
-     * Run the query and return the result
121
-     *
122
-     * @param  array $columns
123
-     * @return \Analogue\ORM\EntityCollection
124
-     */
125
-    public function get($columns = ['*'])
126
-    {
127
-        $entities = $this->getEntities($columns);
128
-
129
-        // TODO Should move the call to new Collection on the result builder
130
-        return $this->entityMap->newCollection($entities);
131
-    }
132
-
133
-    /**
134
-     * Find an entity by its primary key
135
-     *
136
-     * @param  string|integer $id
137
-     * @param  array          $columns
138
-     * @return \Analogue\ORM\Mappable
139
-     */
140
-    public function find($id, $columns = ['*'])
141
-    {
142
-        if (is_array($id)) {
143
-            return $this->findMany($id, $columns);
144
-        }
145
-
146
-        $this->query->where($this->entityMap->getKeyName(), '=', $id);
147
-
148
-        return $this->first($columns);
149
-    }
150
-
151
-    /**
152
-     * Find many entities by their primary keys.
153
-     *
154
-     * @param  array $id
155
-     * @param  array $columns
156
-     * @return EntityCollection
157
-     */
158
-    public function findMany($id, $columns = ['*'])
159
-    {
160
-        if (empty($id)) {
161
-            return new EntityCollection;
162
-        }
163
-
164
-        $this->query->whereIn($this->entityMap->getKeyName(), $id);
165
-
166
-        return $this->get($columns);
167
-    }
168
-
169
-    /**
170
-     * Find a model by its primary key or throw an exception.
171
-     *
172
-     * @param  mixed $id
173
-     * @param  array $columns
174
-     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
175
-     * @return mixed|self
176
-     */
177
-    public function findOrFail($id, $columns = ['*'])
178
-    {
179
-        if (!is_null($entity = $this->find($id, $columns))) {
180
-            return $entity;
181
-        }
182
-
183
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
184
-    }
185
-
186
-
187
-    /**
188
-     * Execute the query and get the first result.
189
-     *
190
-     * @param  array $columns
191
-     * @return \Analogue\ORM\Entity
192
-     */
193
-    public function first($columns = ['*'])
194
-    {
195
-        return $this->take(1)->get($columns)->first();
196
-    }
197
-
198
-    /**
199
-     * Execute the query and get the first result or throw an exception.
200
-     *
201
-     * @param  array $columns
202
-     * @throws EntityNotFoundException
203
-     * @return \Analogue\ORM\Entity
204
-     */
205
-    public function firstOrFail($columns = ['*'])
206
-    {
207
-        if (!is_null($entity = $this->first($columns))) {
208
-            return $entity;
209
-        }
210
-
211
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
212
-    }
213
-
214
-    /**
215
-     * Pluck a single column from the database.
216
-     *
217
-     * @param  string $column
218
-     * @return mixed
219
-     */
220
-    public function pluck($column)
221
-    {
222
-        $result = $this->first([$column]);
223
-
224
-        if ($result) {
225
-            return $result->{$column};
226
-        }
227
-    }
228
-
229
-    /**
230
-     * Chunk the results of the query.
231
-     *
232
-     * @param  int      $count
233
-     * @param  callable $callback
234
-     * @return void
235
-     */
236
-    public function chunk($count, callable $callback)
237
-    {
238
-        $results = $this->forPage($page = 1, $count)->get();
239
-
240
-        while (count($results) > 0) {
241
-            // On each chunk result set, we will pass them to the callback and then let the
242
-            // developer take care of everything within the callback, which allows us to
243
-            // keep the memory low for spinning through large result sets for working.
244
-            call_user_func($callback, $results);
245
-
246
-            $page++;
247
-
248
-            $results = $this->forPage($page, $count)->get();
249
-        }
250
-    }
251
-
252
-    /**
253
-     * Get an array with the values of a given column.
254
-     *
255
-     * @param  string $column
256
-     * @param  string $key
257
-     * @return array
258
-     */
259
-    public function lists($column, $key = null)
260
-    {
261
-        return $this->query->pluck($column, $key);
262
-    }
263
-
264
-    /**
265
-     * Get a paginator for the "select" statement.
266
-     *
267
-     * @param  int   $perPage
268
-     * @param  array $columns
269
-     * @return LengthAwarePaginator
270
-     */
271
-    public function paginate($perPage = null, $columns = ['*'])
272
-    {
273
-        $total = $this->query->getCountForPagination();
274
-
275
-        $this->query->forPage(
276
-            $page = Paginator::resolveCurrentPage(),
277
-            $perPage = $perPage ?: $this->entityMap->getPerPage()
278
-        );
279
-
280
-        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
281
-            'path' => Paginator::resolveCurrentPath()
282
-        ]);
283
-    }
284
-
285
-    /**
286
-     * Get a paginator for a grouped statement.
287
-     *
288
-     * @param  \Illuminate\Pagination\Factory $paginator
289
-     * @param  int                            $perPage
290
-     * @param  array                          $columns
291
-     * @return \Illuminate\Pagination\Paginator
292
-     */
293
-    protected function groupedPaginate($paginator, $perPage, $columns)
294
-    {
295
-        $results = $this->get($columns)->all();
296
-
297
-        return $this->query->buildRawPaginator($paginator, $results, $perPage);
298
-    }
299
-
300
-    /**
301
-     * Get a paginator for an ungrouped statement.
302
-     *
303
-     * @param  \Illuminate\Pagination\Factory $paginator
304
-     * @param  int                            $perPage
305
-     * @param  array                          $columns
306
-     * @return \Illuminate\Pagination\Paginator
307
-     */
308
-    protected function ungroupedPaginate($paginator, $perPage, $columns)
309
-    {
310
-        $total = $this->query->getPaginationCount();
311
-
312
-        // Once we have the paginator we need to set the limit and offset values for
313
-        // the query so we can get the properly paginated items. Once we have an
314
-        // array of items we can create the paginator instances for the items.
315
-        $page = $paginator->getCurrentPage($total);
316
-
317
-        $this->query->forPage($page, $perPage);
318
-
319
-        return $paginator->make($this->get($columns)->all(), $total, $perPage);
320
-    }
321
-
322
-    /**
323
-     * Paginate the given query into a simple paginator.
324
-     *
325
-     * @param  int   $perPage
326
-     * @param  array $columns
327
-     * @return \Illuminate\Contracts\Pagination\Paginator
328
-     */
329
-    public function simplePaginate($perPage = null, $columns = ['*'])
330
-    {
331
-        $page = Paginator::resolveCurrentPage();
332
-
333
-        $perPage = $perPage ?: $this->entityMap->getPerPage();
334
-
335
-        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
336
-
337
-        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
338
-    }
339
-
340
-    /**
341
-     * Add a basic where clause to the query.
342
-     *
343
-     * @param  string $column
344
-     * @param  string $operator
345
-     * @param  mixed  $value
346
-     * @param  string $boolean
347
-     * @return $this
348
-     */
349
-    public function where($column, $operator = null, $value = null, $boolean = 'and')
350
-    {
351
-        if ($column instanceof Closure) {
352
-            $query = $this->newQueryWithoutScopes();
353
-
354
-            call_user_func($column, $query);
355
-
356
-            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
357
-        } else {
358
-            call_user_func_array([$this->query, 'where'], func_get_args());
359
-        }
360
-
361
-        return $this;
362
-    }
363
-
364
-    /**
365
-     * Add an "or where" clause to the query.
366
-     *
367
-     * @param  string $column
368
-     * @param  string $operator
369
-     * @param  mixed  $value
370
-     * @return \Analogue\ORM\System\Query
371
-     */
372
-    public function orWhere($column, $operator = null, $value = null)
373
-    {
374
-        return $this->where($column, $operator, $value, 'or');
375
-    }
376
-
377
-    /**
378
-     * Add a relationship count condition to the query.
379
-     *
380
-     * @param  string   $relation
381
-     * @param  string   $operator
382
-     * @param  int      $count
383
-     * @param  string   $boolean
384
-     * @param  \Closure $callback
385
-     * @return \Analogue\ORM\System\Query
386
-     */
387
-    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
388
-    {
389
-        $entity = $this->mapper->newInstance();
390
-
391
-        $relation = $this->getHasRelationQuery($relation, $entity);
392
-
393
-        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
394
-
395
-        if ($callback) {
396
-            call_user_func($callback, $query);
397
-        }
398
-
399
-        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
400
-    }
401
-
402
-    /**
403
-     * Add a relationship count condition to the query with where clauses.
404
-     *
405
-     * @param  string   $relation
406
-     * @param  \Closure $callback
407
-     * @param  string   $operator
408
-     * @param  int      $count
409
-     * @return \Analogue\ORM\System\Query
410
-     */
411
-    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
412
-    {
413
-        return $this->has($relation, $operator, $count, 'and', $callback);
414
-    }
415
-
416
-    /**
417
-     * Add a relationship count condition to the query with an "or".
418
-     *
419
-     * @param  string $relation
420
-     * @param  string $operator
421
-     * @param  int    $count
422
-     * @return \Analogue\ORM\System\Query
423
-     */
424
-    public function orHas($relation, $operator = '>=', $count = 1)
425
-    {
426
-        return $this->has($relation, $operator, $count, 'or');
427
-    }
428
-
429
-    /**
430
-     * Add a relationship count condition to the query with where clauses and an "or".
431
-     *
432
-     * @param  string   $relation
433
-     * @param  \Closure $callback
434
-     * @param  string   $operator
435
-     * @param  int      $count
436
-     * @return \Analogue\ORM\System\Query
437
-     */
438
-    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
439
-    {
440
-        return $this->has($relation, $operator, $count, 'or', $callback);
441
-    }
442
-
443
-    /**
444
-     * Add the "has" condition where clause to the query.
445
-     *
446
-     * @param  \Analogue\ORM\System\Query               $hasQuery
447
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
448
-     * @param  string                                   $operator
449
-     * @param  int                                      $count
450
-     * @param  string                                   $boolean
451
-     * @return \Analogue\ORM\System\Query
452
-     */
453
-    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
454
-    {
455
-        $this->mergeWheresToHas($hasQuery, $relation);
456
-
457
-        if (is_numeric($count)) {
458
-            $count = new Expression($count);
459
-        }
460
-
461
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
462
-    }
463
-
464
-    /**
465
-     * Merge the "wheres" from a relation query to a has query.
466
-     *
467
-     * @param  \Analogue\ORM\System\Query               $hasQuery
468
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
469
-     * @return void
470
-     */
471
-    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
472
-    {
473
-        // Here we have the "has" query and the original relation. We need to copy over any
474
-        // where clauses the developer may have put in the relationship function over to
475
-        // the has query, and then copy the bindings from the "has" query to the main.
476
-        $relationQuery = $relation->getBaseQuery();
477
-
478
-        $hasQuery->mergeWheres(
479
-            $relationQuery->wheres, $relationQuery->getBindings()
480
-        );
481
-
482
-        $this->query->mergeBindings($hasQuery->getQuery());
483
-    }
484
-
485
-    /**
486
-     * Get the "has relation" base query instance.
487
-     *
488
-     * @param  string $relation
489
-     * @param         $entity
490
-     * @return \Analogue\ORM\System\Query
491
-     */
492
-    protected function getHasRelationQuery($relation, $entity)
493
-    {
494
-        return Relationship::noConstraints(function () use ($relation, $entity) {
495
-            return $this->entityMap->$relation($entity);
496
-        });
497
-    }
498
-
499
-    /**
500
-     * Get the table for the current query object
501
-     *
502
-     * @return string
503
-     */
504
-    public function getTable()
505
-    {
506
-        return $this->entityMap->getTable();
507
-    }
508
-
509
-    /**
510
-     * Set the relationships that should be eager loaded.
511
-     *
512
-     * @param  mixed $relations
513
-     * @return $this
514
-     */
515
-    public function with($relations)
516
-    {
517
-        if (is_string($relations)) {
518
-            $relations = func_get_args();
519
-        }
520
-
521
-        $this->eagerLoad = array_merge($this->eagerLoad, $relations);
522
-
523
-        return $this;
524
-    }
525
-
526
-    /**
527
-     * Get the relationships being eagerly loaded.
528
-     *
529
-     * @return array
530
-     */
531
-    public function getEagerLoads()
532
-    {
533
-        return $this->eagerLoad;
534
-    }
535
-
536
-    /**
537
-     * Add the Entity primary key if not in requested columns
538
-     *
539
-     * @param  array $columns
540
-     * @return array
541
-     */
542
-    protected function enforceIdColumn($columns)
543
-    {
544
-        if (!in_array($this->entityMap->getKeyName(), $columns)) {
545
-            $columns[] = $this->entityMap->getKeyName();
546
-        }
547
-        return $columns;
548
-    }
549
-
550
-    /**
551
-     * Get the hydrated models without eager loading.
552
-     *
553
-     * @param  array  $columns
554
-     * @return \Analogue\ORM\EntityCollection
555
-     */
556
-    public function getEntities($columns = ['*'])
557
-    {
558
-        // As we need the primary key to feed the
559
-        // entity cache, we need it loaded on each
560
-        // request
561
-        $columns = $this->enforceIdColumn($columns);
562
-
563
-        // Run the query
564
-        $results = $this->query->get($columns)->toArray();
565
-
566
-        // Create a result builder.
567
-        $builder = new ResultBuilder($this->mapper); 
568
-
569
-        return $builder->build($results, $this->getEagerLoads());
570
-    }
571
-
572
-    /**
573
-     * Extend the builder with a given callback.
574
-     *
575
-     * @param  string   $name
576
-     * @param  \Closure $callback
577
-     * @return void
578
-     */
579
-    public function macro($name, Closure $callback)
580
-    {
581
-        $this->macros[$name] = $callback;
582
-    }
583
-
584
-    /**
585
-     * Get the given macro by name.
586
-     *
587
-     * @param  string $name
588
-     * @return \Closure
589
-     */
590
-    public function getMacro($name)
591
-    {
592
-        return array_get($this->macros, $name);
593
-    }
594
-
595
-    /**
596
-     * Get a new query builder for the model's table.
597
-     *
598
-     * @return \Analogue\ORM\System\Query
599
-     */
600
-    public function newQuery()
601
-    {
602
-        $builder = new Query($this->mapper, $this->adapter);
603
-
604
-        return $this->applyGlobalScopes($builder);
605
-    }
606
-
607
-    /**
608
-     * Get a new query builder without any scope applied.
609
-     *
610
-     * @return \Analogue\ORM\System\Query
611
-     */
612
-    public function newQueryWithoutScopes()
613
-    {
614
-        return new Query($this->mapper, $this->adapter);
615
-    }
616
-
617
-    /**
618
-     * Get the Mapper instance for this Query Builder
619
-     *
620
-     * @return \Analogue\ORM\System\Mapper
621
-     */
622
-    public function getMapper()
623
-    {
624
-        return $this->mapper;
625
-    }
626
-
627
-    /**
628
-     * Get the underlying query adapter
629
-     *
630
-     * (REFACTOR: this method should move out, we need to provide the client classes
631
-     * with the adapter instead.)
632
-     *
633
-     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
634
-     */
635
-    public function getQuery()
636
-    {
637
-        return $this->query;
638
-    }
639
-
640
-    /**
641
-     * Dynamically handle calls into the query instance.
642
-     *
643
-     * @param  string $method
644
-     * @param  array  $parameters
645
-     * @throws Exception
646
-     * @return mixed
647
-     */
648
-    public function __call($method, $parameters)
649
-    {
650
-        if (isset($this->macros[$method])) {
651
-            array_unshift($parameters, $this);
652
-
653
-            return call_user_func_array($this->macros[$method], $parameters);
654
-        }
655
-
656
-        if (in_array($method, $this->blacklist)) {
657
-            throw new Exception("Method $method doesn't exist");
658
-        }
659
-
660
-        $result = call_user_func_array([$this->query, $method], $parameters);
661
-
662
-        return in_array($method, $this->passthru) ? $result : $this;
663
-    }
22
+	/**
23
+	 * Mapper Instance
24
+	 *
25
+	 * @var \Analogue\ORM\System\Mapper
26
+	 */
27
+	protected $mapper;
28
+
29
+	/**
30
+	 * DB Adatper
31
+	 *
32
+	 * @var \Analogue\ORM\Drivers\DBAdapter
33
+	 */
34
+	protected $adapter;
35
+
36
+	/**
37
+	 * Query Builder Instance
38
+	 *
39
+	 * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
+	 */
41
+	protected $query;
42
+
43
+	/**
44
+	 * Entity Map Instance
45
+	 *
46
+	 * @var \Analogue\ORM\EntityMap
47
+	 */
48
+	protected $entityMap;
49
+
50
+	/**
51
+	 * The relationships that should be eager loaded.
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $eagerLoad = [];
56
+
57
+	/**
58
+	 * All of the registered builder macros.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $macros = [];
63
+
64
+	/**
65
+	 * The methods that should be returned from query builder.
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $passthru = [
70
+		'toSql',
71
+		'lists',
72
+		'pluck',
73
+		'count',
74
+		'min',
75
+		'max',
76
+		'avg',
77
+		'sum',
78
+		'exists',
79
+		'getBindings',
80
+	];
81
+
82
+	/**
83
+	 * Query Builder Blacklist
84
+	 */
85
+	protected $blacklist = [
86
+		'insert',
87
+		'insertGetId',
88
+		'lock',
89
+		'lockForUpdate',
90
+		'sharedLock',
91
+		'update',
92
+		'increment',
93
+		'decrement',
94
+		'delete',
95
+		'truncate',
96
+		'raw',
97
+	];
98
+
99
+	/**
100
+	 * Create a new Analogue Query Builder instance.
101
+	 *
102
+	 * @param  Mapper    $mapper
103
+	 * @param  DBAdapter $adapter
104
+	 */
105
+	public function __construct(Mapper $mapper, DBAdapter $adapter)
106
+	{
107
+		$this->mapper = $mapper;
108
+
109
+		$this->adapter = $adapter;
110
+
111
+		$this->entityMap = $mapper->getEntityMap();
112
+
113
+		// Specify the table to work on
114
+		$this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
+
116
+		$this->with($this->entityMap->getEagerloadedRelationships());
117
+	}
118
+
119
+	/**
120
+	 * Run the query and return the result
121
+	 *
122
+	 * @param  array $columns
123
+	 * @return \Analogue\ORM\EntityCollection
124
+	 */
125
+	public function get($columns = ['*'])
126
+	{
127
+		$entities = $this->getEntities($columns);
128
+
129
+		// TODO Should move the call to new Collection on the result builder
130
+		return $this->entityMap->newCollection($entities);
131
+	}
132
+
133
+	/**
134
+	 * Find an entity by its primary key
135
+	 *
136
+	 * @param  string|integer $id
137
+	 * @param  array          $columns
138
+	 * @return \Analogue\ORM\Mappable
139
+	 */
140
+	public function find($id, $columns = ['*'])
141
+	{
142
+		if (is_array($id)) {
143
+			return $this->findMany($id, $columns);
144
+		}
145
+
146
+		$this->query->where($this->entityMap->getKeyName(), '=', $id);
147
+
148
+		return $this->first($columns);
149
+	}
150
+
151
+	/**
152
+	 * Find many entities by their primary keys.
153
+	 *
154
+	 * @param  array $id
155
+	 * @param  array $columns
156
+	 * @return EntityCollection
157
+	 */
158
+	public function findMany($id, $columns = ['*'])
159
+	{
160
+		if (empty($id)) {
161
+			return new EntityCollection;
162
+		}
163
+
164
+		$this->query->whereIn($this->entityMap->getKeyName(), $id);
165
+
166
+		return $this->get($columns);
167
+	}
168
+
169
+	/**
170
+	 * Find a model by its primary key or throw an exception.
171
+	 *
172
+	 * @param  mixed $id
173
+	 * @param  array $columns
174
+	 * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
175
+	 * @return mixed|self
176
+	 */
177
+	public function findOrFail($id, $columns = ['*'])
178
+	{
179
+		if (!is_null($entity = $this->find($id, $columns))) {
180
+			return $entity;
181
+		}
182
+
183
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
184
+	}
185
+
186
+
187
+	/**
188
+	 * Execute the query and get the first result.
189
+	 *
190
+	 * @param  array $columns
191
+	 * @return \Analogue\ORM\Entity
192
+	 */
193
+	public function first($columns = ['*'])
194
+	{
195
+		return $this->take(1)->get($columns)->first();
196
+	}
197
+
198
+	/**
199
+	 * Execute the query and get the first result or throw an exception.
200
+	 *
201
+	 * @param  array $columns
202
+	 * @throws EntityNotFoundException
203
+	 * @return \Analogue\ORM\Entity
204
+	 */
205
+	public function firstOrFail($columns = ['*'])
206
+	{
207
+		if (!is_null($entity = $this->first($columns))) {
208
+			return $entity;
209
+		}
210
+
211
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
212
+	}
213
+
214
+	/**
215
+	 * Pluck a single column from the database.
216
+	 *
217
+	 * @param  string $column
218
+	 * @return mixed
219
+	 */
220
+	public function pluck($column)
221
+	{
222
+		$result = $this->first([$column]);
223
+
224
+		if ($result) {
225
+			return $result->{$column};
226
+		}
227
+	}
228
+
229
+	/**
230
+	 * Chunk the results of the query.
231
+	 *
232
+	 * @param  int      $count
233
+	 * @param  callable $callback
234
+	 * @return void
235
+	 */
236
+	public function chunk($count, callable $callback)
237
+	{
238
+		$results = $this->forPage($page = 1, $count)->get();
239
+
240
+		while (count($results) > 0) {
241
+			// On each chunk result set, we will pass them to the callback and then let the
242
+			// developer take care of everything within the callback, which allows us to
243
+			// keep the memory low for spinning through large result sets for working.
244
+			call_user_func($callback, $results);
245
+
246
+			$page++;
247
+
248
+			$results = $this->forPage($page, $count)->get();
249
+		}
250
+	}
251
+
252
+	/**
253
+	 * Get an array with the values of a given column.
254
+	 *
255
+	 * @param  string $column
256
+	 * @param  string $key
257
+	 * @return array
258
+	 */
259
+	public function lists($column, $key = null)
260
+	{
261
+		return $this->query->pluck($column, $key);
262
+	}
263
+
264
+	/**
265
+	 * Get a paginator for the "select" statement.
266
+	 *
267
+	 * @param  int   $perPage
268
+	 * @param  array $columns
269
+	 * @return LengthAwarePaginator
270
+	 */
271
+	public function paginate($perPage = null, $columns = ['*'])
272
+	{
273
+		$total = $this->query->getCountForPagination();
274
+
275
+		$this->query->forPage(
276
+			$page = Paginator::resolveCurrentPage(),
277
+			$perPage = $perPage ?: $this->entityMap->getPerPage()
278
+		);
279
+
280
+		return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
281
+			'path' => Paginator::resolveCurrentPath()
282
+		]);
283
+	}
284
+
285
+	/**
286
+	 * Get a paginator for a grouped statement.
287
+	 *
288
+	 * @param  \Illuminate\Pagination\Factory $paginator
289
+	 * @param  int                            $perPage
290
+	 * @param  array                          $columns
291
+	 * @return \Illuminate\Pagination\Paginator
292
+	 */
293
+	protected function groupedPaginate($paginator, $perPage, $columns)
294
+	{
295
+		$results = $this->get($columns)->all();
296
+
297
+		return $this->query->buildRawPaginator($paginator, $results, $perPage);
298
+	}
299
+
300
+	/**
301
+	 * Get a paginator for an ungrouped statement.
302
+	 *
303
+	 * @param  \Illuminate\Pagination\Factory $paginator
304
+	 * @param  int                            $perPage
305
+	 * @param  array                          $columns
306
+	 * @return \Illuminate\Pagination\Paginator
307
+	 */
308
+	protected function ungroupedPaginate($paginator, $perPage, $columns)
309
+	{
310
+		$total = $this->query->getPaginationCount();
311
+
312
+		// Once we have the paginator we need to set the limit and offset values for
313
+		// the query so we can get the properly paginated items. Once we have an
314
+		// array of items we can create the paginator instances for the items.
315
+		$page = $paginator->getCurrentPage($total);
316
+
317
+		$this->query->forPage($page, $perPage);
318
+
319
+		return $paginator->make($this->get($columns)->all(), $total, $perPage);
320
+	}
321
+
322
+	/**
323
+	 * Paginate the given query into a simple paginator.
324
+	 *
325
+	 * @param  int   $perPage
326
+	 * @param  array $columns
327
+	 * @return \Illuminate\Contracts\Pagination\Paginator
328
+	 */
329
+	public function simplePaginate($perPage = null, $columns = ['*'])
330
+	{
331
+		$page = Paginator::resolveCurrentPage();
332
+
333
+		$perPage = $perPage ?: $this->entityMap->getPerPage();
334
+
335
+		$this->skip(($page - 1) * $perPage)->take($perPage + 1);
336
+
337
+		return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
338
+	}
339
+
340
+	/**
341
+	 * Add a basic where clause to the query.
342
+	 *
343
+	 * @param  string $column
344
+	 * @param  string $operator
345
+	 * @param  mixed  $value
346
+	 * @param  string $boolean
347
+	 * @return $this
348
+	 */
349
+	public function where($column, $operator = null, $value = null, $boolean = 'and')
350
+	{
351
+		if ($column instanceof Closure) {
352
+			$query = $this->newQueryWithoutScopes();
353
+
354
+			call_user_func($column, $query);
355
+
356
+			$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
357
+		} else {
358
+			call_user_func_array([$this->query, 'where'], func_get_args());
359
+		}
360
+
361
+		return $this;
362
+	}
363
+
364
+	/**
365
+	 * Add an "or where" clause to the query.
366
+	 *
367
+	 * @param  string $column
368
+	 * @param  string $operator
369
+	 * @param  mixed  $value
370
+	 * @return \Analogue\ORM\System\Query
371
+	 */
372
+	public function orWhere($column, $operator = null, $value = null)
373
+	{
374
+		return $this->where($column, $operator, $value, 'or');
375
+	}
376
+
377
+	/**
378
+	 * Add a relationship count condition to the query.
379
+	 *
380
+	 * @param  string   $relation
381
+	 * @param  string   $operator
382
+	 * @param  int      $count
383
+	 * @param  string   $boolean
384
+	 * @param  \Closure $callback
385
+	 * @return \Analogue\ORM\System\Query
386
+	 */
387
+	public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
388
+	{
389
+		$entity = $this->mapper->newInstance();
390
+
391
+		$relation = $this->getHasRelationQuery($relation, $entity);
392
+
393
+		$query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
394
+
395
+		if ($callback) {
396
+			call_user_func($callback, $query);
397
+		}
398
+
399
+		return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
400
+	}
401
+
402
+	/**
403
+	 * Add a relationship count condition to the query with where clauses.
404
+	 *
405
+	 * @param  string   $relation
406
+	 * @param  \Closure $callback
407
+	 * @param  string   $operator
408
+	 * @param  int      $count
409
+	 * @return \Analogue\ORM\System\Query
410
+	 */
411
+	public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
412
+	{
413
+		return $this->has($relation, $operator, $count, 'and', $callback);
414
+	}
415
+
416
+	/**
417
+	 * Add a relationship count condition to the query with an "or".
418
+	 *
419
+	 * @param  string $relation
420
+	 * @param  string $operator
421
+	 * @param  int    $count
422
+	 * @return \Analogue\ORM\System\Query
423
+	 */
424
+	public function orHas($relation, $operator = '>=', $count = 1)
425
+	{
426
+		return $this->has($relation, $operator, $count, 'or');
427
+	}
428
+
429
+	/**
430
+	 * Add a relationship count condition to the query with where clauses and an "or".
431
+	 *
432
+	 * @param  string   $relation
433
+	 * @param  \Closure $callback
434
+	 * @param  string   $operator
435
+	 * @param  int      $count
436
+	 * @return \Analogue\ORM\System\Query
437
+	 */
438
+	public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
439
+	{
440
+		return $this->has($relation, $operator, $count, 'or', $callback);
441
+	}
442
+
443
+	/**
444
+	 * Add the "has" condition where clause to the query.
445
+	 *
446
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
447
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
448
+	 * @param  string                                   $operator
449
+	 * @param  int                                      $count
450
+	 * @param  string                                   $boolean
451
+	 * @return \Analogue\ORM\System\Query
452
+	 */
453
+	protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
454
+	{
455
+		$this->mergeWheresToHas($hasQuery, $relation);
456
+
457
+		if (is_numeric($count)) {
458
+			$count = new Expression($count);
459
+		}
460
+
461
+		return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
462
+	}
463
+
464
+	/**
465
+	 * Merge the "wheres" from a relation query to a has query.
466
+	 *
467
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
468
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
469
+	 * @return void
470
+	 */
471
+	protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
472
+	{
473
+		// Here we have the "has" query and the original relation. We need to copy over any
474
+		// where clauses the developer may have put in the relationship function over to
475
+		// the has query, and then copy the bindings from the "has" query to the main.
476
+		$relationQuery = $relation->getBaseQuery();
477
+
478
+		$hasQuery->mergeWheres(
479
+			$relationQuery->wheres, $relationQuery->getBindings()
480
+		);
481
+
482
+		$this->query->mergeBindings($hasQuery->getQuery());
483
+	}
484
+
485
+	/**
486
+	 * Get the "has relation" base query instance.
487
+	 *
488
+	 * @param  string $relation
489
+	 * @param         $entity
490
+	 * @return \Analogue\ORM\System\Query
491
+	 */
492
+	protected function getHasRelationQuery($relation, $entity)
493
+	{
494
+		return Relationship::noConstraints(function () use ($relation, $entity) {
495
+			return $this->entityMap->$relation($entity);
496
+		});
497
+	}
498
+
499
+	/**
500
+	 * Get the table for the current query object
501
+	 *
502
+	 * @return string
503
+	 */
504
+	public function getTable()
505
+	{
506
+		return $this->entityMap->getTable();
507
+	}
508
+
509
+	/**
510
+	 * Set the relationships that should be eager loaded.
511
+	 *
512
+	 * @param  mixed $relations
513
+	 * @return $this
514
+	 */
515
+	public function with($relations)
516
+	{
517
+		if (is_string($relations)) {
518
+			$relations = func_get_args();
519
+		}
520
+
521
+		$this->eagerLoad = array_merge($this->eagerLoad, $relations);
522
+
523
+		return $this;
524
+	}
525
+
526
+	/**
527
+	 * Get the relationships being eagerly loaded.
528
+	 *
529
+	 * @return array
530
+	 */
531
+	public function getEagerLoads()
532
+	{
533
+		return $this->eagerLoad;
534
+	}
535
+
536
+	/**
537
+	 * Add the Entity primary key if not in requested columns
538
+	 *
539
+	 * @param  array $columns
540
+	 * @return array
541
+	 */
542
+	protected function enforceIdColumn($columns)
543
+	{
544
+		if (!in_array($this->entityMap->getKeyName(), $columns)) {
545
+			$columns[] = $this->entityMap->getKeyName();
546
+		}
547
+		return $columns;
548
+	}
549
+
550
+	/**
551
+	 * Get the hydrated models without eager loading.
552
+	 *
553
+	 * @param  array  $columns
554
+	 * @return \Analogue\ORM\EntityCollection
555
+	 */
556
+	public function getEntities($columns = ['*'])
557
+	{
558
+		// As we need the primary key to feed the
559
+		// entity cache, we need it loaded on each
560
+		// request
561
+		$columns = $this->enforceIdColumn($columns);
562
+
563
+		// Run the query
564
+		$results = $this->query->get($columns)->toArray();
565
+
566
+		// Create a result builder.
567
+		$builder = new ResultBuilder($this->mapper); 
568
+
569
+		return $builder->build($results, $this->getEagerLoads());
570
+	}
571
+
572
+	/**
573
+	 * Extend the builder with a given callback.
574
+	 *
575
+	 * @param  string   $name
576
+	 * @param  \Closure $callback
577
+	 * @return void
578
+	 */
579
+	public function macro($name, Closure $callback)
580
+	{
581
+		$this->macros[$name] = $callback;
582
+	}
583
+
584
+	/**
585
+	 * Get the given macro by name.
586
+	 *
587
+	 * @param  string $name
588
+	 * @return \Closure
589
+	 */
590
+	public function getMacro($name)
591
+	{
592
+		return array_get($this->macros, $name);
593
+	}
594
+
595
+	/**
596
+	 * Get a new query builder for the model's table.
597
+	 *
598
+	 * @return \Analogue\ORM\System\Query
599
+	 */
600
+	public function newQuery()
601
+	{
602
+		$builder = new Query($this->mapper, $this->adapter);
603
+
604
+		return $this->applyGlobalScopes($builder);
605
+	}
606
+
607
+	/**
608
+	 * Get a new query builder without any scope applied.
609
+	 *
610
+	 * @return \Analogue\ORM\System\Query
611
+	 */
612
+	public function newQueryWithoutScopes()
613
+	{
614
+		return new Query($this->mapper, $this->adapter);
615
+	}
616
+
617
+	/**
618
+	 * Get the Mapper instance for this Query Builder
619
+	 *
620
+	 * @return \Analogue\ORM\System\Mapper
621
+	 */
622
+	public function getMapper()
623
+	{
624
+		return $this->mapper;
625
+	}
626
+
627
+	/**
628
+	 * Get the underlying query adapter
629
+	 *
630
+	 * (REFACTOR: this method should move out, we need to provide the client classes
631
+	 * with the adapter instead.)
632
+	 *
633
+	 * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
634
+	 */
635
+	public function getQuery()
636
+	{
637
+		return $this->query;
638
+	}
639
+
640
+	/**
641
+	 * Dynamically handle calls into the query instance.
642
+	 *
643
+	 * @param  string $method
644
+	 * @param  array  $parameters
645
+	 * @throws Exception
646
+	 * @return mixed
647
+	 */
648
+	public function __call($method, $parameters)
649
+	{
650
+		if (isset($this->macros[$method])) {
651
+			array_unshift($parameters, $this);
652
+
653
+			return call_user_func_array($this->macros[$method], $parameters);
654
+		}
655
+
656
+		if (in_array($method, $this->blacklist)) {
657
+			throw new Exception("Method $method doesn't exist");
658
+		}
659
+
660
+		$result = call_user_func_array([$this->query, $method], $parameters);
661
+
662
+		return in_array($method, $this->passthru) ? $result : $this;
663
+	}
664 664
 }
665 665
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -458,7 +458,7 @@  discard block
 block discarded – undo
458 458
             $count = new Expression($count);
459 459
         }
460 460
 
461
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
461
+        return $this->where(new Expression('('.$hasQuery->toSql().')'), $operator, $count, $boolean);
462 462
     }
463 463
 
464 464
     /**
@@ -491,7 +491,7 @@  discard block
 block discarded – undo
491 491
      */
492 492
     protected function getHasRelationQuery($relation, $entity)
493 493
     {
494
-        return Relationship::noConstraints(function () use ($relation, $entity) {
494
+        return Relationship::noConstraints(function() use ($relation, $entity) {
495 495
             return $this->entityMap->$relation($entity);
496 496
         });
497 497
     }
Please login to merge, or discard this patch.
src/System/Aggregate.php 2 patches
Indentation   +1054 added lines, -1054 removed lines patch added patch discarded remove patch
@@ -16,1080 +16,1080 @@
 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
-     * @throws MappingException
91
-     */
92
-    public function __construct($entity, Aggregate $parent = null, $parentRelationship = null, Aggregate $root = null)
93
-    {
94
-        $factory = new Factory;
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
+	 * @throws MappingException
91
+	 */
92
+	public function __construct($entity, Aggregate $parent = null, $parentRelationship = null, Aggregate $root = null)
93
+	{
94
+		$factory = new Factory;
95 95
         
96
-        $this->class = get_class($entity);
96
+		$this->class = get_class($entity);
97 97
 
98
-        $this->wrappedEntity = $factory->make($entity);
98
+		$this->wrappedEntity = $factory->make($entity);
99 99
 
100
-        $this->parent = $parent;
100
+		$this->parent = $parent;
101 101
 
102
-        $this->parentRelationship = $parentRelationship;
102
+		$this->parentRelationship = $parentRelationship;
103 103
 
104
-        $this->root = $root;
104
+		$this->root = $root;
105 105
 
106
-        $mapper = $this->getMapper($entity);
106
+		$mapper = $this->getMapper($entity);
107 107
 
108
-        $this->entityMap = $mapper->getEntityMap();
108
+		$this->entityMap = $mapper->getEntityMap();
109 109
              
110
-        $this->parseRelationships();
111
-    }
112
-
113
-    /**
114
-     * Parse Every relationships defined on the entity
115
-     *
116
-     * @throws MappingException
117
-     * @return void
118
-     */
119
-    protected function parseRelationships()
120
-    {
121
-        foreach ($this->entityMap->getSingleRelationships() as $relation) {
122
-            $this->parseSingleRelationship($relation);
123
-        }
124
-
125
-        foreach ($this->entityMap->getManyRelationships() as $relation) {
126
-            $this->parseManyRelationship($relation);
127
-        }
128
-    }
129
-
130
-    /**
131
-     * Parse for values common to single & many relations
132
-     *
133
-     * @param  string $relation
134
-     * @throws MappingException
135
-     * @return mixed|boolean
136
-     */
137
-    protected function parseForCommonValues($relation)
138
-    {
139
-        if (!$this->hasAttribute($relation)) {
140
-            // If no attribute exists for this relationships
141
-            // we'll make it a simple empty array. This will
142
-            // save us from constantly checking for the attributes
143
-            // actual existence.
144
-            $this->relationships[$relation] = [];
145
-            return false;
146
-        }
147
-
148
-        $value = $this->getRelationshipValue($relation);
149
-
150
-        if (is_null($value)) {
151
-            $this->relationships[$relation] = [];
152
-
153
-            // If the relationship's content is the null value
154
-            // and the Entity's exist in DB, we'll interpret this
155
-            // as the need to detach all related Entities,
156
-            // therefore a sync operation is needed.
157
-            $this->needSync[] = $relation;
158
-            return false;
159
-        }
160
-
161
-        return $value;
162
-    }
163
-
164
-    /**
165
-     * Parse a 'single' relationship
166
-     *
167
-     * @param  string $relation
168
-     * @throws MappingException
169
-     * @return boolean
170
-     */
171
-    protected function parseSingleRelationship($relation)
172
-    {
173
-        if (!$value = $this->parseForCommonValues($relation)) {
174
-            return true;
175
-        }
110
+		$this->parseRelationships();
111
+	}
112
+
113
+	/**
114
+	 * Parse Every relationships defined on the entity
115
+	 *
116
+	 * @throws MappingException
117
+	 * @return void
118
+	 */
119
+	protected function parseRelationships()
120
+	{
121
+		foreach ($this->entityMap->getSingleRelationships() as $relation) {
122
+			$this->parseSingleRelationship($relation);
123
+		}
124
+
125
+		foreach ($this->entityMap->getManyRelationships() as $relation) {
126
+			$this->parseManyRelationship($relation);
127
+		}
128
+	}
129
+
130
+	/**
131
+	 * Parse for values common to single & many relations
132
+	 *
133
+	 * @param  string $relation
134
+	 * @throws MappingException
135
+	 * @return mixed|boolean
136
+	 */
137
+	protected function parseForCommonValues($relation)
138
+	{
139
+		if (!$this->hasAttribute($relation)) {
140
+			// If no attribute exists for this relationships
141
+			// we'll make it a simple empty array. This will
142
+			// save us from constantly checking for the attributes
143
+			// actual existence.
144
+			$this->relationships[$relation] = [];
145
+			return false;
146
+		}
147
+
148
+		$value = $this->getRelationshipValue($relation);
149
+
150
+		if (is_null($value)) {
151
+			$this->relationships[$relation] = [];
152
+
153
+			// If the relationship's content is the null value
154
+			// and the Entity's exist in DB, we'll interpret this
155
+			// as the need to detach all related Entities,
156
+			// therefore a sync operation is needed.
157
+			$this->needSync[] = $relation;
158
+			return false;
159
+		}
160
+
161
+		return $value;
162
+	}
163
+
164
+	/**
165
+	 * Parse a 'single' relationship
166
+	 *
167
+	 * @param  string $relation
168
+	 * @throws MappingException
169
+	 * @return boolean
170
+	 */
171
+	protected function parseSingleRelationship($relation)
172
+	{
173
+		if (!$value = $this->parseForCommonValues($relation)) {
174
+			return true;
175
+		}
176 176
         
177
-        if ($value instanceof Collection || is_array($value) || $value instanceof CollectionProxy) {
178
-            throw new MappingException("Entity's attribute $relation should not be array, or collection");
179
-        }
180
-
181
-        if ($value instanceof LazyLoadingInterface && !$value->isProxyInitialized()) {
182
-            $this->relationships[$relation] = [];
183
-            return true;
184
-        }
185
-
186
-        // If the attribute is a loaded proxy, swap it for its
187
-        // loaded entity.
188
-        if ($value instanceof LazyLoadingInterface && $value->isProxyInitialized()) {
189
-            $value = $value->getWrappedValueHolderValue();
190
-        }
191
-
192
-        if ($this->isParentOrRoot($value)) {
193
-            $this->relationships[$relation] = [];
194
-            return true;
195
-        }
196
-
197
-        // At this point, we can assume the attribute is an Entity instance
198
-        // so we'll treat it as such.
199
-        $subAggregate = $this->createSubAggregate($value, $relation);
177
+		if ($value instanceof Collection || is_array($value) || $value instanceof CollectionProxy) {
178
+			throw new MappingException("Entity's attribute $relation should not be array, or collection");
179
+		}
180
+
181
+		if ($value instanceof LazyLoadingInterface && !$value->isProxyInitialized()) {
182
+			$this->relationships[$relation] = [];
183
+			return true;
184
+		}
185
+
186
+		// If the attribute is a loaded proxy, swap it for its
187
+		// loaded entity.
188
+		if ($value instanceof LazyLoadingInterface && $value->isProxyInitialized()) {
189
+			$value = $value->getWrappedValueHolderValue();
190
+		}
191
+
192
+		if ($this->isParentOrRoot($value)) {
193
+			$this->relationships[$relation] = [];
194
+			return true;
195
+		}
196
+
197
+		// At this point, we can assume the attribute is an Entity instance
198
+		// so we'll treat it as such.
199
+		$subAggregate = $this->createSubAggregate($value, $relation);
200 200
          
201
-        // Even if it's a single entity, we'll store it as an array
202
-        // just for consistency with other relationships
203
-        $this->relationships[$relation] = [$subAggregate];
204
-
205
-        // We always need to check a loaded relation is in sync
206
-        // with its local key
207
-        $this->needSync[] = $relation;
208
-
209
-        return true;
210
-    }
211
-
212
-    /**
213
-     * Check if value isn't parent or root in the aggregate
214
-     *
215
-     * @param  mixed
216
-     * @return boolean|null
217
-     */
218
-    protected function isParentOrRoot($value)
219
-    {
220
-        if (!is_null($this->root)) {
221
-            $rootClass = get_class($this->root->getEntityObject());
222
-            if ($rootClass == get_class($value)) {
223
-                return true;
224
-            }
225
-        }
226
-
227
-        if (!is_null($this->parent)) {
228
-            $parentClass = get_class($this->parent->getEntityObject());
229
-            if ($parentClass == get_class($value)) {
230
-                return true;
231
-            }
232
-        }
233
-    }
234
-
235
-    /**
236
-     * Parse a 'many' relationship
237
-     *
238
-     * @param  string $relation
239
-     * @throws MappingException
240
-     * @return boolean
241
-     */
242
-    protected function parseManyRelationship($relation)
243
-    {
244
-        if (!$value = $this->parseForCommonValues($relation)) {
245
-            return true;
246
-        }
201
+		// Even if it's a single entity, we'll store it as an array
202
+		// just for consistency with other relationships
203
+		$this->relationships[$relation] = [$subAggregate];
204
+
205
+		// We always need to check a loaded relation is in sync
206
+		// with its local key
207
+		$this->needSync[] = $relation;
208
+
209
+		return true;
210
+	}
211
+
212
+	/**
213
+	 * Check if value isn't parent or root in the aggregate
214
+	 *
215
+	 * @param  mixed
216
+	 * @return boolean|null
217
+	 */
218
+	protected function isParentOrRoot($value)
219
+	{
220
+		if (!is_null($this->root)) {
221
+			$rootClass = get_class($this->root->getEntityObject());
222
+			if ($rootClass == get_class($value)) {
223
+				return true;
224
+			}
225
+		}
226
+
227
+		if (!is_null($this->parent)) {
228
+			$parentClass = get_class($this->parent->getEntityObject());
229
+			if ($parentClass == get_class($value)) {
230
+				return true;
231
+			}
232
+		}
233
+	}
234
+
235
+	/**
236
+	 * Parse a 'many' relationship
237
+	 *
238
+	 * @param  string $relation
239
+	 * @throws MappingException
240
+	 * @return boolean
241
+	 */
242
+	protected function parseManyRelationship($relation)
243
+	{
244
+		if (!$value = $this->parseForCommonValues($relation)) {
245
+			return true;
246
+		}
247 247
         
248
-        if (is_array($value) || (! $value instanceof CollectionProxy && $value instanceof Collection)) {
249
-            $this->needSync[] = $relation;
250
-        }
251
-
252
-        // If the relation is a proxy, we test is the relation
253
-        // has been lazy loaded, otherwise we'll just treat
254
-        // the subset of newly added items.
255
-        if ($value instanceof CollectionProxy && $value->isProxyInitialized()) {
256
-            $this->needSync[] = $relation;
257
-            //$value = $value->getUnderlyingCollection();
258
-        }
259
-
260
-        if ($value instanceof CollectionProxy && ! $value->isProxyInitialized()) {
261
-            $value = $value->getAddedItems();
262
-        }
263
-
264
-        // At this point $value should be either an array or an instance
265
-        // of a collection class.
266
-        if (!is_array($value) && !$value instanceof Collection) {
267
-            throw new MappingException("'$relation' attribute should be array() or Collection");
268
-        }
269
-
270
-        $this->relationships[$relation] = $this->createSubAggregates($value, $relation);
248
+		if (is_array($value) || (! $value instanceof CollectionProxy && $value instanceof Collection)) {
249
+			$this->needSync[] = $relation;
250
+		}
251
+
252
+		// If the relation is a proxy, we test is the relation
253
+		// has been lazy loaded, otherwise we'll just treat
254
+		// the subset of newly added items.
255
+		if ($value instanceof CollectionProxy && $value->isProxyInitialized()) {
256
+			$this->needSync[] = $relation;
257
+			//$value = $value->getUnderlyingCollection();
258
+		}
259
+
260
+		if ($value instanceof CollectionProxy && ! $value->isProxyInitialized()) {
261
+			$value = $value->getAddedItems();
262
+		}
263
+
264
+		// At this point $value should be either an array or an instance
265
+		// of a collection class.
266
+		if (!is_array($value) && !$value instanceof Collection) {
267
+			throw new MappingException("'$relation' attribute should be array() or Collection");
268
+		}
269
+
270
+		$this->relationships[$relation] = $this->createSubAggregates($value, $relation);
271 271
         
272
-        return true;
273
-    }
274
-
275
-    /**
276
-     * Return Entity's relationship attribute
277
-     *
278
-     * @param  string $relation
279
-     * @throws MappingException
280
-     * @return mixed
281
-     */
282
-    protected function getRelationshipValue($relation)
283
-    {
284
-        $value = $this->getEntityAttribute($relation);
285
-        //if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
286
-        if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
287
-            throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
288
-        }
289
-
290
-        return $value;
291
-    }
292
-
293
-    /**
294
-     * Create a child, aggregated entity
295
-     *
296
-     * @param  mixed $entities
297
-     * @param string $relation
298
-     * @return array
299
-     */
300
-    protected function createSubAggregates($entities, $relation)
301
-    {
302
-        $aggregates = [];
303
-
304
-        foreach ($entities as $entity) {
305
-            $aggregates[] = $this->createSubAggregate($entity, $relation);
306
-        }
307
-
308
-        return $aggregates;
309
-    }
310
-
311
-    /**
312
-     * Create a related subAggregate
313
-     *
314
-     * @param  mixed $entity
315
-     * @param  string $relation
316
-     * @throws MappingException
317
-     * @return self
318
-     */
319
-    protected function createSubAggregate($entity, $relation)
320
-    {
321
-        // If root isn't defined, then this is the Aggregate Root
322
-        if (is_null($this->root)) {
323
-            $root = $this;
324
-        } else {
325
-            $root = $this->root;
326
-        }
327
-
328
-        return new self($entity, $this, $relation, $root);
329
-    }
330
-
331
-    /**
332
-     * Get the Entity's primary key attribute
333
-     *
334
-     * @return string|integer
335
-     */
336
-    public function getEntityId()
337
-    {
338
-        return $this->wrappedEntity->getEntityKey();
339
-    }
340
-
341
-    /**
342
-     * Get the name of the primary key
343
-     *
344
-     * @return string
345
-     */
346
-    public function getEntityKey()
347
-    {
348
-        return $this->entityMap->getKeyName();
349
-    }
350
-
351
-    /**
352
-     * Return the entity map for the current entity
353
-     *
354
-     * @return \Analogue\ORM\EntityMap
355
-     */
356
-    public function getEntityMap()
357
-    {
358
-        return $this->entityMap;
359
-    }
360
-
361
-    /**
362
-     * Return the Entity's hash $class.$id
363
-     *
364
-     * @return string
365
-     */
366
-    public function getEntityHash()
367
-    {
368
-        return $this->getEntityClass() . '.' . $this->getEntityId();
369
-    }
370
-
371
-    /**
372
-     * Get wrapped entity class
373
-     *
374
-     * @return string
375
-     */
376
-    public function getEntityClass()
377
-    {
378
-        return $this->entityMap->getClass();
379
-    }
380
-
381
-    /**
382
-     * Return the Mapper's entity cache
383
-     *
384
-     * @return \Analogue\ORM\System\EntityCache
385
-     */
386
-    protected function getEntityCache()
387
-    {
388
-        return $this->getMapper()->getEntityCache();
389
-    }
390
-
391
-    /**
392
-     * Get a relationship as an aggregated entities' array
393
-     *
394
-     * @param  string $name
395
-     * @return array
396
-     */
397
-    public function getRelationship($name)
398
-    {
399
-        if (array_key_exists($name, $this->relationships)) {
400
-            return $this->relationships[$name];
401
-        } else {
402
-            return [];
403
-        }
404
-    }
405
-
406
-    /**
407
-     * [TO IMPLEMENT]
408
-     *
409
-     * @return array
410
-     */
411
-    public function getPivotAttributes()
412
-    {
413
-        return [];
414
-    }
415
-
416
-    /**
417
-     * Get Non existing related entities from several relationships
418
-     *
419
-     * @param  array $relationships
420
-     * @return array
421
-     */
422
-    public function getNonExistingRelated(array $relationships)
423
-    {
424
-        $nonExisting = [];
425
-
426
-        foreach ($relationships as $relation) {
427
-
428
-            if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
429
-                $nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
430
-            }
431
-        }
432
-
433
-        return $nonExisting;
434
-    }
435
-
436
-    /**
437
-     * Get non-existing related entities from a single relation
438
-     *
439
-     * @param  string $relation
440
-     * @return array
441
-     */
442
-    protected function getNonExistingFromRelation($relation)
443
-    {
444
-        $nonExisting = [];
445
-
446
-        foreach ($this->relationships[$relation] as $aggregate) {
447
-            if (!$aggregate->exists()) {
448
-                $nonExisting[] = $aggregate;
449
-            }
450
-        }
451
-
452
-        return $nonExisting;
453
-    }
454
-
455
-    /**
456
-     * Synchronize relationships if needed
457
-     *
458
-     * @param array
459
-     * @return void
460
-     */
461
-    public function syncRelationships(array $relationships)
462
-    {
463
-        foreach ($relationships as $relation) {
464
-            if (in_array($relation, $this->needSync)) {
465
-                $this->synchronize($relation);
466
-            }
467
-        }
468
-    }
469
-
470
-    /**
471
-     * Synchronize a relationship attribute
472
-     *
473
-     * @param $relation
474
-     * @return void
475
-     */
476
-    protected function synchronize($relation)
477
-    {
478
-        $actualContent = $this->relationships[$relation];
479
-
480
-        $relationshipObject = $this->entityMap->$relation($this->getEntityObject());
481
-        $relationshipObject->setParent($this->wrappedEntity);
482
-        $relationshipObject->sync($actualContent);
483
-    }
484
-
485
-    /**
486
-     * Returns an array of Missing related Entities for the
487
-     * given $relation
488
-     *
489
-     * @param  string $relation
490
-     * @return array
491
-     */
492
-    public function getMissingEntities($relation)
493
-    {
494
-        $cachedRelations = $this->getCachedAttribute($relation);
495
-
496
-        if (!is_null($cachedRelations)) {
497
-            $missing = [];
498
-
499
-            foreach ($cachedRelations as $hash) {
500
-                if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
501
-                    $missing[] = $hash;
502
-                }
503
-            }
504
-
505
-            return $missing;
506
-        } else {
507
-            return [];
508
-        }
509
-    }
272
+		return true;
273
+	}
274
+
275
+	/**
276
+	 * Return Entity's relationship attribute
277
+	 *
278
+	 * @param  string $relation
279
+	 * @throws MappingException
280
+	 * @return mixed
281
+	 */
282
+	protected function getRelationshipValue($relation)
283
+	{
284
+		$value = $this->getEntityAttribute($relation);
285
+		//if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
286
+		if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
287
+			throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
288
+		}
289
+
290
+		return $value;
291
+	}
292
+
293
+	/**
294
+	 * Create a child, aggregated entity
295
+	 *
296
+	 * @param  mixed $entities
297
+	 * @param string $relation
298
+	 * @return array
299
+	 */
300
+	protected function createSubAggregates($entities, $relation)
301
+	{
302
+		$aggregates = [];
303
+
304
+		foreach ($entities as $entity) {
305
+			$aggregates[] = $this->createSubAggregate($entity, $relation);
306
+		}
307
+
308
+		return $aggregates;
309
+	}
310
+
311
+	/**
312
+	 * Create a related subAggregate
313
+	 *
314
+	 * @param  mixed $entity
315
+	 * @param  string $relation
316
+	 * @throws MappingException
317
+	 * @return self
318
+	 */
319
+	protected function createSubAggregate($entity, $relation)
320
+	{
321
+		// If root isn't defined, then this is the Aggregate Root
322
+		if (is_null($this->root)) {
323
+			$root = $this;
324
+		} else {
325
+			$root = $this->root;
326
+		}
327
+
328
+		return new self($entity, $this, $relation, $root);
329
+	}
330
+
331
+	/**
332
+	 * Get the Entity's primary key attribute
333
+	 *
334
+	 * @return string|integer
335
+	 */
336
+	public function getEntityId()
337
+	{
338
+		return $this->wrappedEntity->getEntityKey();
339
+	}
340
+
341
+	/**
342
+	 * Get the name of the primary key
343
+	 *
344
+	 * @return string
345
+	 */
346
+	public function getEntityKey()
347
+	{
348
+		return $this->entityMap->getKeyName();
349
+	}
350
+
351
+	/**
352
+	 * Return the entity map for the current entity
353
+	 *
354
+	 * @return \Analogue\ORM\EntityMap
355
+	 */
356
+	public function getEntityMap()
357
+	{
358
+		return $this->entityMap;
359
+	}
360
+
361
+	/**
362
+	 * Return the Entity's hash $class.$id
363
+	 *
364
+	 * @return string
365
+	 */
366
+	public function getEntityHash()
367
+	{
368
+		return $this->getEntityClass() . '.' . $this->getEntityId();
369
+	}
370
+
371
+	/**
372
+	 * Get wrapped entity class
373
+	 *
374
+	 * @return string
375
+	 */
376
+	public function getEntityClass()
377
+	{
378
+		return $this->entityMap->getClass();
379
+	}
380
+
381
+	/**
382
+	 * Return the Mapper's entity cache
383
+	 *
384
+	 * @return \Analogue\ORM\System\EntityCache
385
+	 */
386
+	protected function getEntityCache()
387
+	{
388
+		return $this->getMapper()->getEntityCache();
389
+	}
390
+
391
+	/**
392
+	 * Get a relationship as an aggregated entities' array
393
+	 *
394
+	 * @param  string $name
395
+	 * @return array
396
+	 */
397
+	public function getRelationship($name)
398
+	{
399
+		if (array_key_exists($name, $this->relationships)) {
400
+			return $this->relationships[$name];
401
+		} else {
402
+			return [];
403
+		}
404
+	}
405
+
406
+	/**
407
+	 * [TO IMPLEMENT]
408
+	 *
409
+	 * @return array
410
+	 */
411
+	public function getPivotAttributes()
412
+	{
413
+		return [];
414
+	}
415
+
416
+	/**
417
+	 * Get Non existing related entities from several relationships
418
+	 *
419
+	 * @param  array $relationships
420
+	 * @return array
421
+	 */
422
+	public function getNonExistingRelated(array $relationships)
423
+	{
424
+		$nonExisting = [];
425
+
426
+		foreach ($relationships as $relation) {
427
+
428
+			if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
429
+				$nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
430
+			}
431
+		}
432
+
433
+		return $nonExisting;
434
+	}
435
+
436
+	/**
437
+	 * Get non-existing related entities from a single relation
438
+	 *
439
+	 * @param  string $relation
440
+	 * @return array
441
+	 */
442
+	protected function getNonExistingFromRelation($relation)
443
+	{
444
+		$nonExisting = [];
445
+
446
+		foreach ($this->relationships[$relation] as $aggregate) {
447
+			if (!$aggregate->exists()) {
448
+				$nonExisting[] = $aggregate;
449
+			}
450
+		}
451
+
452
+		return $nonExisting;
453
+	}
454
+
455
+	/**
456
+	 * Synchronize relationships if needed
457
+	 *
458
+	 * @param array
459
+	 * @return void
460
+	 */
461
+	public function syncRelationships(array $relationships)
462
+	{
463
+		foreach ($relationships as $relation) {
464
+			if (in_array($relation, $this->needSync)) {
465
+				$this->synchronize($relation);
466
+			}
467
+		}
468
+	}
469
+
470
+	/**
471
+	 * Synchronize a relationship attribute
472
+	 *
473
+	 * @param $relation
474
+	 * @return void
475
+	 */
476
+	protected function synchronize($relation)
477
+	{
478
+		$actualContent = $this->relationships[$relation];
479
+
480
+		$relationshipObject = $this->entityMap->$relation($this->getEntityObject());
481
+		$relationshipObject->setParent($this->wrappedEntity);
482
+		$relationshipObject->sync($actualContent);
483
+	}
484
+
485
+	/**
486
+	 * Returns an array of Missing related Entities for the
487
+	 * given $relation
488
+	 *
489
+	 * @param  string $relation
490
+	 * @return array
491
+	 */
492
+	public function getMissingEntities($relation)
493
+	{
494
+		$cachedRelations = $this->getCachedAttribute($relation);
495
+
496
+		if (!is_null($cachedRelations)) {
497
+			$missing = [];
498
+
499
+			foreach ($cachedRelations as $hash) {
500
+				if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
501
+					$missing[] = $hash;
502
+				}
503
+			}
504
+
505
+			return $missing;
506
+		} else {
507
+			return [];
508
+		}
509
+	}
510 510
        
511
-    /**
512
-     * Get Relationships who have dirty attributes / dirty relationships
513
-     *
514
-     * @return array
515
-     */
516
-    public function getDirtyRelationships()
517
-    {
518
-        $dirtyAggregates = [];
519
-
520
-        foreach ($this->relationships as $relation) {
521
-            foreach ($relation as $aggregate) {
522
-
523
-                if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships()) > 0) {
524
-                    $dirtyAggregates[] = $aggregate;
525
-                }
526
-            }
527
-        }
528
-
529
-        return $dirtyAggregates;
530
-    }
511
+	/**
512
+	 * Get Relationships who have dirty attributes / dirty relationships
513
+	 *
514
+	 * @return array
515
+	 */
516
+	public function getDirtyRelationships()
517
+	{
518
+		$dirtyAggregates = [];
519
+
520
+		foreach ($this->relationships as $relation) {
521
+			foreach ($relation as $aggregate) {
522
+
523
+				if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships()) > 0) {
524
+					$dirtyAggregates[] = $aggregate;
525
+				}
526
+			}
527
+		}
528
+
529
+		return $dirtyAggregates;
530
+	}
531 531
     
532
-    /**
533
-     * Compare the object's raw attributes with the record in cache
534
-     *
535
-     * @return boolean
536
-     */
537
-    public function isDirty()
538
-    {
539
-        if (count($this->getDirtyRawAttributes()) > 0) {
540
-            return true;
541
-        } else {
542
-            return false;
543
-        }
544
-    }
545
-
546
-    /**
547
-     * Get Raw Entity's attributes, as they are represented
548
-     * in the database, including value objects, foreign keys,
549
-     * and discriminator column
550
-     *
551
-     * @return array
552
-     */
553
-    public function getRawAttributes()
554
-    {
555
-        $attributes = $this->wrappedEntity->getEntityAttributes();
556
-
557
-        foreach ($this->entityMap->getRelationships() as $relation) {
558
-            unset($attributes[$relation]);
559
-        }
560
-
561
-        if($this->entityMap->getInheritanceType() == 'single_table') {
562
-            $attributes = $this->addDiscriminatorColumn($attributes);
563
-        }
564
-
565
-        $attributes = $this->flattenEmbeddables($attributes);
566
-
567
-        $foreignKeys = $this->getForeignKeyAttributes();
568
-
569
-        return $attributes + $foreignKeys;
570
-    }
571
-
572
-    /**
573
-     * Add Discriminator Column if it doesn't exist on the actual entity
574
-     * 
575
-     * @param array $attributes
576
-     * @return array
577
-     */
578
-    protected function addDiscriminatorColumn($attributes)
579
-    {
580
-        $discriminatorColumn = $this->entityMap->getDiscriminatorColumn();
581
-        $entityClass = $this->entityMap->getClass();
582
-
583
-        if(! array_key_exists($discriminatorColumn, $attributes)) {
532
+	/**
533
+	 * Compare the object's raw attributes with the record in cache
534
+	 *
535
+	 * @return boolean
536
+	 */
537
+	public function isDirty()
538
+	{
539
+		if (count($this->getDirtyRawAttributes()) > 0) {
540
+			return true;
541
+		} else {
542
+			return false;
543
+		}
544
+	}
545
+
546
+	/**
547
+	 * Get Raw Entity's attributes, as they are represented
548
+	 * in the database, including value objects, foreign keys,
549
+	 * and discriminator column
550
+	 *
551
+	 * @return array
552
+	 */
553
+	public function getRawAttributes()
554
+	{
555
+		$attributes = $this->wrappedEntity->getEntityAttributes();
556
+
557
+		foreach ($this->entityMap->getRelationships() as $relation) {
558
+			unset($attributes[$relation]);
559
+		}
560
+
561
+		if($this->entityMap->getInheritanceType() == 'single_table') {
562
+			$attributes = $this->addDiscriminatorColumn($attributes);
563
+		}
564
+
565
+		$attributes = $this->flattenEmbeddables($attributes);
566
+
567
+		$foreignKeys = $this->getForeignKeyAttributes();
568
+
569
+		return $attributes + $foreignKeys;
570
+	}
571
+
572
+	/**
573
+	 * Add Discriminator Column if it doesn't exist on the actual entity
574
+	 * 
575
+	 * @param array $attributes
576
+	 * @return array
577
+	 */
578
+	protected function addDiscriminatorColumn($attributes)
579
+	{
580
+		$discriminatorColumn = $this->entityMap->getDiscriminatorColumn();
581
+		$entityClass = $this->entityMap->getClass();
582
+
583
+		if(! array_key_exists($discriminatorColumn, $attributes)) {
584 584
             
585
-            // Use key if present in discriminatorMap
586
-            $map = $this->entityMap->getDiscriminatorColumnMap();
587
-
588
-            $type = array_search($entityClass, $map);
589
-
590
-            if($type === false) {
591
-                // Use entity FQDN if no corresponding key is set
592
-                $attributes[$discriminatorColumn] = $entityClass;
593
-            }
594
-            else {
595
-                $attributes[$discriminatorColumn] = $type;
596
-            }
597
-        }
598
-
599
-        return $attributes;
600
-    }
601
-
602
-    /**
603
-     * Convert Value Objects to raw db attributes
604
-     *
605
-     * @param  array $attributes
606
-     * @return array
607
-     */
608
-    protected function flattenEmbeddables($attributes)
609
-    {
610
-        $embeddables = $this->entityMap->getEmbeddables();
585
+			// Use key if present in discriminatorMap
586
+			$map = $this->entityMap->getDiscriminatorColumnMap();
587
+
588
+			$type = array_search($entityClass, $map);
589
+
590
+			if($type === false) {
591
+				// Use entity FQDN if no corresponding key is set
592
+				$attributes[$discriminatorColumn] = $entityClass;
593
+			}
594
+			else {
595
+				$attributes[$discriminatorColumn] = $type;
596
+			}
597
+		}
598
+
599
+		return $attributes;
600
+	}
601
+
602
+	/**
603
+	 * Convert Value Objects to raw db attributes
604
+	 *
605
+	 * @param  array $attributes
606
+	 * @return array
607
+	 */
608
+	protected function flattenEmbeddables($attributes)
609
+	{
610
+		$embeddables = $this->entityMap->getEmbeddables();
611 611
         
612
-        foreach ($embeddables as $localKey => $embed) {
613
-            // Retrieve the value object from the entity's attributes
614
-            $valueObject = $attributes[$localKey];
612
+		foreach ($embeddables as $localKey => $embed) {
613
+			// Retrieve the value object from the entity's attributes
614
+			$valueObject = $attributes[$localKey];
615 615
 
616
-            // Unset the corresponding key
617
-            unset($attributes[$localKey]);
616
+			// Unset the corresponding key
617
+			unset($attributes[$localKey]);
618 618
 
619
-            // TODO Make wrapper object compatible with value objects
620
-            $valueObjectAttributes = $valueObject->getEntityAttributes();
619
+			// TODO Make wrapper object compatible with value objects
620
+			$valueObjectAttributes = $valueObject->getEntityAttributes();
621 621
 
622
-            // Now (if setup in the entity map) we prefix the value object's
623
-            // attributes with the snake_case name of the embedded class.
624
-            $prefix = snake_case(class_basename($embed));
622
+			// Now (if setup in the entity map) we prefix the value object's
623
+			// attributes with the snake_case name of the embedded class.
624
+			$prefix = snake_case(class_basename($embed));
625 625
 
626
-            foreach ($valueObjectAttributes as $key=>$value) {
627
-                $valueObjectAttributes[$prefix . '_' . $key] = $value;
628
-                unset($valueObjectAttributes[$key]);
629
-            }
626
+			foreach ($valueObjectAttributes as $key=>$value) {
627
+				$valueObjectAttributes[$prefix . '_' . $key] = $value;
628
+				unset($valueObjectAttributes[$key]);
629
+			}
630 630
 
631
-            $attributes = array_merge($attributes, $valueObjectAttributes);
632
-        }
631
+			$attributes = array_merge($attributes, $valueObjectAttributes);
632
+		}
633 633
         
634
-        return $attributes;
635
-    }
636
-
637
-    /**
638
-     * Return's entity raw attributes in the state they were at last
639
-     * query.
640
-     *
641
-     * @param  array|null $columns
642
-     * @return array
643
-     */
644
-    protected function getCachedRawAttributes(array $columns = null)
645
-    {
646
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
647
-
648
-        if (is_null($columns)) {
649
-            return $cachedAttributes;
650
-        } else {
651
-            return array_only($cachedAttributes, $columns);
652
-        }
653
-    }
654
-
655
-    /**
656
-     * Return a single attribute from the cache
657
-     * @param  string $key
658
-     * @return mixed
659
-     */
660
-    protected function getCachedAttribute($key)
661
-    {
662
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
663
-
664
-        if (!array_key_exists($key, $cachedAttributes)) {
665
-            return null;
666
-        } else {
667
-            return $cachedAttributes[$key];
668
-        }
669
-    }
670
-
671
-    /**
672
-     * Convert related Entity's attributes to foreign keys
673
-     *
674
-     * @return array
675
-     */
676
-    protected function getForeignKeyAttributes()
677
-    {
678
-        $foreignKeys = [];
679
-
680
-        foreach ($this->entityMap->getLocalRelationships() as $relation) {
681
-            // check if relationship has been parsed, meaning it has an actual object
682
-            // in the entity's attributes
683
-            if ($this->isActualRelationships($relation)) {
684
-                $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
685
-            }
686
-        }
687
-
688
-        if ( ! is_null($this->parent)) {
689
-            $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
690
-        }
691
-
692
-        return $foreignKeys;
693
-    }
694
-
695
-    /**
696
-     * Return an associative array containing the key-value pair(s) from
697
-     * the related entity.
698
-     *
699
-     * @param  string $relation
700
-     * @return array
701
-     */
702
-    protected function getForeignKeyAttributesFromRelation($relation)
703
-    {
704
-        $localRelations = $this->entityMap->getLocalRelationships();
705
-
706
-        if (in_array($relation, $localRelations)) {
707
-            // Call Relationship's method
708
-            $relationship = $this->entityMap->$relation($this->getEntityObject());
709
-
710
-            $relatedAggregate = $this->relationships[$relation][0];
711
-
712
-            return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
713
-        } else {
714
-            return [];
715
-        }
716
-    }
717
-
718
-    /**
719
-     * Get foreign key attribute(s) from a parent entity in this
720
-     * aggregate context
721
-     *
722
-     * @return array
723
-     */
724
-    protected function getForeignKeyAttributesFromParent()
725
-    {
726
-        $parentMap = $this->parent->getEntityMap();
727
-
728
-        $parentForeignRelations = $parentMap->getForeignRelationships();
729
-        $parentPivotRelations = $parentMap->getPivotRelationships();
730
-
731
-        // The parentRelation is the name of the relationship
732
-        // methods on the parent entity map
733
-        $parentRelation = $this->parentRelationship;
734
-
735
-        if (in_array($parentRelation, $parentForeignRelations)
736
-            && !in_array($parentRelation, $parentPivotRelations)
737
-        ) {
738
-            $parentObject = $this->parent->getEntityObject();
739
-
740
-            // Call Relationship's method on parent map
741
-            $relationship = $parentMap->$parentRelation($parentObject);
742
-
743
-            return $relationship->getForeignKeyValuePair();
744
-        } else {
745
-            return [];
746
-        }
747
-    }
748
-
749
-    /**
750
-     * Update Pivot records on loaded relationships, by comparing the
751
-     * values from the Entity Cache to the actual relationship inside
752
-     * the aggregated entity.
753
-     *
754
-     * @return void
755
-     */
756
-    public function updatePivotRecords()
757
-    {
758
-        $pivots = $this->entityMap->getPivotRelationships();
759
-
760
-        foreach ($pivots as $pivot) {
761
-            if (array_key_exists($pivot, $this->relationships)) {
762
-                $this->updatePivotRelation($pivot);
763
-            }
764
-        }
765
-    }
766
-
767
-    /**
768
-     * Update Single pivot relationship
769
-     *
770
-     * @param  string $relation
771
-     * @return void
772
-     */
773
-    protected function updatePivotRelation($relation)
774
-    {
775
-        $hashes = $this->getEntityHashesFromRelation($relation);
776
-
777
-        $cachedAttributes = $this->getCachedRawAttributes();
778
-
779
-        if (array_key_exists($relation, $cachedAttributes)) {
780
-            // Compare the two array of hashes to find out existing
781
-            // pivot records, and the ones to be created.
782
-            $new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
783
-            $existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
784
-        } else {
785
-            $existing = [];
786
-            $new = $hashes;
787
-        }
788
-
789
-        if (count($new) > 0) {
790
-            $pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
791
-
792
-            $this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
793
-        }
794
-
795
-        if (count($existing) > 0) {
796
-            foreach ($existing as $pivotHash) {
797
-                $this->updatePivotIfDirty($pivotHash, $relation);
798
-            }
799
-        }
800
-    }
801
-
802
-    /**
803
-     * Compare existing pivot record in cache and update it
804
-     * if the pivot attributes are dirty
805
-     *
806
-     * @param  string $pivotHash
807
-     * @param  string $relation
808
-     * @return void
809
-     */
810
-    protected function updatePivotIfDirty($pivotHash, $relation)
811
-    {
812
-        $aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
813
-
814
-        if ($aggregate->hasAttribute('pivot')) {
815
-            $pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
816
-
817
-            $cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
818
-
819
-            $actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
820
-
821
-            $dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
822
-
823
-            if (count($dirty) > 0) {
824
-                $id = $aggregate->getEntityId();
825
-
826
-                $this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
827
-            }
828
-        }
829
-    }
830
-
831
-    /**
832
-     * Compare two attributes array and return dirty attributes
833
-     *
834
-     * @param  array $actual
835
-     * @param  array $cached
836
-     * @return array
837
-     */
838
-    protected function getDirtyAttributes(array $actual, array $cached)
839
-    {
840
-        $dirty = [];
841
-
842
-        foreach ($actual as $key => $value) {
843
-            if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
844
-                $dirty[$key] = $actual[$key];
845
-            }
846
-        }
847
-
848
-        return $dirty;
849
-    }
850
-
851
-    /**
852
-     *
853
-     * @param  string $pivotHash
854
-     * @param  string $relation
855
-     * @return array
856
-     */
857
-    protected function getPivotAttributesFromCache($pivotHash, $relation)
858
-    {
859
-        $cachedAttributes = $this->getCachedRawAttributes();
860
-
861
-        $cachedRelations = $cachedAttributes[$relation];
862
-
863
-        foreach ($cachedRelations as $cachedRelation) {
864
-            if ($cachedRelation == $pivotHash) {
865
-                return $cachedRelation->getPivotAttributes();
866
-            }
867
-        }
868
-    }
869
-
870
-    /**
871
-     * Returns an array of related Aggregates from its entity hashes
872
-     *
873
-     * @param  array  $hashes
874
-     * @param  string $relation
875
-     * @return array
876
-     */
877
-    protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
878
-    {
879
-        $related = [];
880
-
881
-        foreach ($hashes as $hash) {
882
-            $aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
883
-
884
-            if (!is_null($aggregate)) {
885
-                $related[] = $aggregate;
886
-            }
887
-        }
888
-
889
-        return $related;
890
-    }
891
-
892
-    /**
893
-     * Get related aggregate from its hash
894
-     *
895
-     * @param  string $hash
896
-     * @param  string $relation
897
-     * @return \Analogue\ORM\System\Aggregate|null
898
-     */
899
-    protected function getRelatedAggregateFromHash($hash, $relation)
900
-    {
901
-        foreach ($this->relationships[$relation] as $aggregate) {
902
-            if ($aggregate->getEntityHash() == $hash) {
903
-                return $aggregate;
904
-            }
905
-        }
906
-        return null;
907
-    }
908
-
909
-    /**
910
-     * Return an array of Entity Hashes from a specific relation
911
-     *
912
-     * @param  string $relation
913
-     * @return array
914
-     */
915
-    protected function getEntityHashesFromRelation($relation)
916
-    {
917
-        return array_map(function ($aggregate) {
918
-            return $aggregate->getEntityHash();
919
-        }, $this->relationships[$relation]);
920
-    }
921
-
922
-    /**
923
-     * Check the existence of an actual relationship
924
-     *
925
-     * @param  string $relation
926
-     * @return boolean
927
-     */
928
-    protected function isActualRelationships($relation)
929
-    {
930
-        return array_key_exists($relation, $this->relationships)
931
-            && count($this->relationships[$relation]) > 0;
932
-    }
933
-
934
-    /**
935
-     * Return cache instance for the current entity type
936
-     *
937
-     * @return \Analogue\ORM\System\EntityCache
938
-     */
939
-    protected function getCache()
940
-    {
941
-        return $this->getMapper()->getEntityCache();
942
-    }
943
-
944
-    /**
945
-     * Get Only Raw Entiy's attributes which have been modified
946
-     * since last query
947
-     *
948
-     * @return array
949
-     */
950
-    public function getDirtyRawAttributes()
951
-    {
952
-        $attributes = $this->getRawAttributes();
953
-        $cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
954
-
955
-        $dirty = [];
956
-
957
-        foreach ($attributes as $key => $value) {
958
-            if ($this->isRelation($key) || $key == 'pivot') {
959
-                continue;
960
-            }
961
-
962
-            if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
963
-                $dirty[$key] = $value;
964
-            } elseif ($value !== $cachedAttributes[$key] &&
965
-                !$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
966
-                $dirty[$key] = $value;
967
-            }
968
-        }
969
-
970
-        return $dirty;
971
-    }
972
-
973
-    /**
974
-     * @param $key
975
-     * @return bool
976
-     */
977
-    protected function isRelation($key)
978
-    {
979
-        return in_array($key, $this->entityMap->getRelationships());
980
-    }
981
-
982
-    /**
983
-     * Determine if the new and old values for a given key are numerically equivalent.
984
-     *
985
-     * @param $current
986
-     * @param $original
987
-     * @return boolean
988
-     */
989
-    protected function originalIsNumericallyEquivalent($current, $original)
990
-    {
991
-        return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
992
-    }
993
-
994
-    /**
995
-     * Get the underlying entity object
996
-     *
997
-     * @return mixed
998
-     */
999
-    public function getEntityObject()
1000
-    {
1001
-        return $this->wrappedEntity->getObject();
1002
-    }
1003
-
1004
-    /**
1005
-     * Return the Mapper instance for the current Entity Type
1006
-     *
1007
-     * @return \Analogue\ORM\System\Mapper
1008
-     */
1009
-    public function getMapper()
1010
-    {
1011
-        return Manager::getMapper($this->class);
1012
-    }
1013
-
1014
-    /**
1015
-     * Check that the entity already exists in the database, by checking
1016
-     * if it has an EntityCache record
1017
-     *
1018
-     * @return boolean
1019
-     */
1020
-    public function exists()
1021
-    {
1022
-        return $this->getCache()->has($this->getEntityId());
1023
-    }
1024
-
1025
-    /**
1026
-     * Set the object attribute raw values (hydration)
1027
-     *
1028
-     * @param array $attributes
1029
-     */
1030
-    public function setEntityAttributes(array $attributes)
1031
-    {
1032
-        $this->wrappedEntity->setEntityAttributes($attributes);
1033
-    }
1034
-
1035
-    /**
1036
-     * Get the raw object's values.
1037
-     *
1038
-     * @return array
1039
-     */
1040
-    public function getEntityAttributes()
1041
-    {
1042
-        return $this->wrappedEntity->getEntityAttributes();
1043
-    }
1044
-
1045
-    /**
1046
-     * Set the raw entity attributes
1047
-     * @param string $key
1048
-     * @param string $value
1049
-     */
1050
-    public function setEntityAttribute($key, $value)
1051
-    {
1052
-        $this->wrappedEntity->setEntityAttribute($key, $value);
1053
-    }
1054
-
1055
-    /**
1056
-     * Return the entity's attribute
1057
-     * @param  string $key
1058
-     * @return mixed
1059
-     */
1060
-    public function getEntityAttribute($key)
1061
-    {
1062
-        return $this->wrappedEntity->getEntityAttribute($key);
1063
-    }
1064
-
1065
-    /**
1066
-     * Does the attribute exists on the entity
1067
-     *
1068
-     * @param  string  $key
1069
-     * @return boolean
1070
-     */
1071
-    public function hasAttribute($key)
1072
-    {
1073
-        return $this->wrappedEntity->hasAttribute($key);
1074
-    }
1075
-
1076
-    /**
1077
-     * Set the lazyloading proxies on the wrapped entity
1078
-     * 
1079
-     * @return  void
1080
-     */
1081
-    public function setProxies()
1082
-    {
1083
-        $this->wrappedEntity->setProxies();
1084
-    }
1085
-
1086
-    /**  
1087
-     * Hydrate the actual entity
1088
-     * 
1089
-     * @return void
1090
-     */
1091
-    public function hydrate()
1092
-    {
1093
-        $this->wrappedEntity->hydrate();
1094
-    }
634
+		return $attributes;
635
+	}
636
+
637
+	/**
638
+	 * Return's entity raw attributes in the state they were at last
639
+	 * query.
640
+	 *
641
+	 * @param  array|null $columns
642
+	 * @return array
643
+	 */
644
+	protected function getCachedRawAttributes(array $columns = null)
645
+	{
646
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
647
+
648
+		if (is_null($columns)) {
649
+			return $cachedAttributes;
650
+		} else {
651
+			return array_only($cachedAttributes, $columns);
652
+		}
653
+	}
654
+
655
+	/**
656
+	 * Return a single attribute from the cache
657
+	 * @param  string $key
658
+	 * @return mixed
659
+	 */
660
+	protected function getCachedAttribute($key)
661
+	{
662
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
663
+
664
+		if (!array_key_exists($key, $cachedAttributes)) {
665
+			return null;
666
+		} else {
667
+			return $cachedAttributes[$key];
668
+		}
669
+	}
670
+
671
+	/**
672
+	 * Convert related Entity's attributes to foreign keys
673
+	 *
674
+	 * @return array
675
+	 */
676
+	protected function getForeignKeyAttributes()
677
+	{
678
+		$foreignKeys = [];
679
+
680
+		foreach ($this->entityMap->getLocalRelationships() as $relation) {
681
+			// check if relationship has been parsed, meaning it has an actual object
682
+			// in the entity's attributes
683
+			if ($this->isActualRelationships($relation)) {
684
+				$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
685
+			}
686
+		}
687
+
688
+		if ( ! is_null($this->parent)) {
689
+			$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
690
+		}
691
+
692
+		return $foreignKeys;
693
+	}
694
+
695
+	/**
696
+	 * Return an associative array containing the key-value pair(s) from
697
+	 * the related entity.
698
+	 *
699
+	 * @param  string $relation
700
+	 * @return array
701
+	 */
702
+	protected function getForeignKeyAttributesFromRelation($relation)
703
+	{
704
+		$localRelations = $this->entityMap->getLocalRelationships();
705
+
706
+		if (in_array($relation, $localRelations)) {
707
+			// Call Relationship's method
708
+			$relationship = $this->entityMap->$relation($this->getEntityObject());
709
+
710
+			$relatedAggregate = $this->relationships[$relation][0];
711
+
712
+			return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
713
+		} else {
714
+			return [];
715
+		}
716
+	}
717
+
718
+	/**
719
+	 * Get foreign key attribute(s) from a parent entity in this
720
+	 * aggregate context
721
+	 *
722
+	 * @return array
723
+	 */
724
+	protected function getForeignKeyAttributesFromParent()
725
+	{
726
+		$parentMap = $this->parent->getEntityMap();
727
+
728
+		$parentForeignRelations = $parentMap->getForeignRelationships();
729
+		$parentPivotRelations = $parentMap->getPivotRelationships();
730
+
731
+		// The parentRelation is the name of the relationship
732
+		// methods on the parent entity map
733
+		$parentRelation = $this->parentRelationship;
734
+
735
+		if (in_array($parentRelation, $parentForeignRelations)
736
+			&& !in_array($parentRelation, $parentPivotRelations)
737
+		) {
738
+			$parentObject = $this->parent->getEntityObject();
739
+
740
+			// Call Relationship's method on parent map
741
+			$relationship = $parentMap->$parentRelation($parentObject);
742
+
743
+			return $relationship->getForeignKeyValuePair();
744
+		} else {
745
+			return [];
746
+		}
747
+	}
748
+
749
+	/**
750
+	 * Update Pivot records on loaded relationships, by comparing the
751
+	 * values from the Entity Cache to the actual relationship inside
752
+	 * the aggregated entity.
753
+	 *
754
+	 * @return void
755
+	 */
756
+	public function updatePivotRecords()
757
+	{
758
+		$pivots = $this->entityMap->getPivotRelationships();
759
+
760
+		foreach ($pivots as $pivot) {
761
+			if (array_key_exists($pivot, $this->relationships)) {
762
+				$this->updatePivotRelation($pivot);
763
+			}
764
+		}
765
+	}
766
+
767
+	/**
768
+	 * Update Single pivot relationship
769
+	 *
770
+	 * @param  string $relation
771
+	 * @return void
772
+	 */
773
+	protected function updatePivotRelation($relation)
774
+	{
775
+		$hashes = $this->getEntityHashesFromRelation($relation);
776
+
777
+		$cachedAttributes = $this->getCachedRawAttributes();
778
+
779
+		if (array_key_exists($relation, $cachedAttributes)) {
780
+			// Compare the two array of hashes to find out existing
781
+			// pivot records, and the ones to be created.
782
+			$new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
783
+			$existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
784
+		} else {
785
+			$existing = [];
786
+			$new = $hashes;
787
+		}
788
+
789
+		if (count($new) > 0) {
790
+			$pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
791
+
792
+			$this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
793
+		}
794
+
795
+		if (count($existing) > 0) {
796
+			foreach ($existing as $pivotHash) {
797
+				$this->updatePivotIfDirty($pivotHash, $relation);
798
+			}
799
+		}
800
+	}
801
+
802
+	/**
803
+	 * Compare existing pivot record in cache and update it
804
+	 * if the pivot attributes are dirty
805
+	 *
806
+	 * @param  string $pivotHash
807
+	 * @param  string $relation
808
+	 * @return void
809
+	 */
810
+	protected function updatePivotIfDirty($pivotHash, $relation)
811
+	{
812
+		$aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
813
+
814
+		if ($aggregate->hasAttribute('pivot')) {
815
+			$pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
816
+
817
+			$cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
818
+
819
+			$actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
820
+
821
+			$dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
822
+
823
+			if (count($dirty) > 0) {
824
+				$id = $aggregate->getEntityId();
825
+
826
+				$this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
827
+			}
828
+		}
829
+	}
830
+
831
+	/**
832
+	 * Compare two attributes array and return dirty attributes
833
+	 *
834
+	 * @param  array $actual
835
+	 * @param  array $cached
836
+	 * @return array
837
+	 */
838
+	protected function getDirtyAttributes(array $actual, array $cached)
839
+	{
840
+		$dirty = [];
841
+
842
+		foreach ($actual as $key => $value) {
843
+			if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
844
+				$dirty[$key] = $actual[$key];
845
+			}
846
+		}
847
+
848
+		return $dirty;
849
+	}
850
+
851
+	/**
852
+	 *
853
+	 * @param  string $pivotHash
854
+	 * @param  string $relation
855
+	 * @return array
856
+	 */
857
+	protected function getPivotAttributesFromCache($pivotHash, $relation)
858
+	{
859
+		$cachedAttributes = $this->getCachedRawAttributes();
860
+
861
+		$cachedRelations = $cachedAttributes[$relation];
862
+
863
+		foreach ($cachedRelations as $cachedRelation) {
864
+			if ($cachedRelation == $pivotHash) {
865
+				return $cachedRelation->getPivotAttributes();
866
+			}
867
+		}
868
+	}
869
+
870
+	/**
871
+	 * Returns an array of related Aggregates from its entity hashes
872
+	 *
873
+	 * @param  array  $hashes
874
+	 * @param  string $relation
875
+	 * @return array
876
+	 */
877
+	protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
878
+	{
879
+		$related = [];
880
+
881
+		foreach ($hashes as $hash) {
882
+			$aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
883
+
884
+			if (!is_null($aggregate)) {
885
+				$related[] = $aggregate;
886
+			}
887
+		}
888
+
889
+		return $related;
890
+	}
891
+
892
+	/**
893
+	 * Get related aggregate from its hash
894
+	 *
895
+	 * @param  string $hash
896
+	 * @param  string $relation
897
+	 * @return \Analogue\ORM\System\Aggregate|null
898
+	 */
899
+	protected function getRelatedAggregateFromHash($hash, $relation)
900
+	{
901
+		foreach ($this->relationships[$relation] as $aggregate) {
902
+			if ($aggregate->getEntityHash() == $hash) {
903
+				return $aggregate;
904
+			}
905
+		}
906
+		return null;
907
+	}
908
+
909
+	/**
910
+	 * Return an array of Entity Hashes from a specific relation
911
+	 *
912
+	 * @param  string $relation
913
+	 * @return array
914
+	 */
915
+	protected function getEntityHashesFromRelation($relation)
916
+	{
917
+		return array_map(function ($aggregate) {
918
+			return $aggregate->getEntityHash();
919
+		}, $this->relationships[$relation]);
920
+	}
921
+
922
+	/**
923
+	 * Check the existence of an actual relationship
924
+	 *
925
+	 * @param  string $relation
926
+	 * @return boolean
927
+	 */
928
+	protected function isActualRelationships($relation)
929
+	{
930
+		return array_key_exists($relation, $this->relationships)
931
+			&& count($this->relationships[$relation]) > 0;
932
+	}
933
+
934
+	/**
935
+	 * Return cache instance for the current entity type
936
+	 *
937
+	 * @return \Analogue\ORM\System\EntityCache
938
+	 */
939
+	protected function getCache()
940
+	{
941
+		return $this->getMapper()->getEntityCache();
942
+	}
943
+
944
+	/**
945
+	 * Get Only Raw Entiy's attributes which have been modified
946
+	 * since last query
947
+	 *
948
+	 * @return array
949
+	 */
950
+	public function getDirtyRawAttributes()
951
+	{
952
+		$attributes = $this->getRawAttributes();
953
+		$cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
954
+
955
+		$dirty = [];
956
+
957
+		foreach ($attributes as $key => $value) {
958
+			if ($this->isRelation($key) || $key == 'pivot') {
959
+				continue;
960
+			}
961
+
962
+			if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
963
+				$dirty[$key] = $value;
964
+			} elseif ($value !== $cachedAttributes[$key] &&
965
+				!$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
966
+				$dirty[$key] = $value;
967
+			}
968
+		}
969
+
970
+		return $dirty;
971
+	}
972
+
973
+	/**
974
+	 * @param $key
975
+	 * @return bool
976
+	 */
977
+	protected function isRelation($key)
978
+	{
979
+		return in_array($key, $this->entityMap->getRelationships());
980
+	}
981
+
982
+	/**
983
+	 * Determine if the new and old values for a given key are numerically equivalent.
984
+	 *
985
+	 * @param $current
986
+	 * @param $original
987
+	 * @return boolean
988
+	 */
989
+	protected function originalIsNumericallyEquivalent($current, $original)
990
+	{
991
+		return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
992
+	}
993
+
994
+	/**
995
+	 * Get the underlying entity object
996
+	 *
997
+	 * @return mixed
998
+	 */
999
+	public function getEntityObject()
1000
+	{
1001
+		return $this->wrappedEntity->getObject();
1002
+	}
1003
+
1004
+	/**
1005
+	 * Return the Mapper instance for the current Entity Type
1006
+	 *
1007
+	 * @return \Analogue\ORM\System\Mapper
1008
+	 */
1009
+	public function getMapper()
1010
+	{
1011
+		return Manager::getMapper($this->class);
1012
+	}
1013
+
1014
+	/**
1015
+	 * Check that the entity already exists in the database, by checking
1016
+	 * if it has an EntityCache record
1017
+	 *
1018
+	 * @return boolean
1019
+	 */
1020
+	public function exists()
1021
+	{
1022
+		return $this->getCache()->has($this->getEntityId());
1023
+	}
1024
+
1025
+	/**
1026
+	 * Set the object attribute raw values (hydration)
1027
+	 *
1028
+	 * @param array $attributes
1029
+	 */
1030
+	public function setEntityAttributes(array $attributes)
1031
+	{
1032
+		$this->wrappedEntity->setEntityAttributes($attributes);
1033
+	}
1034
+
1035
+	/**
1036
+	 * Get the raw object's values.
1037
+	 *
1038
+	 * @return array
1039
+	 */
1040
+	public function getEntityAttributes()
1041
+	{
1042
+		return $this->wrappedEntity->getEntityAttributes();
1043
+	}
1044
+
1045
+	/**
1046
+	 * Set the raw entity attributes
1047
+	 * @param string $key
1048
+	 * @param string $value
1049
+	 */
1050
+	public function setEntityAttribute($key, $value)
1051
+	{
1052
+		$this->wrappedEntity->setEntityAttribute($key, $value);
1053
+	}
1054
+
1055
+	/**
1056
+	 * Return the entity's attribute
1057
+	 * @param  string $key
1058
+	 * @return mixed
1059
+	 */
1060
+	public function getEntityAttribute($key)
1061
+	{
1062
+		return $this->wrappedEntity->getEntityAttribute($key);
1063
+	}
1064
+
1065
+	/**
1066
+	 * Does the attribute exists on the entity
1067
+	 *
1068
+	 * @param  string  $key
1069
+	 * @return boolean
1070
+	 */
1071
+	public function hasAttribute($key)
1072
+	{
1073
+		return $this->wrappedEntity->hasAttribute($key);
1074
+	}
1075
+
1076
+	/**
1077
+	 * Set the lazyloading proxies on the wrapped entity
1078
+	 * 
1079
+	 * @return  void
1080
+	 */
1081
+	public function setProxies()
1082
+	{
1083
+		$this->wrappedEntity->setProxies();
1084
+	}
1085
+
1086
+	/**  
1087
+	 * Hydrate the actual entity
1088
+	 * 
1089
+	 * @return void
1090
+	 */
1091
+	public function hydrate()
1092
+	{
1093
+		$this->wrappedEntity->hydrate();
1094
+	}
1095 1095
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
             return true;
246 246
         }
247 247
         
248
-        if (is_array($value) || (! $value instanceof CollectionProxy && $value instanceof Collection)) {
248
+        if (is_array($value) || (!$value instanceof CollectionProxy && $value instanceof Collection)) {
249 249
             $this->needSync[] = $relation;
250 250
         }
251 251
 
@@ -257,7 +257,7 @@  discard block
 block discarded – undo
257 257
             //$value = $value->getUnderlyingCollection();
258 258
         }
259 259
 
260
-        if ($value instanceof CollectionProxy && ! $value->isProxyInitialized()) {
260
+        if ($value instanceof CollectionProxy && !$value->isProxyInitialized()) {
261 261
             $value = $value->getAddedItems();
262 262
         }
263 263
 
@@ -365,7 +365,7 @@  discard block
 block discarded – undo
365 365
      */
366 366
     public function getEntityHash()
367 367
     {
368
-        return $this->getEntityClass() . '.' . $this->getEntityId();
368
+        return $this->getEntityClass().'.'.$this->getEntityId();
369 369
     }
370 370
 
371 371
     /**
@@ -558,7 +558,7 @@  discard block
 block discarded – undo
558 558
             unset($attributes[$relation]);
559 559
         }
560 560
 
561
-        if($this->entityMap->getInheritanceType() == 'single_table') {
561
+        if ($this->entityMap->getInheritanceType() == 'single_table') {
562 562
             $attributes = $this->addDiscriminatorColumn($attributes);
563 563
         }
564 564
 
@@ -580,14 +580,14 @@  discard block
 block discarded – undo
580 580
         $discriminatorColumn = $this->entityMap->getDiscriminatorColumn();
581 581
         $entityClass = $this->entityMap->getClass();
582 582
 
583
-        if(! array_key_exists($discriminatorColumn, $attributes)) {
583
+        if (!array_key_exists($discriminatorColumn, $attributes)) {
584 584
             
585 585
             // Use key if present in discriminatorMap
586 586
             $map = $this->entityMap->getDiscriminatorColumnMap();
587 587
 
588 588
             $type = array_search($entityClass, $map);
589 589
 
590
-            if($type === false) {
590
+            if ($type === false) {
591 591
                 // Use entity FQDN if no corresponding key is set
592 592
                 $attributes[$discriminatorColumn] = $entityClass;
593 593
             }
@@ -624,7 +624,7 @@  discard block
 block discarded – undo
624 624
             $prefix = snake_case(class_basename($embed));
625 625
 
626 626
             foreach ($valueObjectAttributes as $key=>$value) {
627
-                $valueObjectAttributes[$prefix . '_' . $key] = $value;
627
+                $valueObjectAttributes[$prefix.'_'.$key] = $value;
628 628
                 unset($valueObjectAttributes[$key]);
629 629
             }
630 630
 
@@ -685,7 +685,7 @@  discard block
 block discarded – undo
685 685
             }
686 686
         }
687 687
 
688
-        if ( ! is_null($this->parent)) {
688
+        if (!is_null($this->parent)) {
689 689
             $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
690 690
         }
691 691
 
@@ -914,7 +914,7 @@  discard block
 block discarded – undo
914 914
      */
915 915
     protected function getEntityHashesFromRelation($relation)
916 916
     {
917
-        return array_map(function ($aggregate) {
917
+        return array_map(function($aggregate) {
918 918
             return $aggregate->getEntityHash();
919 919
         }, $this->relationships[$relation]);
920 920
     }
Please login to merge, or discard this patch.
src/Relationships/BelongsTo.php 2 patches
Indentation   +283 added lines, -283 removed lines patch added patch discarded remove patch
@@ -10,289 +10,289 @@
 block discarded – undo
10 10
 
11 11
 class BelongsTo extends Relationship
12 12
 {
13
-    /**
14
-     * The foreign key of the parent model.
15
-     *
16
-     * @var string
17
-     */
18
-    protected $foreignKey;
19
-
20
-    /**
21
-     * The associated key on the parent model.
22
-     *
23
-     * @var string
24
-     */
25
-    protected $otherKey;
26
-
27
-    /**
28
-     * The name of the relationship.
29
-     *
30
-     * @var string
31
-     */
32
-    protected $relation;
33
-
34
-    /**
35
-     * Indicate if the parent entity hold the key for the relation.
36
-     *
37
-     * @var boolean
38
-     */
39
-    protected static $ownForeignKey = true;
40
-
41
-    /**
42
-     * Create a new belongs to relationship instance.
43
-     *
44
-     * @param Mapper   $mapper
45
-     * @param Mappable $parent
46
-     * @param string   $foreignKey
47
-     * @param string   $otherKey
48
-     * @param string   $relation
49
-     */
50
-    public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $relation)
51
-    {
52
-        $this->otherKey = $otherKey;
53
-        $this->relation = $relation;
54
-        $this->foreignKey = $foreignKey;
55
-
56
-        parent::__construct($mapper, $parent);
57
-    }
58
-
59
-    /**
60
-     * Get the results of the relationship.
61
-     *
62
-     * @param  $relation
63
-     *
64
-     * @return \Analogue\ORM\Entity
65
-     */
66
-    public function getResults($relation)
67
-    {
68
-        $result = $this->query->first();
69
-
70
-        $this->cacheRelation($result, $relation);
71
-
72
-        return $result;
73
-    }
74
-
75
-    /**
76
-     * Set the base constraints on the relation query.
77
-     *
78
-     * @return void
79
-     */
80
-    public function addConstraints()
81
-    {
82
-        if (static::$constraints) {
83
-            // For belongs to relationships, which are essentially the inverse of has one
84
-            // or has many relationships, we need to actually query on the primary key
85
-            // of the related models matching on the foreign key that's on a parent.
86
-            $this->query->where($this->otherKey, '=', $this->parent->getEntityAttribute($this->foreignKey));
87
-        }
88
-    }
89
-
90
-    /**
91
-     * Add the constraints for a relationship count query.
92
-     *
93
-     * @param  Query $query
94
-     * @param  Query $parent
95
-     * @return Query
96
-     */
97
-    public function getRelationCountQuery(Query $query, Query $parent)
98
-    {
99
-        $query->select(new Expression('count(*)'));
100
-
101
-        $otherKey = $this->wrap($query->getTable() . '.' . $this->otherKey);
102
-
103
-        return $query->where($this->getQualifiedForeignKey(), '=', new Expression($otherKey));
104
-    }
105
-
106
-    /**
107
-     * Set the constraints for an eager load of the relation.
108
-     *
109
-     * @param  array $results
110
-     * @return void
111
-     */
112
-    public function addEagerConstraints(array $results)
113
-    {
114
-        // We'll grab the primary key name of the related models since it could be set to
115
-        // a non-standard name and not "id". We will then construct the constraint for
116
-        // our eagerly loading query so it returns the proper models from execution.
117
-        $key = $this->otherKey;
118
-
119
-        $this->query->whereIn($key, $this->getEagerModelKeys($results));
120
-    }
121
-
122
-    /**
123
-     * Gather the keys from an array of related models.
124
-     *
125
-     * @param  array $results
126
-     * @return array
127
-     */
128
-    protected function getEagerModelKeys(array $results)
129
-    {
130
-        $keys = [];
131
-
132
-        // First we need to gather all of the keys from the result set so we know what
133
-        // to query for via the eager loading query. We will add them to an array then
134
-        // execute a "where in" statement to gather up all of those related records.
135
-        foreach ($results as $result) {
136
-
137
-            if(!is_null($value = $result[$this->foreignKey])) {
138
-                $keys[] = $value;
139
-            }
140
-        }
141
-
142
-        // If there are no keys that were not null we will just return an array with 0 in
143
-        // it so the query doesn't fail, but will not return any results, which should
144
-        // be what this developer is expecting in a case where this happens to them.
145
-        if (count($keys) == 0) {
146
-            return [0];
147
-        }
148
-
149
-        return array_values(array_unique($keys));
150
-    }
151
-
152
-    /**
153
-     * Match the Results array to an eagerly loaded relation
154
-     *
155
-     * @param  array            $results
156
-     * @param  string           $relation
157
-     * @return array
158
-     */
159
-    public function match(array $results, $relation)
160
-    {
161
-        $foreign = $this->foreignKey;
162
-
163
-        $other = $this->otherKey;
164
-
165
-        // Execute the relationship and get related entities as an EntityCollection
166
-        $entities = $this->getEager();
167
-
168
-        // First we will get to build a dictionary of the child models by their primary
169
-        // key of the relationship, then we can easily match the children back onto
170
-        // the parents using that dictionary and the primary key of the children.
171
-        $dictionary = [];
172
-
173
-        // TODO ; see if otherKey is the primary key of the related entity, we can
174
-        // simply use the EntityCollection key to match entities to results, which
175
-        // will be much more efficient, and use this method as a fallback if the 
176
-        // otherKey is not the same as the primary Key.
177
-        foreach ($entities as $entity) {
178
-            $entity = $this->factory->make($entity);
179
-            $dictionary[$entity->getEntityAttribute($other)] = $entity->getObject();
180
-        }
181
-
182
-        // Once we have the dictionary constructed, we can loop through all the parents
183
-        // and match back onto their children using these keys of the dictionary and
184
-        // the primary key of the children to map them onto the correct instances.
185
-        return array_map(function($result) use ($dictionary, $foreign, $relation) {
13
+	/**
14
+	 * The foreign key of the parent model.
15
+	 *
16
+	 * @var string
17
+	 */
18
+	protected $foreignKey;
19
+
20
+	/**
21
+	 * The associated key on the parent model.
22
+	 *
23
+	 * @var string
24
+	 */
25
+	protected $otherKey;
26
+
27
+	/**
28
+	 * The name of the relationship.
29
+	 *
30
+	 * @var string
31
+	 */
32
+	protected $relation;
33
+
34
+	/**
35
+	 * Indicate if the parent entity hold the key for the relation.
36
+	 *
37
+	 * @var boolean
38
+	 */
39
+	protected static $ownForeignKey = true;
40
+
41
+	/**
42
+	 * Create a new belongs to relationship instance.
43
+	 *
44
+	 * @param Mapper   $mapper
45
+	 * @param Mappable $parent
46
+	 * @param string   $foreignKey
47
+	 * @param string   $otherKey
48
+	 * @param string   $relation
49
+	 */
50
+	public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $relation)
51
+	{
52
+		$this->otherKey = $otherKey;
53
+		$this->relation = $relation;
54
+		$this->foreignKey = $foreignKey;
55
+
56
+		parent::__construct($mapper, $parent);
57
+	}
58
+
59
+	/**
60
+	 * Get the results of the relationship.
61
+	 *
62
+	 * @param  $relation
63
+	 *
64
+	 * @return \Analogue\ORM\Entity
65
+	 */
66
+	public function getResults($relation)
67
+	{
68
+		$result = $this->query->first();
69
+
70
+		$this->cacheRelation($result, $relation);
71
+
72
+		return $result;
73
+	}
74
+
75
+	/**
76
+	 * Set the base constraints on the relation query.
77
+	 *
78
+	 * @return void
79
+	 */
80
+	public function addConstraints()
81
+	{
82
+		if (static::$constraints) {
83
+			// For belongs to relationships, which are essentially the inverse of has one
84
+			// or has many relationships, we need to actually query on the primary key
85
+			// of the related models matching on the foreign key that's on a parent.
86
+			$this->query->where($this->otherKey, '=', $this->parent->getEntityAttribute($this->foreignKey));
87
+		}
88
+	}
89
+
90
+	/**
91
+	 * Add the constraints for a relationship count query.
92
+	 *
93
+	 * @param  Query $query
94
+	 * @param  Query $parent
95
+	 * @return Query
96
+	 */
97
+	public function getRelationCountQuery(Query $query, Query $parent)
98
+	{
99
+		$query->select(new Expression('count(*)'));
100
+
101
+		$otherKey = $this->wrap($query->getTable() . '.' . $this->otherKey);
102
+
103
+		return $query->where($this->getQualifiedForeignKey(), '=', new Expression($otherKey));
104
+	}
105
+
106
+	/**
107
+	 * Set the constraints for an eager load of the relation.
108
+	 *
109
+	 * @param  array $results
110
+	 * @return void
111
+	 */
112
+	public function addEagerConstraints(array $results)
113
+	{
114
+		// We'll grab the primary key name of the related models since it could be set to
115
+		// a non-standard name and not "id". We will then construct the constraint for
116
+		// our eagerly loading query so it returns the proper models from execution.
117
+		$key = $this->otherKey;
118
+
119
+		$this->query->whereIn($key, $this->getEagerModelKeys($results));
120
+	}
121
+
122
+	/**
123
+	 * Gather the keys from an array of related models.
124
+	 *
125
+	 * @param  array $results
126
+	 * @return array
127
+	 */
128
+	protected function getEagerModelKeys(array $results)
129
+	{
130
+		$keys = [];
131
+
132
+		// First we need to gather all of the keys from the result set so we know what
133
+		// to query for via the eager loading query. We will add them to an array then
134
+		// execute a "where in" statement to gather up all of those related records.
135
+		foreach ($results as $result) {
136
+
137
+			if(!is_null($value = $result[$this->foreignKey])) {
138
+				$keys[] = $value;
139
+			}
140
+		}
141
+
142
+		// If there are no keys that were not null we will just return an array with 0 in
143
+		// it so the query doesn't fail, but will not return any results, which should
144
+		// be what this developer is expecting in a case where this happens to them.
145
+		if (count($keys) == 0) {
146
+			return [0];
147
+		}
148
+
149
+		return array_values(array_unique($keys));
150
+	}
151
+
152
+	/**
153
+	 * Match the Results array to an eagerly loaded relation
154
+	 *
155
+	 * @param  array            $results
156
+	 * @param  string           $relation
157
+	 * @return array
158
+	 */
159
+	public function match(array $results, $relation)
160
+	{
161
+		$foreign = $this->foreignKey;
162
+
163
+		$other = $this->otherKey;
164
+
165
+		// Execute the relationship and get related entities as an EntityCollection
166
+		$entities = $this->getEager();
167
+
168
+		// First we will get to build a dictionary of the child models by their primary
169
+		// key of the relationship, then we can easily match the children back onto
170
+		// the parents using that dictionary and the primary key of the children.
171
+		$dictionary = [];
172
+
173
+		// TODO ; see if otherKey is the primary key of the related entity, we can
174
+		// simply use the EntityCollection key to match entities to results, which
175
+		// will be much more efficient, and use this method as a fallback if the 
176
+		// otherKey is not the same as the primary Key.
177
+		foreach ($entities as $entity) {
178
+			$entity = $this->factory->make($entity);
179
+			$dictionary[$entity->getEntityAttribute($other)] = $entity->getObject();
180
+		}
181
+
182
+		// Once we have the dictionary constructed, we can loop through all the parents
183
+		// and match back onto their children using these keys of the dictionary and
184
+		// the primary key of the children to map them onto the correct instances.
185
+		return array_map(function($result) use ($dictionary, $foreign, $relation) {
186 186
             
187
-            if (isset($dictionary[$result[$foreign]])) {
188
-                $result[$relation] = $dictionary[$result[$foreign]];
189
-            }
190
-            else {
191
-                $result[$relation] = null;
192
-            }
193
-            return $result;
194
-
195
-        }, $results);
196
-    }
197
-
198
-    public function sync(array $entities)
199
-    {
200
-        if (count($entities) > 1) {
201
-            throw new MappingException("Single Relationship shouldn't be synced with more than one entity");
202
-        }
187
+			if (isset($dictionary[$result[$foreign]])) {
188
+				$result[$relation] = $dictionary[$result[$foreign]];
189
+			}
190
+			else {
191
+				$result[$relation] = null;
192
+			}
193
+			return $result;
194
+
195
+		}, $results);
196
+	}
197
+
198
+	public function sync(array $entities)
199
+	{
200
+		if (count($entities) > 1) {
201
+			throw new MappingException("Single Relationship shouldn't be synced with more than one entity");
202
+		}
203 203
         
204
-        if (count($entities) == 1) {
205
-            return $this->associate($entities[0]);
206
-        }
207
-
208
-        return false;
209
-    }
210
-
211
-    /**
212
-     * Associate the model instance to the given parent.
213
-     *
214
-     * @param  mixed $entity
215
-     * @return void
216
-     */
217
-    public function associate($entity)
218
-    {
219
-        $this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
220
-    }
221
-
222
-    /**
223
-     * Dissociate previously associated model from the given parent.
224
-     *
225
-     * @return Mappable
226
-     */
227
-    public function dissociate()
228
-    {
229
-        // The Mapper will retrieve this association within the object model, we won't be using
230
-        // the foreign key attribute inside the parent Entity.
231
-        //
232
-        //$this->parent->setEntityAttribute($this->foreignKey, null);
233
-
234
-        $this->parent->setEntityAttribute($this->relation, null);
235
-    }
236
-
237
-    /**
238
-     * Get the foreign key of the relationship.
239
-     *
240
-     * @return string
241
-     */
242
-    public function getForeignKey()
243
-    {
244
-        return $this->foreignKey;
245
-    }
246
-
247
-    /**
248
-     * Get the foreign key value pair for a related object
249
-     *
250
-     * @param  mixed $related
251
-     *
252
-     * @return array
253
-     */
254
-    public function getForeignKeyValuePair($related)
255
-    {
256
-        $foreignKey = $this->getForeignKey();
257
-
258
-        if ($related) {
259
-            $wrapper = $this->factory->make($related);
260
-
261
-            $relatedKey = $this->relatedMap->getKeyName();
262
-
263
-            return [$foreignKey => $wrapper->getEntityAttribute($relatedKey)];
264
-        } else {
265
-            return [$foreignKey => null];
266
-        }
267
-    }
268
-
269
-    /**
270
-     * Get the fully qualified foreign key of the relationship.
271
-     *
272
-     * @return string
273
-     */
274
-    public function getQualifiedForeignKey()
275
-    {
276
-        return $this->parentMap->getTable() . '.' . $this->foreignKey;
277
-    }
278
-
279
-    /**
280
-     * Get the associated key of the relationship.
281
-     *
282
-     * @return string
283
-     */
284
-    public function getOtherKey()
285
-    {
286
-        return $this->otherKey;
287
-    }
288
-
289
-    /**
290
-     * Get the fully qualified associated key of the relationship.
291
-     *
292
-     * @return string
293
-     */
294
-    public function getQualifiedOtherKeyName()
295
-    {
296
-        return $this->relatedMap->getTable() . '.' . $this->otherKey;
297
-    }
204
+		if (count($entities) == 1) {
205
+			return $this->associate($entities[0]);
206
+		}
207
+
208
+		return false;
209
+	}
210
+
211
+	/**
212
+	 * Associate the model instance to the given parent.
213
+	 *
214
+	 * @param  mixed $entity
215
+	 * @return void
216
+	 */
217
+	public function associate($entity)
218
+	{
219
+		$this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
220
+	}
221
+
222
+	/**
223
+	 * Dissociate previously associated model from the given parent.
224
+	 *
225
+	 * @return Mappable
226
+	 */
227
+	public function dissociate()
228
+	{
229
+		// The Mapper will retrieve this association within the object model, we won't be using
230
+		// the foreign key attribute inside the parent Entity.
231
+		//
232
+		//$this->parent->setEntityAttribute($this->foreignKey, null);
233
+
234
+		$this->parent->setEntityAttribute($this->relation, null);
235
+	}
236
+
237
+	/**
238
+	 * Get the foreign key of the relationship.
239
+	 *
240
+	 * @return string
241
+	 */
242
+	public function getForeignKey()
243
+	{
244
+		return $this->foreignKey;
245
+	}
246
+
247
+	/**
248
+	 * Get the foreign key value pair for a related object
249
+	 *
250
+	 * @param  mixed $related
251
+	 *
252
+	 * @return array
253
+	 */
254
+	public function getForeignKeyValuePair($related)
255
+	{
256
+		$foreignKey = $this->getForeignKey();
257
+
258
+		if ($related) {
259
+			$wrapper = $this->factory->make($related);
260
+
261
+			$relatedKey = $this->relatedMap->getKeyName();
262
+
263
+			return [$foreignKey => $wrapper->getEntityAttribute($relatedKey)];
264
+		} else {
265
+			return [$foreignKey => null];
266
+		}
267
+	}
268
+
269
+	/**
270
+	 * Get the fully qualified foreign key of the relationship.
271
+	 *
272
+	 * @return string
273
+	 */
274
+	public function getQualifiedForeignKey()
275
+	{
276
+		return $this->parentMap->getTable() . '.' . $this->foreignKey;
277
+	}
278
+
279
+	/**
280
+	 * Get the associated key of the relationship.
281
+	 *
282
+	 * @return string
283
+	 */
284
+	public function getOtherKey()
285
+	{
286
+		return $this->otherKey;
287
+	}
288
+
289
+	/**
290
+	 * Get the fully qualified associated key of the relationship.
291
+	 *
292
+	 * @return string
293
+	 */
294
+	public function getQualifiedOtherKeyName()
295
+	{
296
+		return $this->relatedMap->getTable() . '.' . $this->otherKey;
297
+	}
298 298
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
     {
99 99
         $query->select(new Expression('count(*)'));
100 100
 
101
-        $otherKey = $this->wrap($query->getTable() . '.' . $this->otherKey);
101
+        $otherKey = $this->wrap($query->getTable().'.'.$this->otherKey);
102 102
 
103 103
         return $query->where($this->getQualifiedForeignKey(), '=', new Expression($otherKey));
104 104
     }
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
         // execute a "where in" statement to gather up all of those related records.
135 135
         foreach ($results as $result) {
136 136
 
137
-            if(!is_null($value = $result[$this->foreignKey])) {
137
+            if (!is_null($value = $result[$this->foreignKey])) {
138 138
                 $keys[] = $value;
139 139
             }
140 140
         }
@@ -273,7 +273,7 @@  discard block
 block discarded – undo
273 273
      */
274 274
     public function getQualifiedForeignKey()
275 275
     {
276
-        return $this->parentMap->getTable() . '.' . $this->foreignKey;
276
+        return $this->parentMap->getTable().'.'.$this->foreignKey;
277 277
     }
278 278
 
279 279
     /**
@@ -293,6 +293,6 @@  discard block
 block discarded – undo
293 293
      */
294 294
     public function getQualifiedOtherKeyName()
295 295
     {
296
-        return $this->relatedMap->getTable() . '.' . $this->otherKey;
296
+        return $this->relatedMap->getTable().'.'.$this->otherKey;
297 297
     }
298 298
 }
Please login to merge, or discard this patch.
src/Relationships/MorphOne.php 1 patch
Indentation   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -4,30 +4,30 @@
 block discarded – undo
4 4
 
5 5
 class MorphOne extends MorphOneOrMany
6 6
 {
7
-    /**
8
-     * Get the results of the relationship.
9
-     *
10
-     * @param  $relation
11
-     * @return mixed
12
-     */
13
-    public function getResults($relation)
14
-    {
15
-        $result = $this->query->first();
7
+	/**
8
+	 * Get the results of the relationship.
9
+	 *
10
+	 * @param  $relation
11
+	 * @return mixed
12
+	 */
13
+	public function getResults($relation)
14
+	{
15
+		$result = $this->query->first();
16 16
 
17
-        $this->cacheRelation($result, $relation);
17
+		$this->cacheRelation($result, $relation);
18 18
 
19
-        return $result;
20
-    }
19
+		return $result;
20
+	}
21 21
 
22
-    /**
23
-     * Match the eagerly loaded results to their parents.
24
-     *
25
-     * @param  array            $results
26
-     * @param  string           $relation
27
-     * @return array
28
-     */
29
-    public function match(array $results, $relation)
30
-    {
31
-        return $this->matchOne($results, $relation);
32
-    }
22
+	/**
23
+	 * Match the eagerly loaded results to their parents.
24
+	 *
25
+	 * @param  array            $results
26
+	 * @param  string           $relation
27
+	 * @return array
28
+	 */
29
+	public function match(array $results, $relation)
30
+	{
31
+		return $this->matchOne($results, $relation);
32
+	}
33 33
 }
Please login to merge, or discard this patch.
src/Relationships/Relationship.php 2 patches
Indentation   +433 added lines, -433 removed lines patch added patch discarded remove patch
@@ -19,438 +19,438 @@
 block discarded – undo
19 19
  */
20 20
 abstract class Relationship
21 21
 {
22
-    /**
23
-     * The mapper instance for the related entity
24
-     *
25
-     * @var Mapper
26
-     */
27
-    protected $relatedMapper;
28
-
29
-    /**
30
-     * The Analogue Query Builder instance.
31
-     *
32
-     * @var Query
33
-     */
34
-    protected $query;
35
-
36
-    /**
37
-     * The parent entity proxy instance.
38
-     *
39
-     * @var InternallyMappable
40
-     */
41
-    protected $parent;
42
-
43
-    /**
44
-     * The parent entity map
45
-     * @var \Analogue\ORM\EntityMap
46
-     */
47
-    protected $parentMap;
48
-
49
-    /**
50
-     * The Parent Mapper instance
51
-     *
52
-     * @var Mapper
53
-     */
54
-    protected $parentMapper;
55
-
56
-    /**
57
-     * The related entity instance.
58
-     *
59
-     * @var object
60
-     */
61
-    protected $related;
62
-
63
-    /**
64
-     * The related entity Map
65
-     * @var \Analogue\ORM\EntityMap
66
-     */
67
-    protected $relatedMap;
68
-
69
-    /**
70
-     * Indicate if the parent entity hold the key for the relation.
71
-     *
72
-     * @var boolean
73
-     */
74
-    protected static $ownForeignKey = false;
75
-
76
-    /**
77
-     * Indicate if the relationships use a pivot table.*
78
-     *
79
-     * @var boolean
80
-     */
81
-    protected static $hasPivot = false;
82
-
83
-    /**
84
-     * Indicates if the relation is adding constraints.
85
-     *
86
-     * @var bool
87
-     */
88
-    protected static $constraints = true;
89
-
90
-    /**
91
-     * Wrapper factory
92
-     *
93
-     * @var \Analogue\ORM\System\Wrappers\Factory
94
-     */
95
-    protected $factory;
96
-
97
-    /**
98
-     * Create a new relation instance.
99
-     *
100
-     * @param  Mapper   $mapper
101
-     * @param  mixed    $parent
102
-     * @throws \Analogue\ORM\Exceptions\MappingException
103
-     */
104
-    public function __construct(Mapper $mapper, $parent)
105
-    {
106
-        $this->relatedMapper = $mapper;
107
-
108
-        $this->query = $mapper->getQuery();
109
-
110
-        $this->factory = new Factory;
111
-
112
-        $this->parent = $this->factory->make($parent);
113
-
114
-        $this->parentMapper = $mapper->getManager()->getMapper($parent);
115
-
116
-        $this->parentMap = $this->parentMapper->getEntityMap();
117
-
118
-        $this->related = $mapper->newInstance();
119
-
120
-        $this->relatedMap = $mapper->getEntityMap();
121
-
122
-        $this->addConstraints();
123
-    }
124
-
125
-    /**
126
-     * Indicate if the parent entity hold the foreign key for relation.
127
-     *
128
-     * @return boolean
129
-     */
130
-    public function ownForeignKey()
131
-    {
132
-        return static::$ownForeignKey;
133
-    }
134
-
135
-    /**
136
-     * Indicate if the relationship uses a pivot table
137
-     *
138
-     * @return boolean
139
-     */
140
-    public function hasPivot()
141
-    {
142
-        return static::$hasPivot;
143
-    }
144
-
145
-    /**
146
-     * Set the base constraints on the relation query.
147
-     *
148
-     * @return void
149
-     */
150
-    abstract public function addConstraints();
151
-
152
-    /**
153
-     * Set the constraints for an eager load of the relation.
154
-     *
155
-     * @param  array $results
156
-     * @return void
157
-     */
158
-    abstract public function addEagerConstraints(array $results);
159
-
160
-    /**
161
-     * Match the eagerly loaded results to their parents, then return
162
-     * updated results
163
-     *
164
-     * @param  array            $results
165
-     * @param  string           $relation
166
-     * @return array
167
-     */
168
-    abstract public function match(array $results, $relation);
169
-
170
-    /**
171
-     * Get the results of the relationship.
172
-     *
173
-     * @param string $relation relation name in parent's entity map
174
-     * @return mixed
175
-     */
176
-    abstract public function getResults($relation);
177
-
178
-    /**
179
-     * Get the relationship for eager loading.
180
-     *
181
-     * @return EntityCollection
182
-     */
183
-    public function getEager()
184
-    {
185
-        return $this->get();
186
-    }
187
-
188
-    /**
189
-     * Add the constraints for a relationship count query.
190
-     *
191
-     * @param  Query $query
192
-     * @param  Query $parent
193
-     * @return Query
194
-     */
195
-    public function getRelationCountQuery(Query $query, Query $parent)
196
-    {
197
-        $query->select(new Expression('count(*)'));
198
-
199
-        $key = $this->wrap($this->getQualifiedParentKeyName());
200
-
201
-        return $query->where($this->getHasCompareKey(), '=', new Expression($key));
202
-    }
203
-
204
-    /**
205
-     * Run a callback with constraints disabled on the relation.
206
-     *
207
-     * @param  Closure $callback
208
-     * @return mixed
209
-     */
210
-    public static function noConstraints(Closure $callback)
211
-    {
212
-        static::$constraints = false;
213
-
214
-        // When resetting the relation where clause, we want to shift the first element
215
-        // off of the bindings, leaving only the constraints that the developers put
216
-        // as "extra" on the relationships, and not original relation constraints.
217
-        $results = call_user_func($callback);
218
-
219
-        static::$constraints = true;
220
-
221
-        return $results;
222
-    }
223
-
224
-    /**
225
-     * Get all of the primary keys for an array of entities.
226
-     *
227
-     * @param  array  $entities
228
-     * @param  string $key
229
-     * @return array
230
-     */
231
-    protected function getKeys(array $entities, $key = null)
232
-    {
233
-        if (is_null($key)) {
234
-            $key = $this->relatedMap->getKeyName();
235
-        }
236
-
237
-        $host = $this;
238
-
239
-        return array_unique(array_values(array_map(function ($value) use ($key, $host) {
240
-            if (!$value instanceof InternallyMappable) {
241
-                $value = $host->factory->make($value);
242
-            }
243
-
244
-            return $value->getEntityAttribute($key);
245
-
246
-        }, $entities)));
247
-    }
248
-
249
-    /**  
250
-     * Get all the keys from a result set
251
-     * 
252
-     * @param  array  $results 
253
-     * @param  string  $key    
254
-     * @return array          
255
-     */
256
-    protected function getKeysFromResults(array $results, $key = null)
257
-    {
258
-        if (is_null($key)) {
259
-            $key = $this->relatedMap->getKeyName();
260
-        }
261
-
262
-        return array_unique(array_values(array_map(function ($value) use ($key) {
263
-            return $value[$key];
264
-        }, $results)));
265
-    }
266
-
267
-    /**
268
-     * Get the underlying query for the relation.
269
-     *
270
-     * @return Query
271
-     */
272
-    public function getQuery()
273
-    {
274
-        return $this->query;
275
-    }
276
-
277
-    /**
278
-     * Get the base query builder
279
-     *
280
-     * @return \Analogue\ORM\Drivers\QueryAdapter
281
-     */
282
-    public function getBaseQuery()
283
-    {
284
-        return $this->query->getQuery();
285
-    }
286
-
287
-    /**
288
-     * Get the parent model of the relation.
289
-     *
290
-     * @return InternallyMappable
291
-     */
292
-    public function getParent()
293
-    {
294
-        return $this->parent;
295
-    }
296
-
297
-    /**
298
-     * Set the parent model of the relation
299
-     * 
300
-     * @param InternallyMappable $parent 
301
-     * @return void
302
-     */
303
-    public function setParent(InternallyMappable $parent)
304
-    {
305
-        $this->parent = $parent;
306
-    }
307
-
308
-    /**
309
-     * Get the fully qualified parent key name.
310
-     *
311
-     * @return string
312
-     */
313
-    protected function getQualifiedParentKeyName()
314
-    {
315
-        return $this->parent->getQualifiedKeyName();
316
-    }
317
-
318
-    /**
319
-     * Get the related entity of the relation.
320
-     *
321
-     * @return \Analogue\ORM\Entity
322
-     */
323
-    public function getRelated()
324
-    {
325
-        return $this->related;
326
-    }
327
-
328
-    /**
329
-     * Get the related mapper for the relation
330
-     *
331
-     * @return Mapper
332
-     */
333
-    public function getRelatedMapper()
334
-    {
335
-        return $this->relatedMapper;
336
-    }
337
-
338
-
339
-    /**
340
-     * Get the name of the "created at" column.
341
-     *
342
-     * @return string
343
-     */
344
-    public function createdAt()
345
-    {
346
-        return $this->parentMap->getCreatedAtColumn();
347
-    }
348
-
349
-    /**
350
-     * Get the name of the "updated at" column.
351
-     *
352
-     * @return string
353
-     */
354
-    public function updatedAt()
355
-    {
356
-        return $this->parentMap->getUpdatedAtColumn();
357
-    }
358
-
359
-    /**
360
-     * Get the name of the related model's "updated at" column.
361
-     *
362
-     * @return string
363
-     */
364
-    public function relatedUpdatedAt()
365
-    {
366
-        return $this->related->getUpdatedAtColumn();
367
-    }
368
-
369
-    /**
370
-     * Wrap the given value with the parent query's grammar.
371
-     *
372
-     * @param  string $value
373
-     * @return string
374
-     */
375
-    public function wrap($value)
376
-    {
377
-        return $this->parentMapper->getQuery()->getQuery()->getGrammar()->wrap($value);
378
-    }
379
-
380
-    /**
381
-     * Get a fresh timestamp
382
-     *
383
-     * @return Carbon
384
-     */
385
-    protected function freshTimestamp()
386
-    {
387
-        return new Carbon;
388
-    }
389
-
390
-    /**
391
-     * Cache the link between parent and related
392
-     * into the mapper's Entity Cache.
393
-     *
394
-     * @param  EntityCollection|Mappable $results  result of the relation query
395
-     * @param  string                    $relation name of the relation method on the parent entity
396
-     * @return void
397
-     */
398
-    protected function cacheRelation($results, $relation)
399
-    {
400
-        $cache = $this->parentMapper->getEntityCache();
401
-
402
-        $cache->cacheLoadedRelationResult($this->parent->getEntityKey(), $relation, $results, $this);
403
-    }
404
-
405
-    /**
406
-     * Return Pivot attributes when available on a relationship
407
-     *
408
-     * @return array
409
-     */
410
-    public function getPivotAttributes()
411
-    {
412
-        return [];
413
-    }
414
-
415
-    /**
416
-     * Get a combo type.primaryKey
417
-     *
418
-     * @param  Mappable $entity
419
-     * @return string
420
-     */
421
-    protected function getEntityHash(Mappable $entity)
422
-    {
423
-        $class = get_class($entity);
424
-
425
-        $keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
426
-
427
-        return $class . '.' . $entity->getEntityAttribute($keyName);
428
-    }
429
-
430
-    /**
431
-     * Run synchronization content if needed by the
432
-     * relation type.
433
-     *
434
-     * @param  array $actualContent
435
-     * @return void
436
-     */
437
-    abstract public function sync(array $actualContent);
22
+	/**
23
+	 * The mapper instance for the related entity
24
+	 *
25
+	 * @var Mapper
26
+	 */
27
+	protected $relatedMapper;
28
+
29
+	/**
30
+	 * The Analogue Query Builder instance.
31
+	 *
32
+	 * @var Query
33
+	 */
34
+	protected $query;
35
+
36
+	/**
37
+	 * The parent entity proxy instance.
38
+	 *
39
+	 * @var InternallyMappable
40
+	 */
41
+	protected $parent;
42
+
43
+	/**
44
+	 * The parent entity map
45
+	 * @var \Analogue\ORM\EntityMap
46
+	 */
47
+	protected $parentMap;
48
+
49
+	/**
50
+	 * The Parent Mapper instance
51
+	 *
52
+	 * @var Mapper
53
+	 */
54
+	protected $parentMapper;
55
+
56
+	/**
57
+	 * The related entity instance.
58
+	 *
59
+	 * @var object
60
+	 */
61
+	protected $related;
62
+
63
+	/**
64
+	 * The related entity Map
65
+	 * @var \Analogue\ORM\EntityMap
66
+	 */
67
+	protected $relatedMap;
68
+
69
+	/**
70
+	 * Indicate if the parent entity hold the key for the relation.
71
+	 *
72
+	 * @var boolean
73
+	 */
74
+	protected static $ownForeignKey = false;
75
+
76
+	/**
77
+	 * Indicate if the relationships use a pivot table.*
78
+	 *
79
+	 * @var boolean
80
+	 */
81
+	protected static $hasPivot = false;
82
+
83
+	/**
84
+	 * Indicates if the relation is adding constraints.
85
+	 *
86
+	 * @var bool
87
+	 */
88
+	protected static $constraints = true;
89
+
90
+	/**
91
+	 * Wrapper factory
92
+	 *
93
+	 * @var \Analogue\ORM\System\Wrappers\Factory
94
+	 */
95
+	protected $factory;
96
+
97
+	/**
98
+	 * Create a new relation instance.
99
+	 *
100
+	 * @param  Mapper   $mapper
101
+	 * @param  mixed    $parent
102
+	 * @throws \Analogue\ORM\Exceptions\MappingException
103
+	 */
104
+	public function __construct(Mapper $mapper, $parent)
105
+	{
106
+		$this->relatedMapper = $mapper;
107
+
108
+		$this->query = $mapper->getQuery();
109
+
110
+		$this->factory = new Factory;
111
+
112
+		$this->parent = $this->factory->make($parent);
113
+
114
+		$this->parentMapper = $mapper->getManager()->getMapper($parent);
115
+
116
+		$this->parentMap = $this->parentMapper->getEntityMap();
117
+
118
+		$this->related = $mapper->newInstance();
119
+
120
+		$this->relatedMap = $mapper->getEntityMap();
121
+
122
+		$this->addConstraints();
123
+	}
124
+
125
+	/**
126
+	 * Indicate if the parent entity hold the foreign key for relation.
127
+	 *
128
+	 * @return boolean
129
+	 */
130
+	public function ownForeignKey()
131
+	{
132
+		return static::$ownForeignKey;
133
+	}
134
+
135
+	/**
136
+	 * Indicate if the relationship uses a pivot table
137
+	 *
138
+	 * @return boolean
139
+	 */
140
+	public function hasPivot()
141
+	{
142
+		return static::$hasPivot;
143
+	}
144
+
145
+	/**
146
+	 * Set the base constraints on the relation query.
147
+	 *
148
+	 * @return void
149
+	 */
150
+	abstract public function addConstraints();
151
+
152
+	/**
153
+	 * Set the constraints for an eager load of the relation.
154
+	 *
155
+	 * @param  array $results
156
+	 * @return void
157
+	 */
158
+	abstract public function addEagerConstraints(array $results);
159
+
160
+	/**
161
+	 * Match the eagerly loaded results to their parents, then return
162
+	 * updated results
163
+	 *
164
+	 * @param  array            $results
165
+	 * @param  string           $relation
166
+	 * @return array
167
+	 */
168
+	abstract public function match(array $results, $relation);
169
+
170
+	/**
171
+	 * Get the results of the relationship.
172
+	 *
173
+	 * @param string $relation relation name in parent's entity map
174
+	 * @return mixed
175
+	 */
176
+	abstract public function getResults($relation);
177
+
178
+	/**
179
+	 * Get the relationship for eager loading.
180
+	 *
181
+	 * @return EntityCollection
182
+	 */
183
+	public function getEager()
184
+	{
185
+		return $this->get();
186
+	}
187
+
188
+	/**
189
+	 * Add the constraints for a relationship count query.
190
+	 *
191
+	 * @param  Query $query
192
+	 * @param  Query $parent
193
+	 * @return Query
194
+	 */
195
+	public function getRelationCountQuery(Query $query, Query $parent)
196
+	{
197
+		$query->select(new Expression('count(*)'));
198
+
199
+		$key = $this->wrap($this->getQualifiedParentKeyName());
200
+
201
+		return $query->where($this->getHasCompareKey(), '=', new Expression($key));
202
+	}
203
+
204
+	/**
205
+	 * Run a callback with constraints disabled on the relation.
206
+	 *
207
+	 * @param  Closure $callback
208
+	 * @return mixed
209
+	 */
210
+	public static function noConstraints(Closure $callback)
211
+	{
212
+		static::$constraints = false;
213
+
214
+		// When resetting the relation where clause, we want to shift the first element
215
+		// off of the bindings, leaving only the constraints that the developers put
216
+		// as "extra" on the relationships, and not original relation constraints.
217
+		$results = call_user_func($callback);
218
+
219
+		static::$constraints = true;
220
+
221
+		return $results;
222
+	}
223
+
224
+	/**
225
+	 * Get all of the primary keys for an array of entities.
226
+	 *
227
+	 * @param  array  $entities
228
+	 * @param  string $key
229
+	 * @return array
230
+	 */
231
+	protected function getKeys(array $entities, $key = null)
232
+	{
233
+		if (is_null($key)) {
234
+			$key = $this->relatedMap->getKeyName();
235
+		}
236
+
237
+		$host = $this;
238
+
239
+		return array_unique(array_values(array_map(function ($value) use ($key, $host) {
240
+			if (!$value instanceof InternallyMappable) {
241
+				$value = $host->factory->make($value);
242
+			}
243
+
244
+			return $value->getEntityAttribute($key);
245
+
246
+		}, $entities)));
247
+	}
248
+
249
+	/**  
250
+	 * Get all the keys from a result set
251
+	 * 
252
+	 * @param  array  $results 
253
+	 * @param  string  $key    
254
+	 * @return array          
255
+	 */
256
+	protected function getKeysFromResults(array $results, $key = null)
257
+	{
258
+		if (is_null($key)) {
259
+			$key = $this->relatedMap->getKeyName();
260
+		}
261
+
262
+		return array_unique(array_values(array_map(function ($value) use ($key) {
263
+			return $value[$key];
264
+		}, $results)));
265
+	}
266
+
267
+	/**
268
+	 * Get the underlying query for the relation.
269
+	 *
270
+	 * @return Query
271
+	 */
272
+	public function getQuery()
273
+	{
274
+		return $this->query;
275
+	}
276
+
277
+	/**
278
+	 * Get the base query builder
279
+	 *
280
+	 * @return \Analogue\ORM\Drivers\QueryAdapter
281
+	 */
282
+	public function getBaseQuery()
283
+	{
284
+		return $this->query->getQuery();
285
+	}
286
+
287
+	/**
288
+	 * Get the parent model of the relation.
289
+	 *
290
+	 * @return InternallyMappable
291
+	 */
292
+	public function getParent()
293
+	{
294
+		return $this->parent;
295
+	}
296
+
297
+	/**
298
+	 * Set the parent model of the relation
299
+	 * 
300
+	 * @param InternallyMappable $parent 
301
+	 * @return void
302
+	 */
303
+	public function setParent(InternallyMappable $parent)
304
+	{
305
+		$this->parent = $parent;
306
+	}
307
+
308
+	/**
309
+	 * Get the fully qualified parent key name.
310
+	 *
311
+	 * @return string
312
+	 */
313
+	protected function getQualifiedParentKeyName()
314
+	{
315
+		return $this->parent->getQualifiedKeyName();
316
+	}
317
+
318
+	/**
319
+	 * Get the related entity of the relation.
320
+	 *
321
+	 * @return \Analogue\ORM\Entity
322
+	 */
323
+	public function getRelated()
324
+	{
325
+		return $this->related;
326
+	}
327
+
328
+	/**
329
+	 * Get the related mapper for the relation
330
+	 *
331
+	 * @return Mapper
332
+	 */
333
+	public function getRelatedMapper()
334
+	{
335
+		return $this->relatedMapper;
336
+	}
337
+
338
+
339
+	/**
340
+	 * Get the name of the "created at" column.
341
+	 *
342
+	 * @return string
343
+	 */
344
+	public function createdAt()
345
+	{
346
+		return $this->parentMap->getCreatedAtColumn();
347
+	}
348
+
349
+	/**
350
+	 * Get the name of the "updated at" column.
351
+	 *
352
+	 * @return string
353
+	 */
354
+	public function updatedAt()
355
+	{
356
+		return $this->parentMap->getUpdatedAtColumn();
357
+	}
358
+
359
+	/**
360
+	 * Get the name of the related model's "updated at" column.
361
+	 *
362
+	 * @return string
363
+	 */
364
+	public function relatedUpdatedAt()
365
+	{
366
+		return $this->related->getUpdatedAtColumn();
367
+	}
368
+
369
+	/**
370
+	 * Wrap the given value with the parent query's grammar.
371
+	 *
372
+	 * @param  string $value
373
+	 * @return string
374
+	 */
375
+	public function wrap($value)
376
+	{
377
+		return $this->parentMapper->getQuery()->getQuery()->getGrammar()->wrap($value);
378
+	}
379
+
380
+	/**
381
+	 * Get a fresh timestamp
382
+	 *
383
+	 * @return Carbon
384
+	 */
385
+	protected function freshTimestamp()
386
+	{
387
+		return new Carbon;
388
+	}
389
+
390
+	/**
391
+	 * Cache the link between parent and related
392
+	 * into the mapper's Entity Cache.
393
+	 *
394
+	 * @param  EntityCollection|Mappable $results  result of the relation query
395
+	 * @param  string                    $relation name of the relation method on the parent entity
396
+	 * @return void
397
+	 */
398
+	protected function cacheRelation($results, $relation)
399
+	{
400
+		$cache = $this->parentMapper->getEntityCache();
401
+
402
+		$cache->cacheLoadedRelationResult($this->parent->getEntityKey(), $relation, $results, $this);
403
+	}
404
+
405
+	/**
406
+	 * Return Pivot attributes when available on a relationship
407
+	 *
408
+	 * @return array
409
+	 */
410
+	public function getPivotAttributes()
411
+	{
412
+		return [];
413
+	}
414
+
415
+	/**
416
+	 * Get a combo type.primaryKey
417
+	 *
418
+	 * @param  Mappable $entity
419
+	 * @return string
420
+	 */
421
+	protected function getEntityHash(Mappable $entity)
422
+	{
423
+		$class = get_class($entity);
424
+
425
+		$keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
426
+
427
+		return $class . '.' . $entity->getEntityAttribute($keyName);
428
+	}
429
+
430
+	/**
431
+	 * Run synchronization content if needed by the
432
+	 * relation type.
433
+	 *
434
+	 * @param  array $actualContent
435
+	 * @return void
436
+	 */
437
+	abstract public function sync(array $actualContent);
438 438
     
439
-    /**
440
-     * Handle dynamic method calls to the relationship.
441
-     *
442
-     * @param  string $method
443
-     * @param  array  $parameters
444
-     * @return mixed
445
-     */
446
-    public function __call($method, $parameters)
447
-    {
448
-        $result = call_user_func_array([$this->query, $method], $parameters);
449
-
450
-        if ($result === $this->query) {
451
-            return $this;
452
-        }
453
-
454
-        return $result;
455
-    }
439
+	/**
440
+	 * Handle dynamic method calls to the relationship.
441
+	 *
442
+	 * @param  string $method
443
+	 * @param  array  $parameters
444
+	 * @return mixed
445
+	 */
446
+	public function __call($method, $parameters)
447
+	{
448
+		$result = call_user_func_array([$this->query, $method], $parameters);
449
+
450
+		if ($result === $this->query) {
451
+			return $this;
452
+		}
453
+
454
+		return $result;
455
+	}
456 456
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -236,7 +236,7 @@  discard block
 block discarded – undo
236 236
 
237 237
         $host = $this;
238 238
 
239
-        return array_unique(array_values(array_map(function ($value) use ($key, $host) {
239
+        return array_unique(array_values(array_map(function($value) use ($key, $host) {
240 240
             if (!$value instanceof InternallyMappable) {
241 241
                 $value = $host->factory->make($value);
242 242
             }
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
             $key = $this->relatedMap->getKeyName();
260 260
         }
261 261
 
262
-        return array_unique(array_values(array_map(function ($value) use ($key) {
262
+        return array_unique(array_values(array_map(function($value) use ($key) {
263 263
             return $value[$key];
264 264
         }, $results)));
265 265
     }
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
 
425 425
         $keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
426 426
 
427
-        return $class . '.' . $entity->getEntityAttribute($keyName);
427
+        return $class.'.'.$entity->getEntityAttribute($keyName);
428 428
     }
429 429
 
430 430
     /**
Please login to merge, or discard this patch.
src/Relationships/BelongsToMany.php 1 patch
Indentation   +888 added lines, -888 removed lines patch added patch discarded remove patch
@@ -12,898 +12,898 @@
 block discarded – undo
12 12
 
13 13
 class BelongsToMany extends Relationship
14 14
 {
15
-    /**
16
-     * The intermediate table for the relation.
17
-     *
18
-     * @var string
19
-     */
20
-    protected $table;
21
-
22
-    /**
23
-     * The foreign key of the parent model.
24
-     *
25
-     * @var string
26
-     */
27
-    protected $foreignKey;
28
-
29
-    /**
30
-     * The associated key of the relation.
31
-     *
32
-     * @var string
33
-     */
34
-    protected $otherKey;
35
-
36
-    /**
37
-     * The "name" of the relationship.
38
-     *
39
-     * @var string
40
-     */
41
-    protected $relationName;
42
-
43
-    /**
44
-     * The pivot table columns to retrieve.
45
-     *
46
-     * @var array
47
-     */
48
-    protected $pivotColumns = [];
49
-
50
-    /**
51
-     * This relationship has pivot attributes
52
-     *
53
-     * @var boolean
54
-     */
55
-    protected static $hasPivot = true;
56
-
57
-    /**
58
-     * Create a new has many relationship instance.
59
-     *
60
-     * @param Mapper   $mapper
61
-     * @param Mappable $parent
62
-     * @param string   $table
63
-     * @param string   $foreignKey
64
-     * @param string   $otherKey
65
-     * @param string   $relationName
66
-     */
67
-    public function __construct(Mapper $mapper, $parent, $table, $foreignKey, $otherKey, $relationName = null)
68
-    {
69
-        $this->table = $table;
70
-        $this->otherKey = $otherKey;
71
-        $this->foreignKey = $foreignKey;
72
-        $this->relationName = $relationName;
73
-
74
-        parent::__construct($mapper, $parent);
75
-    }
76
-
77
-    /**
78
-     * @param $related
79
-     */
80
-    public function detachMany($related)
81
-    {
82
-        $ids = $this->getIdsFromHashes($related);
83
-
84
-        $this->detach($ids);
85
-    }
86
-
87
-    /**
88
-     * @param array $hashes
89
-     * @return array
90
-     */
91
-    protected function getIdsFromHashes(array $hashes)
92
-    {
93
-        $ids = [];
94
-
95
-        foreach ($hashes as $hash) {
96
-            $split = explode('.', $hash);
97
-            $ids[] = $split[1];
98
-        }
99
-        return $ids;
100
-    }
101
-
102
-    /**
103
-     * Get the results of the relationship.
104
-     *
105
-     * @param $relation
106
-     *
107
-     * @return EntityCollection
108
-     */
109
-    public function getResults($relation)
110
-    {
111
-        $results = $this->get();
112
-
113
-        $this->cacheRelation($results, $relation);
114
-
115
-        return $results;
116
-    }
117
-
118
-    /**
119
-     * Set a where clause for a pivot table column.
120
-     *
121
-     * @param  string $column
122
-     * @param  string $operator
123
-     * @param  mixed  $value
124
-     * @param  string $boolean
125
-     * @return self
126
-     */
127
-    public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
128
-    {
129
-        return $this->where($this->table . '.' . $column, $operator, $value, $boolean);
130
-    }
131
-
132
-    /**
133
-     * Set an or where clause for a pivot table column.
134
-     *
135
-     * @param  string $column
136
-     * @param  string $operator
137
-     * @param  mixed  $value
138
-     * @return self
139
-     */
140
-    public function orWherePivot($column, $operator = null, $value = null)
141
-    {
142
-        return $this->wherePivot($column, $operator, $value, 'or');
143
-    }
144
-
145
-    /**
146
-     * Return Pivot attributes when available on a relationship
147
-     *
148
-     * @return array
149
-     */
150
-    public function getPivotAttributes()
151
-    {
152
-        return $this->pivotColumns;
153
-    }
154
-
155
-    /**
156
-     * Execute the query and get the first result.
157
-     *
158
-     * @param  array $columns
159
-     * @return mixed
160
-     */
161
-    public function first($columns = ['*'])
162
-    {
163
-        $results = $this->take(1)->get($columns);
164
-
165
-        return count($results) > 0 ? $results->first() : null;
166
-    }
167
-
168
-    /**
169
-     * Execute the query and get the first result or throw an exception.
170
-     *
171
-     * @param  array $columns
172
-     *
173
-     * @throws EntityNotFoundException
174
-     *
175
-     * @return Mappable|self
176
-     */
177
-    public function firstOrFail($columns = ['*'])
178
-    {
179
-        if (!is_null($entity = $this->first($columns))) {
180
-            return $entity;
181
-        }
182
-
183
-        throw new EntityNotFoundException;
184
-    }
185
-
186
-    /**
187
-     * Execute the query as a "select" statement.
188
-     *
189
-     * @param  array $columns
190
-     * @return \Analogue\ORM\EntityCollection
191
-     */
192
-    public function get($columns = ['*'])
193
-    {
194
-        // First we'll add the proper select columns onto the query so it is run with
195
-        // the proper columns. Then, we will get the results and hydrate out pivot
196
-        // models with the result of those columns as a separate model relation.
197
-        $columns = $this->query->getQuery()->columns ? [] : $columns;
198
-
199
-        $select = $this->getSelectColumns($columns);
200
-
201
-        $entities = $this->query->addSelect($select)->getEntities();
202
-
203
-        $entities = $this->hydratePivotRelation($entities);
15
+	/**
16
+	 * The intermediate table for the relation.
17
+	 *
18
+	 * @var string
19
+	 */
20
+	protected $table;
21
+
22
+	/**
23
+	 * The foreign key of the parent model.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	protected $foreignKey;
28
+
29
+	/**
30
+	 * The associated key of the relation.
31
+	 *
32
+	 * @var string
33
+	 */
34
+	protected $otherKey;
35
+
36
+	/**
37
+	 * The "name" of the relationship.
38
+	 *
39
+	 * @var string
40
+	 */
41
+	protected $relationName;
42
+
43
+	/**
44
+	 * The pivot table columns to retrieve.
45
+	 *
46
+	 * @var array
47
+	 */
48
+	protected $pivotColumns = [];
49
+
50
+	/**
51
+	 * This relationship has pivot attributes
52
+	 *
53
+	 * @var boolean
54
+	 */
55
+	protected static $hasPivot = true;
56
+
57
+	/**
58
+	 * Create a new has many relationship instance.
59
+	 *
60
+	 * @param Mapper   $mapper
61
+	 * @param Mappable $parent
62
+	 * @param string   $table
63
+	 * @param string   $foreignKey
64
+	 * @param string   $otherKey
65
+	 * @param string   $relationName
66
+	 */
67
+	public function __construct(Mapper $mapper, $parent, $table, $foreignKey, $otherKey, $relationName = null)
68
+	{
69
+		$this->table = $table;
70
+		$this->otherKey = $otherKey;
71
+		$this->foreignKey = $foreignKey;
72
+		$this->relationName = $relationName;
73
+
74
+		parent::__construct($mapper, $parent);
75
+	}
76
+
77
+	/**
78
+	 * @param $related
79
+	 */
80
+	public function detachMany($related)
81
+	{
82
+		$ids = $this->getIdsFromHashes($related);
83
+
84
+		$this->detach($ids);
85
+	}
86
+
87
+	/**
88
+	 * @param array $hashes
89
+	 * @return array
90
+	 */
91
+	protected function getIdsFromHashes(array $hashes)
92
+	{
93
+		$ids = [];
94
+
95
+		foreach ($hashes as $hash) {
96
+			$split = explode('.', $hash);
97
+			$ids[] = $split[1];
98
+		}
99
+		return $ids;
100
+	}
101
+
102
+	/**
103
+	 * Get the results of the relationship.
104
+	 *
105
+	 * @param $relation
106
+	 *
107
+	 * @return EntityCollection
108
+	 */
109
+	public function getResults($relation)
110
+	{
111
+		$results = $this->get();
112
+
113
+		$this->cacheRelation($results, $relation);
114
+
115
+		return $results;
116
+	}
117
+
118
+	/**
119
+	 * Set a where clause for a pivot table column.
120
+	 *
121
+	 * @param  string $column
122
+	 * @param  string $operator
123
+	 * @param  mixed  $value
124
+	 * @param  string $boolean
125
+	 * @return self
126
+	 */
127
+	public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
128
+	{
129
+		return $this->where($this->table . '.' . $column, $operator, $value, $boolean);
130
+	}
131
+
132
+	/**
133
+	 * Set an or where clause for a pivot table column.
134
+	 *
135
+	 * @param  string $column
136
+	 * @param  string $operator
137
+	 * @param  mixed  $value
138
+	 * @return self
139
+	 */
140
+	public function orWherePivot($column, $operator = null, $value = null)
141
+	{
142
+		return $this->wherePivot($column, $operator, $value, 'or');
143
+	}
144
+
145
+	/**
146
+	 * Return Pivot attributes when available on a relationship
147
+	 *
148
+	 * @return array
149
+	 */
150
+	public function getPivotAttributes()
151
+	{
152
+		return $this->pivotColumns;
153
+	}
154
+
155
+	/**
156
+	 * Execute the query and get the first result.
157
+	 *
158
+	 * @param  array $columns
159
+	 * @return mixed
160
+	 */
161
+	public function first($columns = ['*'])
162
+	{
163
+		$results = $this->take(1)->get($columns);
164
+
165
+		return count($results) > 0 ? $results->first() : null;
166
+	}
167
+
168
+	/**
169
+	 * Execute the query and get the first result or throw an exception.
170
+	 *
171
+	 * @param  array $columns
172
+	 *
173
+	 * @throws EntityNotFoundException
174
+	 *
175
+	 * @return Mappable|self
176
+	 */
177
+	public function firstOrFail($columns = ['*'])
178
+	{
179
+		if (!is_null($entity = $this->first($columns))) {
180
+			return $entity;
181
+		}
182
+
183
+		throw new EntityNotFoundException;
184
+	}
185
+
186
+	/**
187
+	 * Execute the query as a "select" statement.
188
+	 *
189
+	 * @param  array $columns
190
+	 * @return \Analogue\ORM\EntityCollection
191
+	 */
192
+	public function get($columns = ['*'])
193
+	{
194
+		// First we'll add the proper select columns onto the query so it is run with
195
+		// the proper columns. Then, we will get the results and hydrate out pivot
196
+		// models with the result of those columns as a separate model relation.
197
+		$columns = $this->query->getQuery()->columns ? [] : $columns;
198
+
199
+		$select = $this->getSelectColumns($columns);
200
+
201
+		$entities = $this->query->addSelect($select)->getEntities();
202
+
203
+		$entities = $this->hydratePivotRelation($entities);
204 204
         
205
-        return $this->relatedMap->newCollection($entities);
206
-    }
207
-
208
-    /**
209
-     * Hydrate the pivot table relationship on the models.
210
-     *
211
-     * @param  array $entities
212
-     * @return void
213
-     */
214
-    protected function hydratePivotRelation(array $entities)
215
-    {
216
-        // TODO (note) We should definitely get rid of the pivot in a next
217
-        // release, as this is not quite relevant in a datamapper context. 
218
-        $host = $this;
219
-        return array_map(function($entity) use ($host) {
220
-            $entityWrapper = $this->factory->make($entity);
221
-
222
-            $pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
223
-            $entityWrapper->setEntityAttribute('pivot', $pivot);
224
-
225
-            return $entityWrapper->getObject();
226
-        }, $entities);
227
-    }
228
-
229
-    /**
230
-     * Get the pivot attributes from a model.
231
-     *
232
-     * @param  $entity
233
-     * @return array
234
-     */
235
-    protected function cleanPivotAttributes(InternallyMappable $entity)
236
-    {
237
-        $values = [];
238
-
239
-        $attributes = $entity->getEntityAttributes();
240
-
241
-        foreach ($attributes as $key => $value) {
242
-            // To get the pivots attributes we will just take any of the attributes which
243
-            // begin with "pivot_" and add those to this arrays, as well as unsetting
244
-            // them from the parent's models since they exist in a different table.
245
-            if (strpos($key, 'pivot_') === 0) {
246
-                $values[substr($key, 6)] = $value;
247
-
248
-                unset($attributes[$key]);
249
-            }
250
-        }
251
-
252
-        // Rehydrate Entity with cleaned array.
253
-        $entity->setEntityAttributes($attributes);
254
-
255
-        return $values;
256
-    }
257
-
258
-    /**
259
-     * Set the base constraints on the relation query.
260
-     *
261
-     * @return void
262
-     */
263
-    public function addConstraints()
264
-    {
265
-        $this->setJoin();
266
-
267
-        if (static::$constraints) {
268
-            $this->setWhere();
269
-        }
270
-    }
271
-
272
-    /**
273
-     * Add the constraints for a relationship count query.
274
-     *
275
-     * @param  Query $query
276
-     * @param  Query $parent
277
-     * @return Query
278
-     */
279
-    public function getRelationCountQuery(Query $query, Query $parent)
280
-    {
281
-        if ($parent->getQuery()->from == $query->getQuery()->from) {
282
-            return $this->getRelationCountQueryForSelfJoin($query, $parent);
283
-        }
284
-
285
-        $this->setJoin($query);
286
-
287
-        return parent::getRelationCountQuery($query, $parent);
288
-    }
289
-
290
-    /**
291
-     * Add the constraints for a relationship count query on the same table.
292
-     *
293
-     * @param  Query $query
294
-     * @param  Query $parent
295
-     * @return Query
296
-     */
297
-    public function getRelationCountQueryForSelfJoin(Query $query, Query $parent)
298
-    {
299
-        $query->select(new Expression('count(*)'));
300
-
301
-        $tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
302
-
303
-        $query->from($this->table . ' as ' . $tablePrefix . $hash = $this->getRelationCountHash());
304
-
305
-        $key = $this->wrap($this->getQualifiedParentKeyName());
306
-
307
-        return $query->where($hash . '.' . $this->foreignKey, '=', new Expression($key));
308
-    }
309
-
310
-    /**
311
-     * Get a relationship join table hash.
312
-     *
313
-     * @return string
314
-     */
315
-    public function getRelationCountHash()
316
-    {
317
-        return 'self_' . md5(microtime(true));
318
-    }
319
-
320
-    /**
321
-     * Set the select clause for the relation query.
322
-     *
323
-     * @param  array $columns
324
-     * @return \Analogue\ORM\Relationships\BelongsToMany
325
-     */
326
-    protected function getSelectColumns(array $columns = ['*'])
327
-    {
328
-        if ($columns == ['*']) {
329
-            $columns = [$this->relatedMap->getTable() . '.*'];
330
-        }
331
-
332
-        return array_merge($columns, $this->getAliasedPivotColumns());
333
-    }
334
-
335
-    /**
336
-     * Get the pivot columns for the relation.
337
-     *
338
-     * @return array
339
-     */
340
-    protected function getAliasedPivotColumns()
341
-    {
342
-        $defaults = [$this->foreignKey, $this->otherKey];
343
-
344
-        // We need to alias all of the pivot columns with the "pivot_" prefix so we
345
-        // can easily extract them out of the models and put them into the pivot
346
-        // relationships when they are retrieved and hydrated into the models.
347
-        $columns = [];
348
-
349
-        foreach (array_merge($defaults, $this->pivotColumns) as $column) {
350
-            $columns[] = $this->table . '.' . $column . ' as pivot_' . $column;
351
-        }
352
-
353
-        return array_unique($columns);
354
-    }
355
-
356
-    /**
357
-     * Set the join clause for the relation query.
358
-     *
359
-     * @param  \Analogue\ORM\Query|null
360
-     * @return $this
361
-     */
362
-    protected function setJoin($query = null)
363
-    {
364
-        $query = $query ?: $this->query;
365
-
366
-        // We need to join to the intermediate table on the related model's primary
367
-        // key column with the intermediate table's foreign key for the related
368
-        // model instance. Then we can set the "where" for the parent models.
369
-        $baseTable = $this->relatedMap->getTable();
370
-
371
-        $key = $baseTable . '.' . $this->relatedMap->getKeyName();
372
-
373
-        $query->join($this->table, $key, '=', $this->getOtherKey());
374
-
375
-        return $this;
376
-    }
377
-
378
-    /**
379
-     * Set the where clause for the relation query.
380
-     *
381
-     * @return $this
382
-     */
383
-    protected function setWhere()
384
-    {
385
-        $foreign = $this->getForeignKey();
386
-
387
-        $parentKey = $this->parentMap->getKeyName();
388
-
389
-        $this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey));
390
-
391
-        return $this;
392
-    }
393
-
394
-    /**
395
-     * Set the constraints for an eager load of the relation.
396
-     *
397
-     * @param  array $results
398
-     * @return void
399
-     */
400
-    public function addEagerConstraints(array $results)
401
-    {
402
-        $this->query->whereIn($this->getForeignKey(), $this->getKeysFromResults($results));
403
-    }
404
-
405
-    /**
406
-     * Match Eagerly loaded relation to result
407
-     *
408
-     * @param  array            $results
409
-     * @param  string           $relation
410
-     * @return array
411
-     */
412
-    public function match(array $results, $relation)
413
-    {
414
-        $entities = $this->getEager();
415
-
416
-        // TODO; optimize this operation
417
-        $dictionary = $this->buildDictionary($entities);
418
-
419
-        $keyName = $this->relatedMap->getKeyName();
420
-
421
-        $cache = $this->parentMapper->getEntityCache();
422
-
423
-        $host = $this;
424
-
425
-        // Once we have an array dictionary of child objects we can easily match the
426
-        // children back to their parent using the dictionary and the keys on the
427
-        // the parent models. Then we will return the hydrated models back out.
428
-        return array_map(function($result) use ($dictionary, $keyName, $cache, $relation, $host) {
429
-
430
-            if (isset($dictionary[$key = $result[$keyName]])) {
431
-                $collection = $host->relatedMap->newCollection($dictionary[$key]);
432
-
433
-                $result[$relation] = $collection;
434
-
435
-                // TODO Refactor this
436
-                $cache->cacheLoadedRelationResult($key, $relation, $collection, $this);
437
-            }
438
-            else {
439
-                $result[$relation] = $host->relatedMap->newCollection();
440
-            }
441
-            return $result;
442
-
443
-        }, $results);
444
-    }
445
-
446
-    /**
447
-     * Build model dictionary keyed by the relation's foreign key.
448
-     *
449
-     * @param  EntityCollection $results
450
-     * @return array
451
-     */
452
-    protected function buildDictionary(EntityCollection $results)
453
-    {
454
-        $foreign = $this->foreignKey;
455
-
456
-        // First we will build a dictionary of child models keyed by the foreign key
457
-        // of the relation so that we will easily and quickly match them to their
458
-        // parents without having a possibly slow inner loops for every models.
459
-        $dictionary = [];
460
-
461
-        foreach ($results as $entity) {
462
-            $wrapper = $this->factory->make($entity);
463
-            $dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity;
464
-        }
465
-
466
-        return $dictionary;
467
-    }
468
-
469
-    /**
470
-     * Get all of the IDs for the related models.
471
-     *
472
-     * @return array
473
-     */
474
-    public function getRelatedIds()
475
-    {
476
-        $fullKey = $this->relatedMap->getQualifiedKeyName();
477
-
478
-        return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName());
479
-    }
480
-
481
-    /**
482
-     * Update Pivot
483
-     *
484
-     * @param  \Analogue\ORM\Entity $entity
485
-     * @return void
486
-     */
487
-    public function updatePivot($entity)
488
-    {
489
-        $keyName = $this->relatedMap->getKeyName();
490
-
491
-        $this->updateExistingPivot(
492
-            $entity->getEntityAttribute($keyName),
493
-            $entity->getEntityAttribute('pivot')->getEntityAttributes()
494
-        );
495
-    }
496
-
497
-    /**
498
-     * Update Multiple pivot
499
-     *
500
-     * @param  $relatedEntities
501
-     * @return void
502
-     */
503
-    public function updatePivots($relatedEntities)
504
-    {
505
-        foreach ($relatedEntities as $entity) {
506
-            $this->updatePivot($entity);
507
-        }
508
-    }
509
-
510
-    /**
511
-     * Create Pivot Records
512
-     *
513
-     * @param \Analogue\ORM\Entity[] $relatedEntities
514
-     * @return void
515
-     */
516
-    public function createPivots($relatedEntities)
517
-    {
518
-        $keys = [];
519
-        $attributes = [];
520
-
521
-        $keyName = $this->relatedMap->getKeyName();
522
-
523
-        foreach ($relatedEntities as $entity) {
524
-            $keys[] = $entity->getEntityAttribute($keyName);
525
-        }
526
-
527
-        $records = $this->createAttachRecords($keys, $attributes);
528
-
529
-        $this->query->getQuery()->from($this->table)->insert($records);
530
-    }
531
-
532
-    /**
533
-     * Update an existing pivot record on the table.
534
-     *
535
-     * @param  mixed $id
536
-     * @param  array $attributes
537
-     * @throws \InvalidArgumentException
538
-     * @return integer
539
-     */
540
-    public function updateExistingPivot($id, array $attributes)
541
-    {
542
-        if (in_array($this->updatedAt(), $this->pivotColumns)) {
543
-            $attributes = $this->setTimestampsOnAttach($attributes, true);
544
-        }
545
-
546
-        return $this->newPivotStatementForId($id)->update($attributes);
547
-    }
548
-
549
-    /**
550
-     * Attach a model to the parent.
551
-     *
552
-     * @param  mixed $id
553
-     * @param  array $attributes
554
-     * @return void
555
-     */
556
-    public function attach($id, array $attributes = [])
557
-    {
558
-        $query = $this->newPivotStatement();
559
-
560
-        $query->insert($this->createAttachRecords((array) $id, $attributes));
561
-    }
562
-
563
-    /**
564
-     * @param  array $entities
565
-     *
566
-     * @throws \InvalidArgumentException
567
-     */
568
-    public function sync(array $entities)
569
-    {
570
-        $this->detachExcept($entities);
571
-    }
572
-
573
-    /**
574
-     * Detach related entities that are not in $id
575
-     *
576
-     * @param  array $entities
577
-     *
578
-     * @throws \InvalidArgumentException
579
-     *
580
-     * @return void
581
-     */
582
-    protected function detachExcept(array $entities = [])
583
-    {
584
-        $query = $this->newPivotQuery();
585
-
586
-        if (count($entities) > 0) {
587
-            $keys = $this->getKeys($entities);
588
-
589
-            $query->whereNotIn($this->otherKey, $keys);
590
-        }
591
-        $parentKey = $this->parentMap->getKeyName();
592
-
593
-        $query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey));
205
+		return $this->relatedMap->newCollection($entities);
206
+	}
207
+
208
+	/**
209
+	 * Hydrate the pivot table relationship on the models.
210
+	 *
211
+	 * @param  array $entities
212
+	 * @return void
213
+	 */
214
+	protected function hydratePivotRelation(array $entities)
215
+	{
216
+		// TODO (note) We should definitely get rid of the pivot in a next
217
+		// release, as this is not quite relevant in a datamapper context. 
218
+		$host = $this;
219
+		return array_map(function($entity) use ($host) {
220
+			$entityWrapper = $this->factory->make($entity);
221
+
222
+			$pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
223
+			$entityWrapper->setEntityAttribute('pivot', $pivot);
224
+
225
+			return $entityWrapper->getObject();
226
+		}, $entities);
227
+	}
228
+
229
+	/**
230
+	 * Get the pivot attributes from a model.
231
+	 *
232
+	 * @param  $entity
233
+	 * @return array
234
+	 */
235
+	protected function cleanPivotAttributes(InternallyMappable $entity)
236
+	{
237
+		$values = [];
238
+
239
+		$attributes = $entity->getEntityAttributes();
240
+
241
+		foreach ($attributes as $key => $value) {
242
+			// To get the pivots attributes we will just take any of the attributes which
243
+			// begin with "pivot_" and add those to this arrays, as well as unsetting
244
+			// them from the parent's models since they exist in a different table.
245
+			if (strpos($key, 'pivot_') === 0) {
246
+				$values[substr($key, 6)] = $value;
247
+
248
+				unset($attributes[$key]);
249
+			}
250
+		}
251
+
252
+		// Rehydrate Entity with cleaned array.
253
+		$entity->setEntityAttributes($attributes);
254
+
255
+		return $values;
256
+	}
257
+
258
+	/**
259
+	 * Set the base constraints on the relation query.
260
+	 *
261
+	 * @return void
262
+	 */
263
+	public function addConstraints()
264
+	{
265
+		$this->setJoin();
266
+
267
+		if (static::$constraints) {
268
+			$this->setWhere();
269
+		}
270
+	}
271
+
272
+	/**
273
+	 * Add the constraints for a relationship count query.
274
+	 *
275
+	 * @param  Query $query
276
+	 * @param  Query $parent
277
+	 * @return Query
278
+	 */
279
+	public function getRelationCountQuery(Query $query, Query $parent)
280
+	{
281
+		if ($parent->getQuery()->from == $query->getQuery()->from) {
282
+			return $this->getRelationCountQueryForSelfJoin($query, $parent);
283
+		}
284
+
285
+		$this->setJoin($query);
286
+
287
+		return parent::getRelationCountQuery($query, $parent);
288
+	}
289
+
290
+	/**
291
+	 * Add the constraints for a relationship count query on the same table.
292
+	 *
293
+	 * @param  Query $query
294
+	 * @param  Query $parent
295
+	 * @return Query
296
+	 */
297
+	public function getRelationCountQueryForSelfJoin(Query $query, Query $parent)
298
+	{
299
+		$query->select(new Expression('count(*)'));
300
+
301
+		$tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
302
+
303
+		$query->from($this->table . ' as ' . $tablePrefix . $hash = $this->getRelationCountHash());
304
+
305
+		$key = $this->wrap($this->getQualifiedParentKeyName());
306
+
307
+		return $query->where($hash . '.' . $this->foreignKey, '=', new Expression($key));
308
+	}
309
+
310
+	/**
311
+	 * Get a relationship join table hash.
312
+	 *
313
+	 * @return string
314
+	 */
315
+	public function getRelationCountHash()
316
+	{
317
+		return 'self_' . md5(microtime(true));
318
+	}
319
+
320
+	/**
321
+	 * Set the select clause for the relation query.
322
+	 *
323
+	 * @param  array $columns
324
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
325
+	 */
326
+	protected function getSelectColumns(array $columns = ['*'])
327
+	{
328
+		if ($columns == ['*']) {
329
+			$columns = [$this->relatedMap->getTable() . '.*'];
330
+		}
331
+
332
+		return array_merge($columns, $this->getAliasedPivotColumns());
333
+	}
334
+
335
+	/**
336
+	 * Get the pivot columns for the relation.
337
+	 *
338
+	 * @return array
339
+	 */
340
+	protected function getAliasedPivotColumns()
341
+	{
342
+		$defaults = [$this->foreignKey, $this->otherKey];
343
+
344
+		// We need to alias all of the pivot columns with the "pivot_" prefix so we
345
+		// can easily extract them out of the models and put them into the pivot
346
+		// relationships when they are retrieved and hydrated into the models.
347
+		$columns = [];
348
+
349
+		foreach (array_merge($defaults, $this->pivotColumns) as $column) {
350
+			$columns[] = $this->table . '.' . $column . ' as pivot_' . $column;
351
+		}
352
+
353
+		return array_unique($columns);
354
+	}
355
+
356
+	/**
357
+	 * Set the join clause for the relation query.
358
+	 *
359
+	 * @param  \Analogue\ORM\Query|null
360
+	 * @return $this
361
+	 */
362
+	protected function setJoin($query = null)
363
+	{
364
+		$query = $query ?: $this->query;
365
+
366
+		// We need to join to the intermediate table on the related model's primary
367
+		// key column with the intermediate table's foreign key for the related
368
+		// model instance. Then we can set the "where" for the parent models.
369
+		$baseTable = $this->relatedMap->getTable();
370
+
371
+		$key = $baseTable . '.' . $this->relatedMap->getKeyName();
372
+
373
+		$query->join($this->table, $key, '=', $this->getOtherKey());
374
+
375
+		return $this;
376
+	}
377
+
378
+	/**
379
+	 * Set the where clause for the relation query.
380
+	 *
381
+	 * @return $this
382
+	 */
383
+	protected function setWhere()
384
+	{
385
+		$foreign = $this->getForeignKey();
386
+
387
+		$parentKey = $this->parentMap->getKeyName();
388
+
389
+		$this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey));
390
+
391
+		return $this;
392
+	}
393
+
394
+	/**
395
+	 * Set the constraints for an eager load of the relation.
396
+	 *
397
+	 * @param  array $results
398
+	 * @return void
399
+	 */
400
+	public function addEagerConstraints(array $results)
401
+	{
402
+		$this->query->whereIn($this->getForeignKey(), $this->getKeysFromResults($results));
403
+	}
404
+
405
+	/**
406
+	 * Match Eagerly loaded relation to result
407
+	 *
408
+	 * @param  array            $results
409
+	 * @param  string           $relation
410
+	 * @return array
411
+	 */
412
+	public function match(array $results, $relation)
413
+	{
414
+		$entities = $this->getEager();
415
+
416
+		// TODO; optimize this operation
417
+		$dictionary = $this->buildDictionary($entities);
418
+
419
+		$keyName = $this->relatedMap->getKeyName();
420
+
421
+		$cache = $this->parentMapper->getEntityCache();
422
+
423
+		$host = $this;
424
+
425
+		// Once we have an array dictionary of child objects we can easily match the
426
+		// children back to their parent using the dictionary and the keys on the
427
+		// the parent models. Then we will return the hydrated models back out.
428
+		return array_map(function($result) use ($dictionary, $keyName, $cache, $relation, $host) {
429
+
430
+			if (isset($dictionary[$key = $result[$keyName]])) {
431
+				$collection = $host->relatedMap->newCollection($dictionary[$key]);
432
+
433
+				$result[$relation] = $collection;
434
+
435
+				// TODO Refactor this
436
+				$cache->cacheLoadedRelationResult($key, $relation, $collection, $this);
437
+			}
438
+			else {
439
+				$result[$relation] = $host->relatedMap->newCollection();
440
+			}
441
+			return $result;
442
+
443
+		}, $results);
444
+	}
445
+
446
+	/**
447
+	 * Build model dictionary keyed by the relation's foreign key.
448
+	 *
449
+	 * @param  EntityCollection $results
450
+	 * @return array
451
+	 */
452
+	protected function buildDictionary(EntityCollection $results)
453
+	{
454
+		$foreign = $this->foreignKey;
455
+
456
+		// First we will build a dictionary of child models keyed by the foreign key
457
+		// of the relation so that we will easily and quickly match them to their
458
+		// parents without having a possibly slow inner loops for every models.
459
+		$dictionary = [];
460
+
461
+		foreach ($results as $entity) {
462
+			$wrapper = $this->factory->make($entity);
463
+			$dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity;
464
+		}
465
+
466
+		return $dictionary;
467
+	}
468
+
469
+	/**
470
+	 * Get all of the IDs for the related models.
471
+	 *
472
+	 * @return array
473
+	 */
474
+	public function getRelatedIds()
475
+	{
476
+		$fullKey = $this->relatedMap->getQualifiedKeyName();
477
+
478
+		return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName());
479
+	}
480
+
481
+	/**
482
+	 * Update Pivot
483
+	 *
484
+	 * @param  \Analogue\ORM\Entity $entity
485
+	 * @return void
486
+	 */
487
+	public function updatePivot($entity)
488
+	{
489
+		$keyName = $this->relatedMap->getKeyName();
490
+
491
+		$this->updateExistingPivot(
492
+			$entity->getEntityAttribute($keyName),
493
+			$entity->getEntityAttribute('pivot')->getEntityAttributes()
494
+		);
495
+	}
496
+
497
+	/**
498
+	 * Update Multiple pivot
499
+	 *
500
+	 * @param  $relatedEntities
501
+	 * @return void
502
+	 */
503
+	public function updatePivots($relatedEntities)
504
+	{
505
+		foreach ($relatedEntities as $entity) {
506
+			$this->updatePivot($entity);
507
+		}
508
+	}
509
+
510
+	/**
511
+	 * Create Pivot Records
512
+	 *
513
+	 * @param \Analogue\ORM\Entity[] $relatedEntities
514
+	 * @return void
515
+	 */
516
+	public function createPivots($relatedEntities)
517
+	{
518
+		$keys = [];
519
+		$attributes = [];
520
+
521
+		$keyName = $this->relatedMap->getKeyName();
522
+
523
+		foreach ($relatedEntities as $entity) {
524
+			$keys[] = $entity->getEntityAttribute($keyName);
525
+		}
526
+
527
+		$records = $this->createAttachRecords($keys, $attributes);
528
+
529
+		$this->query->getQuery()->from($this->table)->insert($records);
530
+	}
531
+
532
+	/**
533
+	 * Update an existing pivot record on the table.
534
+	 *
535
+	 * @param  mixed $id
536
+	 * @param  array $attributes
537
+	 * @throws \InvalidArgumentException
538
+	 * @return integer
539
+	 */
540
+	public function updateExistingPivot($id, array $attributes)
541
+	{
542
+		if (in_array($this->updatedAt(), $this->pivotColumns)) {
543
+			$attributes = $this->setTimestampsOnAttach($attributes, true);
544
+		}
545
+
546
+		return $this->newPivotStatementForId($id)->update($attributes);
547
+	}
548
+
549
+	/**
550
+	 * Attach a model to the parent.
551
+	 *
552
+	 * @param  mixed $id
553
+	 * @param  array $attributes
554
+	 * @return void
555
+	 */
556
+	public function attach($id, array $attributes = [])
557
+	{
558
+		$query = $this->newPivotStatement();
559
+
560
+		$query->insert($this->createAttachRecords((array) $id, $attributes));
561
+	}
562
+
563
+	/**
564
+	 * @param  array $entities
565
+	 *
566
+	 * @throws \InvalidArgumentException
567
+	 */
568
+	public function sync(array $entities)
569
+	{
570
+		$this->detachExcept($entities);
571
+	}
572
+
573
+	/**
574
+	 * Detach related entities that are not in $id
575
+	 *
576
+	 * @param  array $entities
577
+	 *
578
+	 * @throws \InvalidArgumentException
579
+	 *
580
+	 * @return void
581
+	 */
582
+	protected function detachExcept(array $entities = [])
583
+	{
584
+		$query = $this->newPivotQuery();
585
+
586
+		if (count($entities) > 0) {
587
+			$keys = $this->getKeys($entities);
588
+
589
+			$query->whereNotIn($this->otherKey, $keys);
590
+		}
591
+		$parentKey = $this->parentMap->getKeyName();
592
+
593
+		$query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey));
594 594
         
595
-        $query->delete();
596
-
597
-        $query = $this->newPivotQuery();
598
-    }
599
-
600
-
601
-    /**
602
-     * Create an array of records to insert into the pivot table.
603
-     *
604
-     * @param  array $ids
605
-     * @param  array $attributes
606
-     * @return array
607
-     */
608
-    protected function createAttachRecords($ids, array $attributes)
609
-    {
610
-        $records = [];
611
-
612
-        $timed = in_array($this->createdAt(), $this->pivotColumns);
613
-
614
-        // To create the attachment records, we will simply spin through the IDs given
615
-        // and create a new record to insert for each ID. Each ID may actually be a
616
-        // key in the array, with extra attributes to be placed in other columns.
617
-        foreach ($ids as $key => $value) {
618
-            $records[] = $this->attacher($key, $value, $attributes, $timed);
619
-        }
595
+		$query->delete();
596
+
597
+		$query = $this->newPivotQuery();
598
+	}
599
+
600
+
601
+	/**
602
+	 * Create an array of records to insert into the pivot table.
603
+	 *
604
+	 * @param  array $ids
605
+	 * @param  array $attributes
606
+	 * @return array
607
+	 */
608
+	protected function createAttachRecords($ids, array $attributes)
609
+	{
610
+		$records = [];
611
+
612
+		$timed = in_array($this->createdAt(), $this->pivotColumns);
613
+
614
+		// To create the attachment records, we will simply spin through the IDs given
615
+		// and create a new record to insert for each ID. Each ID may actually be a
616
+		// key in the array, with extra attributes to be placed in other columns.
617
+		foreach ($ids as $key => $value) {
618
+			$records[] = $this->attacher($key, $value, $attributes, $timed);
619
+		}
620 620
         
621
-        return $records;
622
-    }
623
-
624
-    /**
625
-     * Create a full attachment record payload.
626
-     *
627
-     * @param  int   $key
628
-     * @param  mixed $value
629
-     * @param  array $attributes
630
-     * @param  bool  $timed
631
-     * @return array
632
-     */
633
-    protected function attacher($key, $value, $attributes, $timed)
634
-    {
635
-        list($id, $extra) = $this->getAttachId($key, $value, $attributes);
621
+		return $records;
622
+	}
623
+
624
+	/**
625
+	 * Create a full attachment record payload.
626
+	 *
627
+	 * @param  int   $key
628
+	 * @param  mixed $value
629
+	 * @param  array $attributes
630
+	 * @param  bool  $timed
631
+	 * @return array
632
+	 */
633
+	protected function attacher($key, $value, $attributes, $timed)
634
+	{
635
+		list($id, $extra) = $this->getAttachId($key, $value, $attributes);
636 636
         
637
-        // To create the attachment records, we will simply spin through the IDs given
638
-        // and create a new record to insert for each ID. Each ID may actually be a
639
-        // key in the array, with extra attributes to be placed in other columns.
640
-        $record = $this->createAttachRecord($id, $timed);
641
-
642
-        return array_merge($record, $extra);
643
-    }
644
-
645
-    /**
646
-     * Get the attach record ID and extra attributes.
647
-     *
648
-     * @param  int   $key
649
-     * @param  mixed $value
650
-     * @param  array $attributes
651
-     * @return array
652
-     */
653
-    protected function getAttachId($key, $value, array $attributes)
654
-    {
655
-        if (is_array($value)) {
656
-            return [$key, array_merge($value, $attributes)];
657
-        }
658
-
659
-        return [$value, $attributes];
660
-    }
661
-
662
-    /**
663
-     * Create a new pivot attachment record.
664
-     *
665
-     * @param  int  $id
666
-     * @param  bool $timed
667
-     * @return array
668
-     */
669
-    protected function createAttachRecord($id, $timed)
670
-    {
671
-        $parentKey = $this->parentMap->getKeyName();
672
-
673
-        $record = [];
674
-
675
-        $record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey);
676
-
677
-        $record[$this->otherKey] = $id;
678
-
679
-        // If the record needs to have creation and update timestamps, we will make
680
-        // them by calling the parent model's "freshTimestamp" method which will
681
-        // provide us with a fresh timestamp in this model's preferred format.
682
-        if ($timed) {
683
-            $record = $this->setTimestampsOnAttach($record);
684
-        }
685
-
686
-        return $record;
687
-    }
688
-
689
-    /**
690
-     * Set the creation and update timestamps on an attach record.
691
-     *
692
-     * @param  array $record
693
-     * @param  bool  $exists
694
-     * @return array
695
-     */
696
-    protected function setTimestampsOnAttach(array $record, $exists = false)
697
-    {
698
-        $fresh = $this->freshTimestamp();
699
-
700
-        if (!$exists) {
701
-            $record[$this->createdAt()] = $fresh;
702
-        }
703
-
704
-        $record[$this->updatedAt()] = $fresh;
705
-
706
-        return $record;
707
-    }
708
-
709
-    /**
710
-     * @param EntityCollection $entities
711
-     * @return array
712
-     */
713
-    protected function getModelKeysFromCollection(EntityCollection $entities)
714
-    {
715
-        $keyName = $this->relatedMap->getKeyName();
716
-
717
-        return array_map(function ($m) use ($keyName) {
718
-            return $m->$keyName;
719
-        }, $entities);
720
-    }
721
-
722
-    /**
723
-     * Detach models from the relationship.
724
-     *
725
-     * @param  int|array $ids
726
-     * @throws \InvalidArgumentException
727
-     * @return int
728
-     */
729
-    public function detach($ids = [])
730
-    {
731
-        if ($ids instanceof EntityCollection) {
732
-            $ids = (array) $ids->modelKeys();
733
-        }
734
-
735
-        $query = $this->newPivotQuery();
736
-
737
-        // If associated IDs were passed to the method we will only delete those
738
-        // associations, otherwise all of the association ties will be broken.
739
-        // We'll return the numbers of affected rows when we do the deletes.
740
-        $ids = (array) $ids;
741
-
742
-        if (count($ids) > 0) {
743
-            $query->whereIn($this->otherKey, (array) $ids);
744
-        }
745
-
746
-        // Once we have all of the conditions set on the statement, we are ready
747
-        // to run the delete on the pivot table. Then, if the touch parameter
748
-        // is true, we will go ahead and touch all related models to sync.
749
-        return $query->delete();
750
-    }
637
+		// To create the attachment records, we will simply spin through the IDs given
638
+		// and create a new record to insert for each ID. Each ID may actually be a
639
+		// key in the array, with extra attributes to be placed in other columns.
640
+		$record = $this->createAttachRecord($id, $timed);
641
+
642
+		return array_merge($record, $extra);
643
+	}
644
+
645
+	/**
646
+	 * Get the attach record ID and extra attributes.
647
+	 *
648
+	 * @param  int   $key
649
+	 * @param  mixed $value
650
+	 * @param  array $attributes
651
+	 * @return array
652
+	 */
653
+	protected function getAttachId($key, $value, array $attributes)
654
+	{
655
+		if (is_array($value)) {
656
+			return [$key, array_merge($value, $attributes)];
657
+		}
658
+
659
+		return [$value, $attributes];
660
+	}
661
+
662
+	/**
663
+	 * Create a new pivot attachment record.
664
+	 *
665
+	 * @param  int  $id
666
+	 * @param  bool $timed
667
+	 * @return array
668
+	 */
669
+	protected function createAttachRecord($id, $timed)
670
+	{
671
+		$parentKey = $this->parentMap->getKeyName();
672
+
673
+		$record = [];
674
+
675
+		$record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey);
676
+
677
+		$record[$this->otherKey] = $id;
678
+
679
+		// If the record needs to have creation and update timestamps, we will make
680
+		// them by calling the parent model's "freshTimestamp" method which will
681
+		// provide us with a fresh timestamp in this model's preferred format.
682
+		if ($timed) {
683
+			$record = $this->setTimestampsOnAttach($record);
684
+		}
685
+
686
+		return $record;
687
+	}
688
+
689
+	/**
690
+	 * Set the creation and update timestamps on an attach record.
691
+	 *
692
+	 * @param  array $record
693
+	 * @param  bool  $exists
694
+	 * @return array
695
+	 */
696
+	protected function setTimestampsOnAttach(array $record, $exists = false)
697
+	{
698
+		$fresh = $this->freshTimestamp();
699
+
700
+		if (!$exists) {
701
+			$record[$this->createdAt()] = $fresh;
702
+		}
703
+
704
+		$record[$this->updatedAt()] = $fresh;
705
+
706
+		return $record;
707
+	}
708
+
709
+	/**
710
+	 * @param EntityCollection $entities
711
+	 * @return array
712
+	 */
713
+	protected function getModelKeysFromCollection(EntityCollection $entities)
714
+	{
715
+		$keyName = $this->relatedMap->getKeyName();
716
+
717
+		return array_map(function ($m) use ($keyName) {
718
+			return $m->$keyName;
719
+		}, $entities);
720
+	}
721
+
722
+	/**
723
+	 * Detach models from the relationship.
724
+	 *
725
+	 * @param  int|array $ids
726
+	 * @throws \InvalidArgumentException
727
+	 * @return int
728
+	 */
729
+	public function detach($ids = [])
730
+	{
731
+		if ($ids instanceof EntityCollection) {
732
+			$ids = (array) $ids->modelKeys();
733
+		}
734
+
735
+		$query = $this->newPivotQuery();
736
+
737
+		// If associated IDs were passed to the method we will only delete those
738
+		// associations, otherwise all of the association ties will be broken.
739
+		// We'll return the numbers of affected rows when we do the deletes.
740
+		$ids = (array) $ids;
741
+
742
+		if (count($ids) > 0) {
743
+			$query->whereIn($this->otherKey, (array) $ids);
744
+		}
745
+
746
+		// Once we have all of the conditions set on the statement, we are ready
747
+		// to run the delete on the pivot table. Then, if the touch parameter
748
+		// is true, we will go ahead and touch all related models to sync.
749
+		return $query->delete();
750
+	}
751 751
     
752
-    /**
753
-     * Create a new query builder for the pivot table.
754
-     *
755
-     * @throws \InvalidArgumentException
756
-     *
757
-     * @return \Illuminate\Database\Query\Builder
758
-     */
759
-    protected function newPivotQuery()
760
-    {
761
-        $query = $this->newPivotStatement();
762
-
763
-        $parentKey = $this->parentMap->getKeyName();
764
-
765
-        return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey));
766
-    }
767
-
768
-    /**
769
-     * Get a new plain query builder for the pivot table.
770
-     *
771
-     * @return \Illuminate\Database\Query\Builder
772
-     */
773
-    public function newPivotStatement()
774
-    {
775
-        return $this->query->getQuery()->newQuery()->from($this->table);
776
-    }
777
-
778
-    /**
779
-     * Get a new pivot statement for a given "other" ID.
780
-     *
781
-     * @param  mixed $id
782
-     *
783
-     * @throws \InvalidArgumentException
784
-     *
785
-     * @return \Illuminate\Database\Query\Builder
786
-     */
787
-    public function newPivotStatementForId($id)
788
-    {
789
-        $pivot = $this->newPivotStatement();
790
-
791
-        $parentKeyName = $this->parentMap->getKeyName();
792
-
793
-        $key = $this->parent->getEntityAttribute($parentKeyName);
794
-
795
-        return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id);
796
-    }
797
-
798
-    /**
799
-     * Create a new pivot model instance.
800
-     *
801
-     * @param  array $attributes
802
-     * @param  bool  $exists
803
-     * @return \Analogue\ORM\Relationships\Pivot
804
-     */
805
-    public function newPivot(array $attributes = [], $exists = false)
806
-    {
807
-        $pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
752
+	/**
753
+	 * Create a new query builder for the pivot table.
754
+	 *
755
+	 * @throws \InvalidArgumentException
756
+	 *
757
+	 * @return \Illuminate\Database\Query\Builder
758
+	 */
759
+	protected function newPivotQuery()
760
+	{
761
+		$query = $this->newPivotStatement();
762
+
763
+		$parentKey = $this->parentMap->getKeyName();
764
+
765
+		return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey));
766
+	}
767
+
768
+	/**
769
+	 * Get a new plain query builder for the pivot table.
770
+	 *
771
+	 * @return \Illuminate\Database\Query\Builder
772
+	 */
773
+	public function newPivotStatement()
774
+	{
775
+		return $this->query->getQuery()->newQuery()->from($this->table);
776
+	}
777
+
778
+	/**
779
+	 * Get a new pivot statement for a given "other" ID.
780
+	 *
781
+	 * @param  mixed $id
782
+	 *
783
+	 * @throws \InvalidArgumentException
784
+	 *
785
+	 * @return \Illuminate\Database\Query\Builder
786
+	 */
787
+	public function newPivotStatementForId($id)
788
+	{
789
+		$pivot = $this->newPivotStatement();
790
+
791
+		$parentKeyName = $this->parentMap->getKeyName();
792
+
793
+		$key = $this->parent->getEntityAttribute($parentKeyName);
794
+
795
+		return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id);
796
+	}
797
+
798
+	/**
799
+	 * Create a new pivot model instance.
800
+	 *
801
+	 * @param  array $attributes
802
+	 * @param  bool  $exists
803
+	 * @return \Analogue\ORM\Relationships\Pivot
804
+	 */
805
+	public function newPivot(array $attributes = [], $exists = false)
806
+	{
807
+		$pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
808 808
         
809
-        return $pivot->setPivotKeys($this->foreignKey, $this->otherKey);
810
-    }
811
-
812
-    /**
813
-     * Create a new existing pivot model instance.
814
-     *
815
-     * @param  array $attributes
816
-     * @return \Analogue\ORM\Relationships\Pivot
817
-     */
818
-    public function newExistingPivot(array $attributes = [])
819
-    {
820
-        return $this->newPivot($attributes, true);
821
-    }
822
-
823
-    /**
824
-     * Set the columns on the pivot table to retrieve.
825
-     *
826
-     * @param  array $columns
827
-     * @return $this
828
-     */
829
-    public function withPivot($columns)
830
-    {
831
-        $columns = is_array($columns) ? $columns : func_get_args();
832
-
833
-        $this->pivotColumns = array_merge($this->pivotColumns, $columns);
834
-
835
-        return $this;
836
-    }
837
-
838
-    /**
839
-     * Specify that the pivot table has creation and update timestamps.
840
-     *
841
-     * @param  mixed $createdAt
842
-     * @param  mixed $updatedAt
843
-     * @return \Analogue\ORM\Relationships\BelongsToMany
844
-     */
845
-    public function withTimestamps($createdAt = null, $updatedAt = null)
846
-    {
847
-        return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt());
848
-    }
849
-
850
-    /**
851
-     * Get the key for comparing against the parent key in "has" query.
852
-     *
853
-     * @return string
854
-     */
855
-    public function getHasCompareKey()
856
-    {
857
-        return $this->getForeignKey();
858
-    }
859
-
860
-    /**
861
-     * Get the fully qualified foreign key for the relation.
862
-     *
863
-     * @return string
864
-     */
865
-    public function getForeignKey()
866
-    {
867
-        return $this->table . '.' . $this->foreignKey;
868
-    }
869
-
870
-    /**
871
-     * Get the fully qualified "other key" for the relation.
872
-     *
873
-     * @return string
874
-     */
875
-    public function getOtherKey()
876
-    {
877
-        return $this->table . '.' . $this->otherKey;
878
-    }
879
-
880
-    /**
881
-     * Get the fully qualified parent key name.
882
-     *
883
-     * @return string
884
-     */
885
-    protected function getQualifiedParentKeyName()
886
-    {
887
-        return $this->parentMap->getQualifiedKeyName();
888
-    }
889
-
890
-    /**
891
-     * Get the intermediate table for the relationship.
892
-     *
893
-     * @return string
894
-     */
895
-    public function getTable()
896
-    {
897
-        return $this->table;
898
-    }
899
-
900
-    /**
901
-     * Get the relationship name for the relationship.
902
-     *
903
-     * @return string
904
-     */
905
-    public function getRelationName()
906
-    {
907
-        return $this->relationName;
908
-    }
809
+		return $pivot->setPivotKeys($this->foreignKey, $this->otherKey);
810
+	}
811
+
812
+	/**
813
+	 * Create a new existing pivot model instance.
814
+	 *
815
+	 * @param  array $attributes
816
+	 * @return \Analogue\ORM\Relationships\Pivot
817
+	 */
818
+	public function newExistingPivot(array $attributes = [])
819
+	{
820
+		return $this->newPivot($attributes, true);
821
+	}
822
+
823
+	/**
824
+	 * Set the columns on the pivot table to retrieve.
825
+	 *
826
+	 * @param  array $columns
827
+	 * @return $this
828
+	 */
829
+	public function withPivot($columns)
830
+	{
831
+		$columns = is_array($columns) ? $columns : func_get_args();
832
+
833
+		$this->pivotColumns = array_merge($this->pivotColumns, $columns);
834
+
835
+		return $this;
836
+	}
837
+
838
+	/**
839
+	 * Specify that the pivot table has creation and update timestamps.
840
+	 *
841
+	 * @param  mixed $createdAt
842
+	 * @param  mixed $updatedAt
843
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
844
+	 */
845
+	public function withTimestamps($createdAt = null, $updatedAt = null)
846
+	{
847
+		return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt());
848
+	}
849
+
850
+	/**
851
+	 * Get the key for comparing against the parent key in "has" query.
852
+	 *
853
+	 * @return string
854
+	 */
855
+	public function getHasCompareKey()
856
+	{
857
+		return $this->getForeignKey();
858
+	}
859
+
860
+	/**
861
+	 * Get the fully qualified foreign key for the relation.
862
+	 *
863
+	 * @return string
864
+	 */
865
+	public function getForeignKey()
866
+	{
867
+		return $this->table . '.' . $this->foreignKey;
868
+	}
869
+
870
+	/**
871
+	 * Get the fully qualified "other key" for the relation.
872
+	 *
873
+	 * @return string
874
+	 */
875
+	public function getOtherKey()
876
+	{
877
+		return $this->table . '.' . $this->otherKey;
878
+	}
879
+
880
+	/**
881
+	 * Get the fully qualified parent key name.
882
+	 *
883
+	 * @return string
884
+	 */
885
+	protected function getQualifiedParentKeyName()
886
+	{
887
+		return $this->parentMap->getQualifiedKeyName();
888
+	}
889
+
890
+	/**
891
+	 * Get the intermediate table for the relationship.
892
+	 *
893
+	 * @return string
894
+	 */
895
+	public function getTable()
896
+	{
897
+		return $this->table;
898
+	}
899
+
900
+	/**
901
+	 * Get the relationship name for the relationship.
902
+	 *
903
+	 * @return string
904
+	 */
905
+	public function getRelationName()
906
+	{
907
+		return $this->relationName;
908
+	}
909 909
 }
Please login to merge, or discard this patch.
src/Relationships/MorphToMany.php 1 patch
Indentation   +162 added lines, -162 removed lines patch added patch discarded remove patch
@@ -7,167 +7,167 @@
 block discarded – undo
7 7
 
8 8
 class MorphToMany extends BelongsToMany
9 9
 {
10
-    /**
11
-     * The type of the polymorphic relation.
12
-     *
13
-     * @var string
14
-     */
15
-    protected $morphType;
16
-
17
-    /**
18
-     * The class name of the morph type constraint.
19
-     *
20
-     * @var string
21
-     */
22
-    protected $morphClass;
23
-
24
-    /**
25
-     * Indicates if we are connecting the inverse of the relation.
26
-     *
27
-     * This primarily affects the morphClass constraint.
28
-     *
29
-     * @var bool
30
-     */
31
-    protected $inverse;
32
-
33
-    protected static $hasPivot = true;
34
-
35
-    /**
36
-     * Create a new has many relationship instance.
37
-     *
38
-     * @param Mapper                $mapper
39
-     * @param  \Analogue\ORM\Entity $parent
40
-     * @param  string               $name
41
-     * @param  string               $table
42
-     * @param  string               $foreignKey
43
-     * @param  string               $otherKey
44
-     * @param  string|null          $relationName
45
-     * @param  bool                 $inverse
46
-     */
47
-    public function __construct(Mapper $mapper, $parent, $name, $table, $foreignKey, $otherKey, $relationName = null, $inverse = false)
48
-    {
49
-        $this->inverse = $inverse;
10
+	/**
11
+	 * The type of the polymorphic relation.
12
+	 *
13
+	 * @var string
14
+	 */
15
+	protected $morphType;
16
+
17
+	/**
18
+	 * The class name of the morph type constraint.
19
+	 *
20
+	 * @var string
21
+	 */
22
+	protected $morphClass;
23
+
24
+	/**
25
+	 * Indicates if we are connecting the inverse of the relation.
26
+	 *
27
+	 * This primarily affects the morphClass constraint.
28
+	 *
29
+	 * @var bool
30
+	 */
31
+	protected $inverse;
32
+
33
+	protected static $hasPivot = true;
34
+
35
+	/**
36
+	 * Create a new has many relationship instance.
37
+	 *
38
+	 * @param Mapper                $mapper
39
+	 * @param  \Analogue\ORM\Entity $parent
40
+	 * @param  string               $name
41
+	 * @param  string               $table
42
+	 * @param  string               $foreignKey
43
+	 * @param  string               $otherKey
44
+	 * @param  string|null          $relationName
45
+	 * @param  bool                 $inverse
46
+	 */
47
+	public function __construct(Mapper $mapper, $parent, $name, $table, $foreignKey, $otherKey, $relationName = null, $inverse = false)
48
+	{
49
+		$this->inverse = $inverse;
50 50
         
51
-        $this->morphType = $name . '_type';
52
-
53
-        $this->morphClass = $inverse ? $mapper->getEntityMap()->getClass() : get_class($parent);
54
-
55
-        parent::__construct($mapper, $parent, $table, $foreignKey, $otherKey, $relationName);
56
-    }
57
-
58
-    /**
59
-     * Set the where clause for the relation query.
60
-     *
61
-     * @return self
62
-     */
63
-    protected function setWhere()
64
-    {
65
-        parent::setWhere();
66
-
67
-        $this->query->where($this->table . '.' . $this->morphType, $this->morphClass);
68
-
69
-        return $this;
70
-    }
71
-
72
-    /**
73
-     * Add the constraints for a relationship count query.
74
-     *
75
-     * @param  Query $query
76
-     * @param  Query $parent
77
-     * @return Query
78
-     */
79
-    public function getRelationCountQuery(Query $query, Query $parent)
80
-    {
81
-        $query = parent::getRelationCountQuery($query, $parent);
82
-
83
-        return $query->where($this->table . '.' . $this->morphType, $this->morphClass);
84
-    }
85
-
86
-    /**
87
-     * Set the constraints for an eager load of the relation.
88
-     *
89
-     * @param  array $results
90
-     * @return void
91
-     */
92
-    public function addEagerConstraints(array $results)
93
-    {
94
-        parent::addEagerConstraints($results);
95
-
96
-        $this->query->where($this->table . '.' . $this->morphType, $this->morphClass);
97
-    }
98
-
99
-    /**
100
-     * Create a new pivot attachment record.
101
-     *
102
-     * @param  int  $id
103
-     * @param  bool $timed
104
-     * @return array
105
-     */
106
-    protected function createAttachRecord($id, $timed)
107
-    {
108
-        $record = parent::createAttachRecord($id, $timed);
109
-
110
-        return array_add($record, $this->morphType, $this->morphClass);
111
-    }
112
-
113
-    /**
114
-     * Create a new query builder for the pivot table.
115
-     *
116
-     * @throws \InvalidArgumentException
117
-     * @return \Illuminate\Database\Query\Builder
118
-     */
119
-    protected function newPivotQuery()
120
-    {
121
-        $query = parent::newPivotQuery();
122
-
123
-        return $query->where($this->morphType, $this->morphClass);
124
-    }
125
-
126
-    /**
127
-     * Create a new pivot model instance.
128
-     *
129
-     * @param  array $attributes
130
-     * @param  bool  $exists
131
-     * @return Pivot
132
-     */
133
-    public function newPivot(array $attributes = [], $exists = false)
134
-    {
135
-        $pivot = new MorphPivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
136
-
137
-        $pivot->setPivotKeys($this->foreignKey, $this->otherKey)
138
-            ->setMorphType($this->morphType)
139
-            ->setMorphClass($this->morphClass);
140
-
141
-        return $pivot;
142
-    }
143
-
144
-    /**
145
-     * Return Pivot attributes when available on a relationship
146
-     *
147
-     * @return array
148
-     */
149
-    public function getPivotAttributes()
150
-    {
151
-        return $this->pivotColumns;
152
-    }
153
-
154
-    /**
155
-     * Get the foreign key "type" name.
156
-     *
157
-     * @return string
158
-     */
159
-    public function getMorphType()
160
-    {
161
-        return $this->morphType;
162
-    }
163
-
164
-    /**
165
-     * Get the class name of the parent model.
166
-     *
167
-     * @return string
168
-     */
169
-    public function getMorphClass()
170
-    {
171
-        return $this->morphClass;
172
-    }
51
+		$this->morphType = $name . '_type';
52
+
53
+		$this->morphClass = $inverse ? $mapper->getEntityMap()->getClass() : get_class($parent);
54
+
55
+		parent::__construct($mapper, $parent, $table, $foreignKey, $otherKey, $relationName);
56
+	}
57
+
58
+	/**
59
+	 * Set the where clause for the relation query.
60
+	 *
61
+	 * @return self
62
+	 */
63
+	protected function setWhere()
64
+	{
65
+		parent::setWhere();
66
+
67
+		$this->query->where($this->table . '.' . $this->morphType, $this->morphClass);
68
+
69
+		return $this;
70
+	}
71
+
72
+	/**
73
+	 * Add the constraints for a relationship count query.
74
+	 *
75
+	 * @param  Query $query
76
+	 * @param  Query $parent
77
+	 * @return Query
78
+	 */
79
+	public function getRelationCountQuery(Query $query, Query $parent)
80
+	{
81
+		$query = parent::getRelationCountQuery($query, $parent);
82
+
83
+		return $query->where($this->table . '.' . $this->morphType, $this->morphClass);
84
+	}
85
+
86
+	/**
87
+	 * Set the constraints for an eager load of the relation.
88
+	 *
89
+	 * @param  array $results
90
+	 * @return void
91
+	 */
92
+	public function addEagerConstraints(array $results)
93
+	{
94
+		parent::addEagerConstraints($results);
95
+
96
+		$this->query->where($this->table . '.' . $this->morphType, $this->morphClass);
97
+	}
98
+
99
+	/**
100
+	 * Create a new pivot attachment record.
101
+	 *
102
+	 * @param  int  $id
103
+	 * @param  bool $timed
104
+	 * @return array
105
+	 */
106
+	protected function createAttachRecord($id, $timed)
107
+	{
108
+		$record = parent::createAttachRecord($id, $timed);
109
+
110
+		return array_add($record, $this->morphType, $this->morphClass);
111
+	}
112
+
113
+	/**
114
+	 * Create a new query builder for the pivot table.
115
+	 *
116
+	 * @throws \InvalidArgumentException
117
+	 * @return \Illuminate\Database\Query\Builder
118
+	 */
119
+	protected function newPivotQuery()
120
+	{
121
+		$query = parent::newPivotQuery();
122
+
123
+		return $query->where($this->morphType, $this->morphClass);
124
+	}
125
+
126
+	/**
127
+	 * Create a new pivot model instance.
128
+	 *
129
+	 * @param  array $attributes
130
+	 * @param  bool  $exists
131
+	 * @return Pivot
132
+	 */
133
+	public function newPivot(array $attributes = [], $exists = false)
134
+	{
135
+		$pivot = new MorphPivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
136
+
137
+		$pivot->setPivotKeys($this->foreignKey, $this->otherKey)
138
+			->setMorphType($this->morphType)
139
+			->setMorphClass($this->morphClass);
140
+
141
+		return $pivot;
142
+	}
143
+
144
+	/**
145
+	 * Return Pivot attributes when available on a relationship
146
+	 *
147
+	 * @return array
148
+	 */
149
+	public function getPivotAttributes()
150
+	{
151
+		return $this->pivotColumns;
152
+	}
153
+
154
+	/**
155
+	 * Get the foreign key "type" name.
156
+	 *
157
+	 * @return string
158
+	 */
159
+	public function getMorphType()
160
+	{
161
+		return $this->morphType;
162
+	}
163
+
164
+	/**
165
+	 * Get the class name of the parent model.
166
+	 *
167
+	 * @return string
168
+	 */
169
+	public function getMorphClass()
170
+	{
171
+		return $this->morphClass;
172
+	}
173 173
 }
Please login to merge, or discard this patch.
src/Relationships/HasOne.php 1 patch
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -6,40 +6,40 @@
 block discarded – undo
6 6
 
7 7
 class HasOne extends HasOneOrMany
8 8
 {
9
-    /**
10
-     * Get the results of the relationship.
11
-     *
12
-     * @param $relation
13
-     * @return mixed
14
-     */
15
-    public function getResults($relation)
16
-    {
17
-        $result = $this->query->first();
9
+	/**
10
+	 * Get the results of the relationship.
11
+	 *
12
+	 * @param $relation
13
+	 * @return mixed
14
+	 */
15
+	public function getResults($relation)
16
+	{
17
+		$result = $this->query->first();
18 18
 
19
-        $this->cacheRelation($result, $relation);
19
+		$this->cacheRelation($result, $relation);
20 20
 
21
-        return $result;
22
-    }
21
+		return $result;
22
+	}
23 23
 
24
-    /**
25
-     * Get the results of the relationship.
26
-     *
27
-     * @return mixed
28
-     */
29
-    public function fetch()
30
-    {
31
-        return $this->query->first();
32
-    }
24
+	/**
25
+	 * Get the results of the relationship.
26
+	 *
27
+	 * @return mixed
28
+	 */
29
+	public function fetch()
30
+	{
31
+		return $this->query->first();
32
+	}
33 33
 
34
-    /**
35
-     * Match the eagerly loaded results to their parents.
36
-     *
37
-     * @param  array     $results
38
-     * @param  string    $relation
39
-     * @return array
40
-     */
41
-    public function match(array $results, $relation)
42
-    {
43
-        return $this->matchOne($results, $relation);
44
-    }
34
+	/**
35
+	 * Match the eagerly loaded results to their parents.
36
+	 *
37
+	 * @param  array     $results
38
+	 * @param  string    $relation
39
+	 * @return array
40
+	 */
41
+	public function match(array $results, $relation)
42
+	{
43
+		return $this->matchOne($results, $relation);
44
+	}
45 45
 }
Please login to merge, or discard this patch.
src/Relationships/HasOneOrMany.php 1 patch
Indentation   +296 added lines, -296 removed lines patch added patch discarded remove patch
@@ -7,303 +7,303 @@
 block discarded – undo
7 7
 
8 8
 abstract class HasOneOrMany extends Relationship
9 9
 {
10
-    /**
11
-     * The foreign key of the parent model.
12
-     *
13
-     * @var string
14
-     */
15
-    protected $foreignKey;
16
-
17
-    /**
18
-     * The local key of the parent model.
19
-     *
20
-     * @var string
21
-     */
22
-    protected $localKey;
23
-
24
-    /**
25
-     * Create a new has many relationship instance.
26
-     *
27
-     * @param Mapper                 $mapper
28
-     * @param \Analogue\ORM\Mappable $parentEntity
29
-     * @param string                 $foreignKey
30
-     * @param string                 $localKey
31
-     */
32
-    public function __construct(Mapper $mapper, $parentEntity, $foreignKey, $localKey)
33
-    {
34
-        $this->localKey = $localKey;
35
-        $this->foreignKey = $foreignKey;
36
-
37
-        parent::__construct($mapper, $parentEntity);
38
-    }
39
-
40
-    /**
41
-     * @param \Analogue\ORM\Entity $entity
42
-     */
43
-    public function attachOne($entity)
44
-    {
45
-        $wrapper = $this->factory->make($entity);
46
-
47
-        // Ok, we need to guess the inverse of the relation from there.
48
-        // Let's assume the inverse of the relation method is the name of
49
-        // the entity.
50
-
51
-        $wrapper->setEntityAttribute($this->getPlainForeignKey(), $this->getParentKey());
52
-    }
53
-
54
-    /**
55
-     * @param EntityCollection $entities
56
-     */
57
-    public function attachMany(EntityCollection $entities)
58
-    {
59
-        foreach ($entities as $entity) {
60
-            $this->attachOne($entity);
61
-        }
62
-    }
63
-
64
-    /**
65
-     * @param $entityHash
66
-     */
67
-    protected function detachOne($entityHash)
68
-    {
69
-        $this->detachMany([$entityHash]);
70
-    }
71
-
72
-    /**
73
-     * Attach ids that are passed as arguments, and detach any other
74
-     * @param  mixed $entities
75
-     * @throws \InvalidArgumentException
76
-     * @return void
77
-     */
78
-    public function sync(array $entities)
79
-    {
80
-        $this->detachExcept($entities);
81
-    }
82
-
83
-    /**
84
-     * @param  $entities
85
-     * @throws \InvalidArgumentException
86
-     */
87
-    protected function detachExcept($entities)
88
-    {
89
-        $query = $this->query->getQuery()->from($this->relatedMap->getTable());
90
-
91
-        if (count($entities) > 0) {
92
-            $keys = $this->getKeys($entities);
93
-            $query->whereNotIn($this->relatedMap->getKeyName(), $keys);
94
-        }
95
-
96
-        $parentKey = $this->parentMap->getKeyName();
97
-
98
-        $query->where($this->getPlainForeignKey(), '=', $this->parent->getEntityAttribute($parentKey))
99
-            ->update([$this->getPlainForeignKey() => null]);
100
-    }
101
-
102
-    /**
103
-     * @param array $entityHashes
104
-     */
105
-    public function detachMany(array $entityHashes)
106
-    {
107
-        $keys = [];
108
-
109
-        foreach ($entityHashes as $hash) {
110
-            $split = explode('.', $hash);
111
-            $keys[] = $split[1];
112
-        }
113
-
114
-        $query = $this->query->getQuery()->from($this->relatedMap->getTable());
115
-
116
-        $query->whereIn($this->relatedMap->getKeyName(), $keys)
117
-            ->update([$this->getPlainForeignKey() => null]);
118
-    }
119
-
120
-    /**
121
-     * Set the base constraints on the relation query.
122
-     *
123
-     * @return void
124
-     */
125
-    public function addConstraints()
126
-    {
127
-        if (static::$constraints) {
128
-            $this->query->where($this->foreignKey, '=', $this->getParentKey());
129
-        }
130
-    }
131
-
132
-    /**
133
-     * Set the constraints for an eager load of the relation.
134
-     *
135
-     * @param  array $results
136
-     * @return void
137
-     */
138
-    public function addEagerConstraints(array $results)
139
-    {
140
-        $this->query->whereIn($this->foreignKey, $this->getKeysFromResults($results, $this->localKey));
141
-    }
142
-
143
-    /**
144
-     * Match the eagerly loaded relationship to the current result set
145
-     *
146
-     * @param  array            $results
147
-     * @param  string           $relation
148
-     * @return array
149
-     */
150
-    public function matchOne(array $results, $relation)
151
-    {
152
-        return $this->matchOneOrMany($results, $relation, 'one');
153
-    }
154
-
155
-    /**
156
-     * Match the eagerly loaded results to their many parents.
157
-     *
158
-     * @param  array            $results
159
-     * @param  string           $relation
160
-     * @return array
161
-     */
162
-    public function matchMany(array $results, $relation)
163
-    {
164
-        return $this->matchOneOrMany($results, $relation, 'many');
165
-    }
166
-
167
-    /**
168
-     * Match the eagerly loaded results to their many parents.
169
-     *
170
-     * @param  array            $results
171
-     * @param  string           $relation
172
-     * @param  string           $type
173
-     * @return array
174
-     */
175
-    protected function matchOneOrMany(array $results, $relation, $type)
176
-    {
177
-        $entities = $this->getEager();
178
-
179
-        $dictionary = $this->buildDictionary($entities);
180
-
181
-        $cache = $this->parentMapper->getEntityCache();
182
-
183
-        $host = $this;
184
-
185
-        // Once we have the dictionary we can simply spin through the parent models to
186
-        // link them up with their children using the keyed dictionary to make the
187
-        // matching very convenient and easy work. Then we'll just return them.
188
-        return array_map(function($result) use ($dictionary, $cache, $type, $relation, $host) { 
10
+	/**
11
+	 * The foreign key of the parent model.
12
+	 *
13
+	 * @var string
14
+	 */
15
+	protected $foreignKey;
16
+
17
+	/**
18
+	 * The local key of the parent model.
19
+	 *
20
+	 * @var string
21
+	 */
22
+	protected $localKey;
23
+
24
+	/**
25
+	 * Create a new has many relationship instance.
26
+	 *
27
+	 * @param Mapper                 $mapper
28
+	 * @param \Analogue\ORM\Mappable $parentEntity
29
+	 * @param string                 $foreignKey
30
+	 * @param string                 $localKey
31
+	 */
32
+	public function __construct(Mapper $mapper, $parentEntity, $foreignKey, $localKey)
33
+	{
34
+		$this->localKey = $localKey;
35
+		$this->foreignKey = $foreignKey;
36
+
37
+		parent::__construct($mapper, $parentEntity);
38
+	}
39
+
40
+	/**
41
+	 * @param \Analogue\ORM\Entity $entity
42
+	 */
43
+	public function attachOne($entity)
44
+	{
45
+		$wrapper = $this->factory->make($entity);
46
+
47
+		// Ok, we need to guess the inverse of the relation from there.
48
+		// Let's assume the inverse of the relation method is the name of
49
+		// the entity.
50
+
51
+		$wrapper->setEntityAttribute($this->getPlainForeignKey(), $this->getParentKey());
52
+	}
53
+
54
+	/**
55
+	 * @param EntityCollection $entities
56
+	 */
57
+	public function attachMany(EntityCollection $entities)
58
+	{
59
+		foreach ($entities as $entity) {
60
+			$this->attachOne($entity);
61
+		}
62
+	}
63
+
64
+	/**
65
+	 * @param $entityHash
66
+	 */
67
+	protected function detachOne($entityHash)
68
+	{
69
+		$this->detachMany([$entityHash]);
70
+	}
71
+
72
+	/**
73
+	 * Attach ids that are passed as arguments, and detach any other
74
+	 * @param  mixed $entities
75
+	 * @throws \InvalidArgumentException
76
+	 * @return void
77
+	 */
78
+	public function sync(array $entities)
79
+	{
80
+		$this->detachExcept($entities);
81
+	}
82
+
83
+	/**
84
+	 * @param  $entities
85
+	 * @throws \InvalidArgumentException
86
+	 */
87
+	protected function detachExcept($entities)
88
+	{
89
+		$query = $this->query->getQuery()->from($this->relatedMap->getTable());
90
+
91
+		if (count($entities) > 0) {
92
+			$keys = $this->getKeys($entities);
93
+			$query->whereNotIn($this->relatedMap->getKeyName(), $keys);
94
+		}
95
+
96
+		$parentKey = $this->parentMap->getKeyName();
97
+
98
+		$query->where($this->getPlainForeignKey(), '=', $this->parent->getEntityAttribute($parentKey))
99
+			->update([$this->getPlainForeignKey() => null]);
100
+	}
101
+
102
+	/**
103
+	 * @param array $entityHashes
104
+	 */
105
+	public function detachMany(array $entityHashes)
106
+	{
107
+		$keys = [];
108
+
109
+		foreach ($entityHashes as $hash) {
110
+			$split = explode('.', $hash);
111
+			$keys[] = $split[1];
112
+		}
113
+
114
+		$query = $this->query->getQuery()->from($this->relatedMap->getTable());
115
+
116
+		$query->whereIn($this->relatedMap->getKeyName(), $keys)
117
+			->update([$this->getPlainForeignKey() => null]);
118
+	}
119
+
120
+	/**
121
+	 * Set the base constraints on the relation query.
122
+	 *
123
+	 * @return void
124
+	 */
125
+	public function addConstraints()
126
+	{
127
+		if (static::$constraints) {
128
+			$this->query->where($this->foreignKey, '=', $this->getParentKey());
129
+		}
130
+	}
131
+
132
+	/**
133
+	 * Set the constraints for an eager load of the relation.
134
+	 *
135
+	 * @param  array $results
136
+	 * @return void
137
+	 */
138
+	public function addEagerConstraints(array $results)
139
+	{
140
+		$this->query->whereIn($this->foreignKey, $this->getKeysFromResults($results, $this->localKey));
141
+	}
142
+
143
+	/**
144
+	 * Match the eagerly loaded relationship to the current result set
145
+	 *
146
+	 * @param  array            $results
147
+	 * @param  string           $relation
148
+	 * @return array
149
+	 */
150
+	public function matchOne(array $results, $relation)
151
+	{
152
+		return $this->matchOneOrMany($results, $relation, 'one');
153
+	}
154
+
155
+	/**
156
+	 * Match the eagerly loaded results to their many parents.
157
+	 *
158
+	 * @param  array            $results
159
+	 * @param  string           $relation
160
+	 * @return array
161
+	 */
162
+	public function matchMany(array $results, $relation)
163
+	{
164
+		return $this->matchOneOrMany($results, $relation, 'many');
165
+	}
166
+
167
+	/**
168
+	 * Match the eagerly loaded results to their many parents.
169
+	 *
170
+	 * @param  array            $results
171
+	 * @param  string           $relation
172
+	 * @param  string           $type
173
+	 * @return array
174
+	 */
175
+	protected function matchOneOrMany(array $results, $relation, $type)
176
+	{
177
+		$entities = $this->getEager();
178
+
179
+		$dictionary = $this->buildDictionary($entities);
180
+
181
+		$cache = $this->parentMapper->getEntityCache();
182
+
183
+		$host = $this;
184
+
185
+		// Once we have the dictionary we can simply spin through the parent models to
186
+		// link them up with their children using the keyed dictionary to make the
187
+		// matching very convenient and easy work. Then we'll just return them.
188
+		return array_map(function($result) use ($dictionary, $cache, $type, $relation, $host) { 
189 189
             
190
-            $key = $result[$host->localKey];
190
+			$key = $result[$host->localKey];
191 191
 
192
-            if (isset($dictionary[$key])) {
192
+			if (isset($dictionary[$key])) {
193 193
                 
194
-                $value = $host->getRelationValue($dictionary, $key, $type);
195
-
196
-                $result[$relation] = $value;
197
-
198
-                // TODO : Refactor This
199
-                $cache->cacheLoadedRelationResult($key, $relation, $value, $this);
200
-            }
201
-            else {
202
-                // TODO set an empty collection or entityCollection if type is many ? 
203
-                $result[$relation] = null;
204
-            }
205
-
206
-            return $result;
207
-
208
-        }, $results);
209
-    }
210
-
211
-    /**
212
-     * Get the value of a relationship by one or many type.
213
-     *
214
-     * @param  array  $dictionary
215
-     * @param  string $key
216
-     * @param  string $type
217
-     * @return mixed
218
-     */
219
-    protected function getRelationValue(array $dictionary, $key, $type)
220
-    {
221
-        $value = $dictionary[$key];
222
-
223
-        return $type == 'one' ? reset($value) : $this->relatedMap->newCollection($value);
224
-    }
225
-
226
-    /**
227
-     * Build model dictionary keyed by the relation's foreign key.
228
-     *
229
-     * @param  EntityCollection $results
230
-     * @return array
231
-     */
232
-    protected function buildDictionary(EntityCollection $results)
233
-    {
234
-        $dictionary = [];
235
-
236
-        $foreign = $this->getPlainForeignKey();
237
-
238
-        // First we will create a dictionary of models keyed by the foreign key of the
239
-        // relationship as this will allow us to quickly access all of the related
240
-        // models without having to do nested looping which will be quite slow.
241
-        foreach ($results as $result) {
242
-            $dictionary[$result->{$foreign}][] = $result;
243
-        }
244
-
245
-        return $dictionary;
246
-    }
247
-
248
-    /**
249
-     * Get the key for comparing against the parent key in "has" query.
250
-     *
251
-     * @return string
252
-     */
253
-    public function getHasCompareKey()
254
-    {
255
-        return $this->getForeignKey();
256
-    }
257
-
258
-    /**
259
-     * Get the foreign key for the relationship.
260
-     *
261
-     * @return string
262
-     */
263
-    public function getForeignKey()
264
-    {
265
-        return $this->foreignKey;
266
-    }
267
-
268
-    /**
269
-     * Get the plain foreign key.
270
-     *
271
-     * @return string
272
-     */
273
-    public function getPlainForeignKey()
274
-    {
275
-        $segments = explode('.', $this->getForeignKey());
276
-
277
-        return $segments[count($segments) - 1];
278
-    }
279
-
280
-    /**
281
-     * Get the key value of the parent's local key.
282
-     *
283
-     * @return mixed
284
-     */
285
-    public function getParentKey()
286
-    {
287
-        return $this->parent->getEntityAttribute($this->localKey);
288
-    }
289
-
290
-    /**
291
-     * Get the fully qualified parent key name.
292
-     *
293
-     * @return string
294
-     */
295
-    public function getQualifiedParentKeyName()
296
-    {
297
-        return $this->parentMap->getTable() . '.' . $this->localKey;
298
-    }
299
-
300
-    /**
301
-     * Get the foreign key as value pair for this relation
302
-     *
303
-     * @return array
304
-     */
305
-    public function getForeignKeyValuePair()
306
-    {
307
-        return [$this->getPlainForeignKey() => $this->getParentKey()];
308
-    }
194
+				$value = $host->getRelationValue($dictionary, $key, $type);
195
+
196
+				$result[$relation] = $value;
197
+
198
+				// TODO : Refactor This
199
+				$cache->cacheLoadedRelationResult($key, $relation, $value, $this);
200
+			}
201
+			else {
202
+				// TODO set an empty collection or entityCollection if type is many ? 
203
+				$result[$relation] = null;
204
+			}
205
+
206
+			return $result;
207
+
208
+		}, $results);
209
+	}
210
+
211
+	/**
212
+	 * Get the value of a relationship by one or many type.
213
+	 *
214
+	 * @param  array  $dictionary
215
+	 * @param  string $key
216
+	 * @param  string $type
217
+	 * @return mixed
218
+	 */
219
+	protected function getRelationValue(array $dictionary, $key, $type)
220
+	{
221
+		$value = $dictionary[$key];
222
+
223
+		return $type == 'one' ? reset($value) : $this->relatedMap->newCollection($value);
224
+	}
225
+
226
+	/**
227
+	 * Build model dictionary keyed by the relation's foreign key.
228
+	 *
229
+	 * @param  EntityCollection $results
230
+	 * @return array
231
+	 */
232
+	protected function buildDictionary(EntityCollection $results)
233
+	{
234
+		$dictionary = [];
235
+
236
+		$foreign = $this->getPlainForeignKey();
237
+
238
+		// First we will create a dictionary of models keyed by the foreign key of the
239
+		// relationship as this will allow us to quickly access all of the related
240
+		// models without having to do nested looping which will be quite slow.
241
+		foreach ($results as $result) {
242
+			$dictionary[$result->{$foreign}][] = $result;
243
+		}
244
+
245
+		return $dictionary;
246
+	}
247
+
248
+	/**
249
+	 * Get the key for comparing against the parent key in "has" query.
250
+	 *
251
+	 * @return string
252
+	 */
253
+	public function getHasCompareKey()
254
+	{
255
+		return $this->getForeignKey();
256
+	}
257
+
258
+	/**
259
+	 * Get the foreign key for the relationship.
260
+	 *
261
+	 * @return string
262
+	 */
263
+	public function getForeignKey()
264
+	{
265
+		return $this->foreignKey;
266
+	}
267
+
268
+	/**
269
+	 * Get the plain foreign key.
270
+	 *
271
+	 * @return string
272
+	 */
273
+	public function getPlainForeignKey()
274
+	{
275
+		$segments = explode('.', $this->getForeignKey());
276
+
277
+		return $segments[count($segments) - 1];
278
+	}
279
+
280
+	/**
281
+	 * Get the key value of the parent's local key.
282
+	 *
283
+	 * @return mixed
284
+	 */
285
+	public function getParentKey()
286
+	{
287
+		return $this->parent->getEntityAttribute($this->localKey);
288
+	}
289
+
290
+	/**
291
+	 * Get the fully qualified parent key name.
292
+	 *
293
+	 * @return string
294
+	 */
295
+	public function getQualifiedParentKeyName()
296
+	{
297
+		return $this->parentMap->getTable() . '.' . $this->localKey;
298
+	}
299
+
300
+	/**
301
+	 * Get the foreign key as value pair for this relation
302
+	 *
303
+	 * @return array
304
+	 */
305
+	public function getForeignKeyValuePair()
306
+	{
307
+		return [$this->getPlainForeignKey() => $this->getParentKey()];
308
+	}
309 309
 }
Please login to merge, or discard this patch.