Completed
Branch master (901ac8)
by Rémi
11:17
created
src/Relationships/Relationship.php 2 patches
Indentation   +448 added lines, -448 removed lines patch added patch discarded remove patch
@@ -19,452 +19,452 @@
 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
-     *
46
-     * @var \Analogue\ORM\EntityMap
47
-     */
48
-    protected $parentMap;
49
-
50
-    /**
51
-     * The Parent Mapper instance.
52
-     *
53
-     * @var Mapper
54
-     */
55
-    protected $parentMapper;
56
-
57
-    /**
58
-     * The related entity instance.
59
-     *
60
-     * @var object
61
-     */
62
-    protected $related;
63
-
64
-    /**
65
-     * The related entity Map.
66
-     *
67
-     * @var \Analogue\ORM\EntityMap
68
-     */
69
-    protected $relatedMap;
70
-
71
-    /**
72
-     * Indicate if the parent entity hold the key for the relation.
73
-     *
74
-     * @var bool
75
-     */
76
-    protected static $ownForeignKey = false;
77
-
78
-    /**
79
-     * Indicate if the relationships use a pivot table.*.
80
-     *
81
-     * @var bool
82
-     */
83
-    protected static $hasPivot = false;
84
-
85
-    /**
86
-     * Indicates if the relation is adding constraints.
87
-     *
88
-     * @var bool
89
-     */
90
-    protected static $constraints = true;
91
-
92
-    /**
93
-     * Wrapper factory.
94
-     *
95
-     * @var \Analogue\ORM\System\Wrappers\Factory
96
-     */
97
-    protected $factory;
98
-
99
-    /**
100
-     * Create a new relation instance.
101
-     *
102
-     * @param Mapper $mapper
103
-     * @param mixed  $parent
104
-     *
105
-     * @throws \Analogue\ORM\Exceptions\MappingException
106
-     */
107
-    public function __construct(Mapper $mapper, $parent)
108
-    {
109
-        $this->relatedMapper = $mapper;
110
-
111
-        $this->query = $mapper->getQuery();
112
-
113
-        $this->factory = new Factory();
114
-
115
-        $this->parent = $this->factory->make($parent);
116
-
117
-        $this->parentMapper = $mapper->getManager()->getMapper($parent);
118
-
119
-        $this->parentMap = $this->parentMapper->getEntityMap();
120
-
121
-        $this->related = $mapper->newInstance();
122
-
123
-        $this->relatedMap = $mapper->getEntityMap();
124
-
125
-        $this->addConstraints();
126
-    }
127
-
128
-    /**
129
-     * Indicate if the parent entity hold the foreign key for relation.
130
-     *
131
-     * @return bool
132
-     */
133
-    public function ownForeignKey()
134
-    {
135
-        return static::$ownForeignKey;
136
-    }
137
-
138
-    /**
139
-     * Indicate if the relationship uses a pivot table.
140
-     *
141
-     * @return bool
142
-     */
143
-    public function hasPivot()
144
-    {
145
-        return static::$hasPivot;
146
-    }
147
-
148
-    /**
149
-     * Set the base constraints on the relation query.
150
-     *
151
-     * @return void
152
-     */
153
-    abstract public function addConstraints();
154
-
155
-    /**
156
-     * Set the constraints for an eager load of the relation.
157
-     *
158
-     * @param array $results
159
-     *
160
-     * @return void
161
-     */
162
-    abstract public function addEagerConstraints(array $results);
163
-
164
-    /**
165
-     * Match the eagerly loaded results to their parents, then return
166
-     * updated results.
167
-     *
168
-     * @param array  $results
169
-     * @param string $relation
170
-     *
171
-     * @return array
172
-     */
173
-    abstract public function match(array $results, $relation);
174
-
175
-    /**
176
-     * Get the results of the relationship.
177
-     *
178
-     * @param string $relation relation name in parent's entity map
179
-     *
180
-     * @return mixed
181
-     */
182
-    abstract public function getResults($relation);
183
-
184
-    /**
185
-     * Get the relationship for eager loading.
186
-     *
187
-     * @return EntityCollection
188
-     */
189
-    public function getEager()
190
-    {
191
-        return $this->get();
192
-    }
193
-
194
-    /**
195
-     * Add the constraints for a relationship count query.
196
-     *
197
-     * @param Query $query
198
-     * @param Query $parent
199
-     *
200
-     * @return Query
201
-     */
202
-    public function getRelationCountQuery(Query $query, Query $parent)
203
-    {
204
-        $query->select(new Expression('count(*)'));
205
-
206
-        $key = $this->wrap($this->getQualifiedParentKeyName());
207
-
208
-        return $query->where($this->getHasCompareKey(), '=', new Expression($key));
209
-    }
210
-
211
-    /**
212
-     * Run a callback with constraints disabled on the relation.
213
-     *
214
-     * @param Closure $callback
215
-     *
216
-     * @return mixed
217
-     */
218
-    public static function noConstraints(Closure $callback)
219
-    {
220
-        static::$constraints = false;
221
-
222
-        // When resetting the relation where clause, we want to shift the first element
223
-        // off of the bindings, leaving only the constraints that the developers put
224
-        // as "extra" on the relationships, and not original relation constraints.
225
-        $results = call_user_func($callback);
226
-
227
-        static::$constraints = true;
228
-
229
-        return $results;
230
-    }
231
-
232
-    /**
233
-     * Get all of the primary keys for an array of entities.
234
-     *
235
-     * @param array  $entities
236
-     * @param string $key
237
-     *
238
-     * @return array
239
-     */
240
-    protected function getKeys(array $entities, $key = null)
241
-    {
242
-        if (is_null($key)) {
243
-            $key = $this->relatedMap->getKeyName();
244
-        }
245
-
246
-        $host = $this;
247
-
248
-        return array_unique(array_values(array_map(function ($value) use ($key, $host) {
249
-            if (!$value instanceof InternallyMappable) {
250
-                $value = $host->factory->make($value);
251
-            }
252
-
253
-            return $value->getEntityAttribute($key);
254
-        }, $entities)));
255
-    }
256
-
257
-    /**
258
-     * Get all the keys from a result set.
259
-     *
260
-     * @param array  $results
261
-     * @param string $key
262
-     *
263
-     * @return array
264
-     */
265
-    protected function getKeysFromResults(array $results, $key = null)
266
-    {
267
-        if (is_null($key)) {
268
-            $key = $this->relatedMap->getKeyName();
269
-        }
270
-
271
-        return array_unique(array_values(array_map(function ($value) use ($key) {
272
-            return $value[$key];
273
-        }, $results)));
274
-    }
275
-
276
-    /**
277
-     * Get the underlying query for the relation.
278
-     *
279
-     * @return Query
280
-     */
281
-    public function getQuery()
282
-    {
283
-        return $this->query;
284
-    }
285
-
286
-    /**
287
-     * Get the base query builder.
288
-     *
289
-     * @return \Analogue\ORM\Drivers\QueryAdapter
290
-     */
291
-    public function getBaseQuery()
292
-    {
293
-        return $this->query->getQuery();
294
-    }
295
-
296
-    /**
297
-     * Get the parent model of the relation.
298
-     *
299
-     * @return InternallyMappable
300
-     */
301
-    public function getParent()
302
-    {
303
-        return $this->parent;
304
-    }
305
-
306
-    /**
307
-     * Set the parent model of the relation.
308
-     *
309
-     * @param InternallyMappable $parent
310
-     *
311
-     * @return void
312
-     */
313
-    public function setParent(InternallyMappable $parent)
314
-    {
315
-        $this->parent = $parent;
316
-    }
317
-
318
-    /**
319
-     * Get the fully qualified parent key name.
320
-     *
321
-     * @return string
322
-     */
323
-    protected function getQualifiedParentKeyName()
324
-    {
325
-        return $this->parent->getQualifiedKeyName();
326
-    }
327
-
328
-    /**
329
-     * Get the related entity of the relation.
330
-     *
331
-     * @return \Analogue\ORM\Entity
332
-     */
333
-    public function getRelated()
334
-    {
335
-        return $this->related;
336
-    }
337
-
338
-    /**
339
-     * Get the related mapper for the relation.
340
-     *
341
-     * @return Mapper
342
-     */
343
-    public function getRelatedMapper()
344
-    {
345
-        return $this->relatedMapper;
346
-    }
347
-
348
-    /**
349
-     * Get the name of the "created at" column.
350
-     *
351
-     * @return string
352
-     */
353
-    public function createdAt()
354
-    {
355
-        return $this->parentMap->getCreatedAtColumn();
356
-    }
357
-
358
-    /**
359
-     * Get the name of the "updated at" column.
360
-     *
361
-     * @return string
362
-     */
363
-    public function updatedAt()
364
-    {
365
-        return $this->parentMap->getUpdatedAtColumn();
366
-    }
367
-
368
-    /**
369
-     * Get the name of the related model's "updated at" column.
370
-     *
371
-     * @return string
372
-     */
373
-    public function relatedUpdatedAt()
374
-    {
375
-        return $this->related->getUpdatedAtColumn();
376
-    }
377
-
378
-    /**
379
-     * Wrap the given value with the parent query's grammar.
380
-     *
381
-     * @param string $value
382
-     *
383
-     * @return string
384
-     */
385
-    public function wrap($value)
386
-    {
387
-        return $this->parentMapper->getQuery()->getQuery()->getGrammar()->wrap($value);
388
-    }
389
-
390
-    /**
391
-     * Get a fresh timestamp.
392
-     *
393
-     * @return Carbon
394
-     */
395
-    protected function freshTimestamp()
396
-    {
397
-        return new Carbon();
398
-    }
399
-
400
-    /**
401
-     * Cache the link between parent and related
402
-     * into the mapper's Entity Cache.
403
-     *
404
-     * @param EntityCollection|Mappable $results  result of the relation query
405
-     * @param string                    $relation name of the relation method on the parent entity
406
-     *
407
-     * @return void
408
-     */
409
-    protected function cacheRelation($results, $relation)
410
-    {
411
-        $cache = $this->parentMapper->getEntityCache();
412
-
413
-        $cache->cacheLoadedRelationResult($this->parent->getEntityKey(), $relation, $results, $this);
414
-    }
415
-
416
-    /**
417
-     * Return Pivot attributes when available on a relationship.
418
-     *
419
-     * @return array
420
-     */
421
-    public function getPivotAttributes()
422
-    {
423
-        return [];
424
-    }
425
-
426
-    /**
427
-     * Get a combo type.primaryKey.
428
-     *
429
-     * @param Mappable $entity
430
-     *
431
-     * @return string
432
-     */
433
-    protected function getEntityHash(Mappable $entity)
434
-    {
435
-        $class = get_class($entity);
436
-
437
-        $keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
438
-
439
-        return $class.'.'.$entity->getEntityAttribute($keyName);
440
-    }
441
-
442
-    /**
443
-     * Run synchronization content if needed by the
444
-     * relation type.
445
-     *
446
-     * @param array $actualContent
447
-     *
448
-     * @return void
449
-     */
450
-    abstract public function sync(array $actualContent);
451
-
452
-    /**
453
-     * Handle dynamic method calls to the relationship.
454
-     *
455
-     * @param string $method
456
-     * @param array  $parameters
457
-     *
458
-     * @return mixed
459
-     */
460
-    public function __call($method, $parameters)
461
-    {
462
-        $result = call_user_func_array([$this->query, $method], $parameters);
463
-
464
-        if ($result === $this->query) {
465
-            return $this;
466
-        }
467
-
468
-        return $result;
469
-    }
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
+	 *
46
+	 * @var \Analogue\ORM\EntityMap
47
+	 */
48
+	protected $parentMap;
49
+
50
+	/**
51
+	 * The Parent Mapper instance.
52
+	 *
53
+	 * @var Mapper
54
+	 */
55
+	protected $parentMapper;
56
+
57
+	/**
58
+	 * The related entity instance.
59
+	 *
60
+	 * @var object
61
+	 */
62
+	protected $related;
63
+
64
+	/**
65
+	 * The related entity Map.
66
+	 *
67
+	 * @var \Analogue\ORM\EntityMap
68
+	 */
69
+	protected $relatedMap;
70
+
71
+	/**
72
+	 * Indicate if the parent entity hold the key for the relation.
73
+	 *
74
+	 * @var bool
75
+	 */
76
+	protected static $ownForeignKey = false;
77
+
78
+	/**
79
+	 * Indicate if the relationships use a pivot table.*.
80
+	 *
81
+	 * @var bool
82
+	 */
83
+	protected static $hasPivot = false;
84
+
85
+	/**
86
+	 * Indicates if the relation is adding constraints.
87
+	 *
88
+	 * @var bool
89
+	 */
90
+	protected static $constraints = true;
91
+
92
+	/**
93
+	 * Wrapper factory.
94
+	 *
95
+	 * @var \Analogue\ORM\System\Wrappers\Factory
96
+	 */
97
+	protected $factory;
98
+
99
+	/**
100
+	 * Create a new relation instance.
101
+	 *
102
+	 * @param Mapper $mapper
103
+	 * @param mixed  $parent
104
+	 *
105
+	 * @throws \Analogue\ORM\Exceptions\MappingException
106
+	 */
107
+	public function __construct(Mapper $mapper, $parent)
108
+	{
109
+		$this->relatedMapper = $mapper;
110
+
111
+		$this->query = $mapper->getQuery();
112
+
113
+		$this->factory = new Factory();
114
+
115
+		$this->parent = $this->factory->make($parent);
116
+
117
+		$this->parentMapper = $mapper->getManager()->getMapper($parent);
118
+
119
+		$this->parentMap = $this->parentMapper->getEntityMap();
120
+
121
+		$this->related = $mapper->newInstance();
122
+
123
+		$this->relatedMap = $mapper->getEntityMap();
124
+
125
+		$this->addConstraints();
126
+	}
127
+
128
+	/**
129
+	 * Indicate if the parent entity hold the foreign key for relation.
130
+	 *
131
+	 * @return bool
132
+	 */
133
+	public function ownForeignKey()
134
+	{
135
+		return static::$ownForeignKey;
136
+	}
137
+
138
+	/**
139
+	 * Indicate if the relationship uses a pivot table.
140
+	 *
141
+	 * @return bool
142
+	 */
143
+	public function hasPivot()
144
+	{
145
+		return static::$hasPivot;
146
+	}
147
+
148
+	/**
149
+	 * Set the base constraints on the relation query.
150
+	 *
151
+	 * @return void
152
+	 */
153
+	abstract public function addConstraints();
154
+
155
+	/**
156
+	 * Set the constraints for an eager load of the relation.
157
+	 *
158
+	 * @param array $results
159
+	 *
160
+	 * @return void
161
+	 */
162
+	abstract public function addEagerConstraints(array $results);
163
+
164
+	/**
165
+	 * Match the eagerly loaded results to their parents, then return
166
+	 * updated results.
167
+	 *
168
+	 * @param array  $results
169
+	 * @param string $relation
170
+	 *
171
+	 * @return array
172
+	 */
173
+	abstract public function match(array $results, $relation);
174
+
175
+	/**
176
+	 * Get the results of the relationship.
177
+	 *
178
+	 * @param string $relation relation name in parent's entity map
179
+	 *
180
+	 * @return mixed
181
+	 */
182
+	abstract public function getResults($relation);
183
+
184
+	/**
185
+	 * Get the relationship for eager loading.
186
+	 *
187
+	 * @return EntityCollection
188
+	 */
189
+	public function getEager()
190
+	{
191
+		return $this->get();
192
+	}
193
+
194
+	/**
195
+	 * Add the constraints for a relationship count query.
196
+	 *
197
+	 * @param Query $query
198
+	 * @param Query $parent
199
+	 *
200
+	 * @return Query
201
+	 */
202
+	public function getRelationCountQuery(Query $query, Query $parent)
203
+	{
204
+		$query->select(new Expression('count(*)'));
205
+
206
+		$key = $this->wrap($this->getQualifiedParentKeyName());
207
+
208
+		return $query->where($this->getHasCompareKey(), '=', new Expression($key));
209
+	}
210
+
211
+	/**
212
+	 * Run a callback with constraints disabled on the relation.
213
+	 *
214
+	 * @param Closure $callback
215
+	 *
216
+	 * @return mixed
217
+	 */
218
+	public static function noConstraints(Closure $callback)
219
+	{
220
+		static::$constraints = false;
221
+
222
+		// When resetting the relation where clause, we want to shift the first element
223
+		// off of the bindings, leaving only the constraints that the developers put
224
+		// as "extra" on the relationships, and not original relation constraints.
225
+		$results = call_user_func($callback);
226
+
227
+		static::$constraints = true;
228
+
229
+		return $results;
230
+	}
231
+
232
+	/**
233
+	 * Get all of the primary keys for an array of entities.
234
+	 *
235
+	 * @param array  $entities
236
+	 * @param string $key
237
+	 *
238
+	 * @return array
239
+	 */
240
+	protected function getKeys(array $entities, $key = null)
241
+	{
242
+		if (is_null($key)) {
243
+			$key = $this->relatedMap->getKeyName();
244
+		}
245
+
246
+		$host = $this;
247
+
248
+		return array_unique(array_values(array_map(function ($value) use ($key, $host) {
249
+			if (!$value instanceof InternallyMappable) {
250
+				$value = $host->factory->make($value);
251
+			}
252
+
253
+			return $value->getEntityAttribute($key);
254
+		}, $entities)));
255
+	}
256
+
257
+	/**
258
+	 * Get all the keys from a result set.
259
+	 *
260
+	 * @param array  $results
261
+	 * @param string $key
262
+	 *
263
+	 * @return array
264
+	 */
265
+	protected function getKeysFromResults(array $results, $key = null)
266
+	{
267
+		if (is_null($key)) {
268
+			$key = $this->relatedMap->getKeyName();
269
+		}
270
+
271
+		return array_unique(array_values(array_map(function ($value) use ($key) {
272
+			return $value[$key];
273
+		}, $results)));
274
+	}
275
+
276
+	/**
277
+	 * Get the underlying query for the relation.
278
+	 *
279
+	 * @return Query
280
+	 */
281
+	public function getQuery()
282
+	{
283
+		return $this->query;
284
+	}
285
+
286
+	/**
287
+	 * Get the base query builder.
288
+	 *
289
+	 * @return \Analogue\ORM\Drivers\QueryAdapter
290
+	 */
291
+	public function getBaseQuery()
292
+	{
293
+		return $this->query->getQuery();
294
+	}
295
+
296
+	/**
297
+	 * Get the parent model of the relation.
298
+	 *
299
+	 * @return InternallyMappable
300
+	 */
301
+	public function getParent()
302
+	{
303
+		return $this->parent;
304
+	}
305
+
306
+	/**
307
+	 * Set the parent model of the relation.
308
+	 *
309
+	 * @param InternallyMappable $parent
310
+	 *
311
+	 * @return void
312
+	 */
313
+	public function setParent(InternallyMappable $parent)
314
+	{
315
+		$this->parent = $parent;
316
+	}
317
+
318
+	/**
319
+	 * Get the fully qualified parent key name.
320
+	 *
321
+	 * @return string
322
+	 */
323
+	protected function getQualifiedParentKeyName()
324
+	{
325
+		return $this->parent->getQualifiedKeyName();
326
+	}
327
+
328
+	/**
329
+	 * Get the related entity of the relation.
330
+	 *
331
+	 * @return \Analogue\ORM\Entity
332
+	 */
333
+	public function getRelated()
334
+	{
335
+		return $this->related;
336
+	}
337
+
338
+	/**
339
+	 * Get the related mapper for the relation.
340
+	 *
341
+	 * @return Mapper
342
+	 */
343
+	public function getRelatedMapper()
344
+	{
345
+		return $this->relatedMapper;
346
+	}
347
+
348
+	/**
349
+	 * Get the name of the "created at" column.
350
+	 *
351
+	 * @return string
352
+	 */
353
+	public function createdAt()
354
+	{
355
+		return $this->parentMap->getCreatedAtColumn();
356
+	}
357
+
358
+	/**
359
+	 * Get the name of the "updated at" column.
360
+	 *
361
+	 * @return string
362
+	 */
363
+	public function updatedAt()
364
+	{
365
+		return $this->parentMap->getUpdatedAtColumn();
366
+	}
367
+
368
+	/**
369
+	 * Get the name of the related model's "updated at" column.
370
+	 *
371
+	 * @return string
372
+	 */
373
+	public function relatedUpdatedAt()
374
+	{
375
+		return $this->related->getUpdatedAtColumn();
376
+	}
377
+
378
+	/**
379
+	 * Wrap the given value with the parent query's grammar.
380
+	 *
381
+	 * @param string $value
382
+	 *
383
+	 * @return string
384
+	 */
385
+	public function wrap($value)
386
+	{
387
+		return $this->parentMapper->getQuery()->getQuery()->getGrammar()->wrap($value);
388
+	}
389
+
390
+	/**
391
+	 * Get a fresh timestamp.
392
+	 *
393
+	 * @return Carbon
394
+	 */
395
+	protected function freshTimestamp()
396
+	{
397
+		return new Carbon();
398
+	}
399
+
400
+	/**
401
+	 * Cache the link between parent and related
402
+	 * into the mapper's Entity Cache.
403
+	 *
404
+	 * @param EntityCollection|Mappable $results  result of the relation query
405
+	 * @param string                    $relation name of the relation method on the parent entity
406
+	 *
407
+	 * @return void
408
+	 */
409
+	protected function cacheRelation($results, $relation)
410
+	{
411
+		$cache = $this->parentMapper->getEntityCache();
412
+
413
+		$cache->cacheLoadedRelationResult($this->parent->getEntityKey(), $relation, $results, $this);
414
+	}
415
+
416
+	/**
417
+	 * Return Pivot attributes when available on a relationship.
418
+	 *
419
+	 * @return array
420
+	 */
421
+	public function getPivotAttributes()
422
+	{
423
+		return [];
424
+	}
425
+
426
+	/**
427
+	 * Get a combo type.primaryKey.
428
+	 *
429
+	 * @param Mappable $entity
430
+	 *
431
+	 * @return string
432
+	 */
433
+	protected function getEntityHash(Mappable $entity)
434
+	{
435
+		$class = get_class($entity);
436
+
437
+		$keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
438
+
439
+		return $class.'.'.$entity->getEntityAttribute($keyName);
440
+	}
441
+
442
+	/**
443
+	 * Run synchronization content if needed by the
444
+	 * relation type.
445
+	 *
446
+	 * @param array $actualContent
447
+	 *
448
+	 * @return void
449
+	 */
450
+	abstract public function sync(array $actualContent);
451
+
452
+	/**
453
+	 * Handle dynamic method calls to the relationship.
454
+	 *
455
+	 * @param string $method
456
+	 * @param array  $parameters
457
+	 *
458
+	 * @return mixed
459
+	 */
460
+	public function __call($method, $parameters)
461
+	{
462
+		$result = call_user_func_array([$this->query, $method], $parameters);
463
+
464
+		if ($result === $this->query) {
465
+			return $this;
466
+		}
467
+
468
+		return $result;
469
+	}
470 470
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
 
246 246
         $host = $this;
247 247
 
248
-        return array_unique(array_values(array_map(function ($value) use ($key, $host) {
248
+        return array_unique(array_values(array_map(function($value) use ($key, $host) {
249 249
             if (!$value instanceof InternallyMappable) {
250 250
                 $value = $host->factory->make($value);
251 251
             }
@@ -268,7 +268,7 @@  discard block
 block discarded – undo
268 268
             $key = $this->relatedMap->getKeyName();
269 269
         }
270 270
 
271
-        return array_unique(array_values(array_map(function ($value) use ($key) {
271
+        return array_unique(array_values(array_map(function($value) use ($key) {
272 272
             return $value[$key];
273 273
         }, $results)));
274 274
     }
Please login to merge, or discard this patch.
src/Relationships/BelongsToMany.php 3 patches
Indentation   +925 added lines, -925 removed lines patch added patch discarded remove patch
@@ -12,929 +12,929 @@
 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 bool
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
-     *
90
-     * @return array
91
-     */
92
-    protected function getIdsFromHashes(array $hashes)
93
-    {
94
-        $ids = [];
95
-
96
-        foreach ($hashes as $hash) {
97
-            $split = explode('.', $hash);
98
-            $ids[] = $split[1];
99
-        }
100
-
101
-        return $ids;
102
-    }
103
-
104
-    /**
105
-     * Get the results of the relationship.
106
-     *
107
-     * @param $relation
108
-     *
109
-     * @return EntityCollection
110
-     */
111
-    public function getResults($relation)
112
-    {
113
-        $results = $this->get();
114
-
115
-        $this->cacheRelation($results, $relation);
116
-
117
-        return $results;
118
-    }
119
-
120
-    /**
121
-     * Set a where clause for a pivot table column.
122
-     *
123
-     * @param string $column
124
-     * @param string $operator
125
-     * @param mixed  $value
126
-     * @param string $boolean
127
-     *
128
-     * @return self
129
-     */
130
-    public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
131
-    {
132
-        return $this->where($this->table.'.'.$column, $operator, $value, $boolean);
133
-    }
134
-
135
-    /**
136
-     * Set an or where clause for a pivot table column.
137
-     *
138
-     * @param string $column
139
-     * @param string $operator
140
-     * @param mixed  $value
141
-     *
142
-     * @return self
143
-     */
144
-    public function orWherePivot($column, $operator = null, $value = null)
145
-    {
146
-        return $this->wherePivot($column, $operator, $value, 'or');
147
-    }
148
-
149
-    /**
150
-     * Return Pivot attributes when available on a relationship.
151
-     *
152
-     * @return array
153
-     */
154
-    public function getPivotAttributes()
155
-    {
156
-        return $this->pivotColumns;
157
-    }
158
-
159
-    /**
160
-     * Execute the query and get the first result.
161
-     *
162
-     * @param array $columns
163
-     *
164
-     * @return mixed
165
-     */
166
-    public function first($columns = ['*'])
167
-    {
168
-        $results = $this->take(1)->get($columns);
169
-
170
-        return count($results) > 0 ? $results->first() : null;
171
-    }
172
-
173
-    /**
174
-     * Execute the query and get the first result or throw an exception.
175
-     *
176
-     * @param array $columns
177
-     *
178
-     * @throws EntityNotFoundException
179
-     *
180
-     * @return Mappable|self
181
-     */
182
-    public function firstOrFail($columns = ['*'])
183
-    {
184
-        if (!is_null($entity = $this->first($columns))) {
185
-            return $entity;
186
-        }
187
-
188
-        throw new EntityNotFoundException();
189
-    }
190
-
191
-    /**
192
-     * Execute the query as a "select" statement.
193
-     *
194
-     * @param array $columns
195
-     *
196
-     * @return \Analogue\ORM\EntityCollection
197
-     */
198
-    public function get($columns = ['*'])
199
-    {
200
-        // First we'll add the proper select columns onto the query so it is run with
201
-        // the proper columns. Then, we will get the results and hydrate out pivot
202
-        // models with the result of those columns as a separate model relation.
203
-        $columns = $this->query->getQuery()->columns ? [] : $columns;
204
-
205
-        $select = $this->getSelectColumns($columns);
206
-
207
-        $entities = $this->query->addSelect($select)->getEntities();
208
-
209
-        $entities = $this->hydratePivotRelation($entities);
210
-
211
-        return $this->relatedMap->newCollection($entities);
212
-    }
213
-
214
-    /**
215
-     * Hydrate the pivot table relationship on the models.
216
-     *
217
-     * @param array $entities
218
-     *
219
-     * @return void
220
-     */
221
-    protected function hydratePivotRelation(array $entities)
222
-    {
223
-        // TODO (note) We should definitely get rid of the pivot in a next
224
-        // release, as this is not quite relevant in a datamapper context.
225
-        $host = $this;
226
-
227
-        return array_map(function ($entity) use ($host) {
228
-            $entityWrapper = $this->factory->make($entity);
229
-
230
-            $pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
231
-            $entityWrapper->setEntityAttribute('pivot', $pivot);
232
-
233
-            return $entityWrapper->getObject();
234
-        }, $entities);
235
-    }
236
-
237
-    /**
238
-     * Get the pivot attributes from a model.
239
-     *
240
-     * @param  $entity
241
-     *
242
-     * @return array
243
-     */
244
-    protected function cleanPivotAttributes(InternallyMappable $entity)
245
-    {
246
-        $values = [];
247
-
248
-        $attributes = $entity->getEntityAttributes();
249
-
250
-        foreach ($attributes as $key => $value) {
251
-            // To get the pivots attributes we will just take any of the attributes which
252
-            // begin with "pivot_" and add those to this arrays, as well as unsetting
253
-            // them from the parent's models since they exist in a different table.
254
-            if (strpos($key, 'pivot_') === 0) {
255
-                $values[substr($key, 6)] = $value;
256
-
257
-                unset($attributes[$key]);
258
-            }
259
-        }
260
-
261
-        // Rehydrate Entity with cleaned array.
262
-        $entity->setEntityAttributes($attributes);
263
-
264
-        return $values;
265
-    }
266
-
267
-    /**
268
-     * Set the base constraints on the relation query.
269
-     *
270
-     * @return void
271
-     */
272
-    public function addConstraints()
273
-    {
274
-        $this->setJoin();
275
-
276
-        if (static::$constraints) {
277
-            $this->setWhere();
278
-        }
279
-    }
280
-
281
-    /**
282
-     * Add the constraints for a relationship count query.
283
-     *
284
-     * @param Query $query
285
-     * @param Query $parent
286
-     *
287
-     * @return Query
288
-     */
289
-    public function getRelationCountQuery(Query $query, Query $parent)
290
-    {
291
-        if ($parent->getQuery()->from == $query->getQuery()->from) {
292
-            return $this->getRelationCountQueryForSelfJoin($query, $parent);
293
-        }
294
-
295
-        $this->setJoin($query);
296
-
297
-        return parent::getRelationCountQuery($query, $parent);
298
-    }
299
-
300
-    /**
301
-     * Add the constraints for a relationship count query on the same table.
302
-     *
303
-     * @param Query $query
304
-     * @param Query $parent
305
-     *
306
-     * @return Query
307
-     */
308
-    public function getRelationCountQueryForSelfJoin(Query $query, Query $parent)
309
-    {
310
-        $query->select(new Expression('count(*)'));
311
-
312
-        $tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
313
-
314
-        $query->from($this->table.' as '.$tablePrefix.$hash = $this->getRelationCountHash());
315
-
316
-        $key = $this->wrap($this->getQualifiedParentKeyName());
317
-
318
-        return $query->where($hash.'.'.$this->foreignKey, '=', new Expression($key));
319
-    }
320
-
321
-    /**
322
-     * Get a relationship join table hash.
323
-     *
324
-     * @return string
325
-     */
326
-    public function getRelationCountHash()
327
-    {
328
-        return 'self_'.md5(microtime(true));
329
-    }
330
-
331
-    /**
332
-     * Set the select clause for the relation query.
333
-     *
334
-     * @param array $columns
335
-     *
336
-     * @return \Analogue\ORM\Relationships\BelongsToMany
337
-     */
338
-    protected function getSelectColumns(array $columns = ['*'])
339
-    {
340
-        if ($columns == ['*']) {
341
-            $columns = [$this->relatedMap->getTable().'.*'];
342
-        }
343
-
344
-        return array_merge($columns, $this->getAliasedPivotColumns());
345
-    }
346
-
347
-    /**
348
-     * Get the pivot columns for the relation.
349
-     *
350
-     * @return array
351
-     */
352
-    protected function getAliasedPivotColumns()
353
-    {
354
-        $defaults = [$this->foreignKey, $this->otherKey];
355
-
356
-        // We need to alias all of the pivot columns with the "pivot_" prefix so we
357
-        // can easily extract them out of the models and put them into the pivot
358
-        // relationships when they are retrieved and hydrated into the models.
359
-        $columns = [];
360
-
361
-        foreach (array_merge($defaults, $this->pivotColumns) as $column) {
362
-            $columns[] = $this->table.'.'.$column.' as pivot_'.$column;
363
-        }
364
-
365
-        return array_unique($columns);
366
-    }
367
-
368
-    /**
369
-     * Set the join clause for the relation query.
370
-     *
371
-     * @param  \Analogue\ORM\Query|null
372
-     *
373
-     * @return $this
374
-     */
375
-    protected function setJoin($query = null)
376
-    {
377
-        $query = $query ?: $this->query;
378
-
379
-        // We need to join to the intermediate table on the related model's primary
380
-        // key column with the intermediate table's foreign key for the related
381
-        // model instance. Then we can set the "where" for the parent models.
382
-        $baseTable = $this->relatedMap->getTable();
383
-
384
-        $key = $baseTable.'.'.$this->relatedMap->getKeyName();
385
-
386
-        $query->join($this->table, $key, '=', $this->getOtherKey());
387
-
388
-        return $this;
389
-    }
390
-
391
-    /**
392
-     * Set the where clause for the relation query.
393
-     *
394
-     * @return $this
395
-     */
396
-    protected function setWhere()
397
-    {
398
-        $foreign = $this->getForeignKey();
399
-
400
-        $parentKey = $this->parentMap->getKeyName();
401
-
402
-        $this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey));
403
-
404
-        return $this;
405
-    }
406
-
407
-    /**
408
-     * Set the constraints for an eager load of the relation.
409
-     *
410
-     * @param array $results
411
-     *
412
-     * @return void
413
-     */
414
-    public function addEagerConstraints(array $results)
415
-    {
416
-        $this->query->whereIn($this->getForeignKey(), $this->getKeysFromResults($results));
417
-    }
418
-
419
-    /**
420
-     * Match Eagerly loaded relation to result.
421
-     *
422
-     * @param array  $results
423
-     * @param string $relation
424
-     *
425
-     * @return array
426
-     */
427
-    public function match(array $results, $relation)
428
-    {
429
-        $entities = $this->getEager();
430
-
431
-        // TODO; optimize this operation
432
-        $dictionary = $this->buildDictionary($entities);
433
-
434
-        $keyName = $this->relatedMap->getKeyName();
435
-
436
-        $cache = $this->parentMapper->getEntityCache();
437
-
438
-        $host = $this;
439
-
440
-        // Once we have an array dictionary of child objects we can easily match the
441
-        // children back to their parent using the dictionary and the keys on the
442
-        // the parent models. Then we will return the hydrated models back out.
443
-        return array_map(function ($result) use ($dictionary, $keyName, $cache, $relation, $host) {
444
-            if (isset($dictionary[$key = $result[$keyName]])) {
445
-                $collection = $host->relatedMap->newCollection($dictionary[$key]);
446
-
447
-                $result[$relation] = $collection;
448
-
449
-                // TODO Refactor this
450
-                $cache->cacheLoadedRelationResult($key, $relation, $collection, $this);
451
-            } else {
452
-                $result[$relation] = $host->relatedMap->newCollection();
453
-            }
454
-
455
-            return $result;
456
-        }, $results);
457
-    }
458
-
459
-    /**
460
-     * Build model dictionary keyed by the relation's foreign key.
461
-     *
462
-     * @param EntityCollection $results
463
-     *
464
-     * @return array
465
-     */
466
-    protected function buildDictionary(EntityCollection $results)
467
-    {
468
-        $foreign = $this->foreignKey;
469
-
470
-        // First we will build a dictionary of child models keyed by the foreign key
471
-        // of the relation so that we will easily and quickly match them to their
472
-        // parents without having a possibly slow inner loops for every models.
473
-        $dictionary = [];
474
-
475
-        foreach ($results as $entity) {
476
-            $wrapper = $this->factory->make($entity);
477
-            $dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity;
478
-        }
479
-
480
-        return $dictionary;
481
-    }
482
-
483
-    /**
484
-     * Get all of the IDs for the related models.
485
-     *
486
-     * @return array
487
-     */
488
-    public function getRelatedIds()
489
-    {
490
-        $fullKey = $this->relatedMap->getQualifiedKeyName();
491
-
492
-        return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName());
493
-    }
494
-
495
-    /**
496
-     * Update Pivot.
497
-     *
498
-     * @param \Analogue\ORM\Entity $entity
499
-     *
500
-     * @return void
501
-     */
502
-    public function updatePivot($entity)
503
-    {
504
-        $keyName = $this->relatedMap->getKeyName();
505
-
506
-        $this->updateExistingPivot(
507
-            $entity->getEntityAttribute($keyName),
508
-            $entity->getEntityAttribute('pivot')->getEntityAttributes()
509
-        );
510
-    }
511
-
512
-    /**
513
-     * Update Multiple pivot.
514
-     *
515
-     * @param  $relatedEntities
516
-     *
517
-     * @return void
518
-     */
519
-    public function updatePivots($relatedEntities)
520
-    {
521
-        foreach ($relatedEntities as $entity) {
522
-            $this->updatePivot($entity);
523
-        }
524
-    }
525
-
526
-    /**
527
-     * Create Pivot Records.
528
-     *
529
-     * @param \Analogue\ORM\Entity[] $relatedEntities
530
-     *
531
-     * @return void
532
-     */
533
-    public function createPivots($relatedEntities)
534
-    {
535
-        $keys = [];
536
-        $attributes = [];
537
-
538
-        $keyName = $this->relatedMap->getKeyName();
539
-
540
-        foreach ($relatedEntities as $entity) {
541
-            $keys[] = $entity->getEntityAttribute($keyName);
542
-        }
543
-
544
-        $records = $this->createAttachRecords($keys, $attributes);
545
-
546
-        $this->query->getQuery()->from($this->table)->insert($records);
547
-    }
548
-
549
-    /**
550
-     * Update an existing pivot record on the table.
551
-     *
552
-     * @param mixed $id
553
-     * @param array $attributes
554
-     *
555
-     * @throws \InvalidArgumentException
556
-     *
557
-     * @return int
558
-     */
559
-    public function updateExistingPivot($id, array $attributes)
560
-    {
561
-        if (in_array($this->updatedAt(), $this->pivotColumns)) {
562
-            $attributes = $this->setTimestampsOnAttach($attributes, true);
563
-        }
564
-
565
-        return $this->newPivotStatementForId($id)->update($attributes);
566
-    }
567
-
568
-    /**
569
-     * Attach a model to the parent.
570
-     *
571
-     * @param mixed $id
572
-     * @param array $attributes
573
-     *
574
-     * @return void
575
-     */
576
-    public function attach($id, array $attributes = [])
577
-    {
578
-        $query = $this->newPivotStatement();
579
-
580
-        $query->insert($this->createAttachRecords((array) $id, $attributes));
581
-    }
582
-
583
-    /**
584
-     * @param array $entities
585
-     *
586
-     * @throws \InvalidArgumentException
587
-     */
588
-    public function sync(array $entities)
589
-    {
590
-        $this->detachExcept($entities);
591
-    }
592
-
593
-    /**
594
-     * Detach related entities that are not in $id.
595
-     *
596
-     * @param array $entities
597
-     *
598
-     * @throws \InvalidArgumentException
599
-     *
600
-     * @return void
601
-     */
602
-    protected function detachExcept(array $entities = [])
603
-    {
604
-        $query = $this->newPivotQuery();
605
-
606
-        if (count($entities) > 0) {
607
-            $keys = $this->getKeys($entities);
608
-
609
-            $query->whereNotIn($this->otherKey, $keys);
610
-        }
611
-        $parentKey = $this->parentMap->getKeyName();
612
-
613
-        $query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey));
614
-
615
-        $query->delete();
616
-
617
-        $query = $this->newPivotQuery();
618
-    }
619
-
620
-    /**
621
-     * Create an array of records to insert into the pivot table.
622
-     *
623
-     * @param array $ids
624
-     * @param array $attributes
625
-     *
626
-     * @return array
627
-     */
628
-    protected function createAttachRecords($ids, array $attributes)
629
-    {
630
-        $records = [];
631
-
632
-        $timed = in_array($this->createdAt(), $this->pivotColumns);
633
-
634
-        // To create the attachment records, we will simply spin through the IDs given
635
-        // and create a new record to insert for each ID. Each ID may actually be a
636
-        // key in the array, with extra attributes to be placed in other columns.
637
-        foreach ($ids as $key => $value) {
638
-            $records[] = $this->attacher($key, $value, $attributes, $timed);
639
-        }
640
-
641
-        return $records;
642
-    }
643
-
644
-    /**
645
-     * Create a full attachment record payload.
646
-     *
647
-     * @param int   $key
648
-     * @param mixed $value
649
-     * @param array $attributes
650
-     * @param bool  $timed
651
-     *
652
-     * @return array
653
-     */
654
-    protected function attacher($key, $value, $attributes, $timed)
655
-    {
656
-        list($id, $extra) = $this->getAttachId($key, $value, $attributes);
657
-
658
-        // To create the attachment records, we will simply spin through the IDs given
659
-        // and create a new record to insert for each ID. Each ID may actually be a
660
-        // key in the array, with extra attributes to be placed in other columns.
661
-        $record = $this->createAttachRecord($id, $timed);
662
-
663
-        return array_merge($record, $extra);
664
-    }
665
-
666
-    /**
667
-     * Get the attach record ID and extra attributes.
668
-     *
669
-     * @param int   $key
670
-     * @param mixed $value
671
-     * @param array $attributes
672
-     *
673
-     * @return array
674
-     */
675
-    protected function getAttachId($key, $value, array $attributes)
676
-    {
677
-        if (is_array($value)) {
678
-            return [$key, array_merge($value, $attributes)];
679
-        }
680
-
681
-        return [$value, $attributes];
682
-    }
683
-
684
-    /**
685
-     * Create a new pivot attachment record.
686
-     *
687
-     * @param int  $id
688
-     * @param bool $timed
689
-     *
690
-     * @return array
691
-     */
692
-    protected function createAttachRecord($id, $timed)
693
-    {
694
-        $parentKey = $this->parentMap->getKeyName();
695
-
696
-        $record = [];
697
-
698
-        $record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey);
699
-
700
-        $record[$this->otherKey] = $id;
701
-
702
-        // If the record needs to have creation and update timestamps, we will make
703
-        // them by calling the parent model's "freshTimestamp" method which will
704
-        // provide us with a fresh timestamp in this model's preferred format.
705
-        if ($timed) {
706
-            $record = $this->setTimestampsOnAttach($record);
707
-        }
708
-
709
-        return $record;
710
-    }
711
-
712
-    /**
713
-     * Set the creation and update timestamps on an attach record.
714
-     *
715
-     * @param array $record
716
-     * @param bool  $exists
717
-     *
718
-     * @return array
719
-     */
720
-    protected function setTimestampsOnAttach(array $record, $exists = false)
721
-    {
722
-        $fresh = $this->freshTimestamp();
723
-
724
-        if (!$exists) {
725
-            $record[$this->createdAt()] = $fresh;
726
-        }
727
-
728
-        $record[$this->updatedAt()] = $fresh;
729
-
730
-        return $record;
731
-    }
732
-
733
-    /**
734
-     * @param EntityCollection $entities
735
-     *
736
-     * @return array
737
-     */
738
-    protected function getModelKeysFromCollection(EntityCollection $entities)
739
-    {
740
-        $keyName = $this->relatedMap->getKeyName();
741
-
742
-        return array_map(function ($m) use ($keyName) {
743
-            return $m->$keyName;
744
-        }, $entities);
745
-    }
746
-
747
-    /**
748
-     * Detach models from the relationship.
749
-     *
750
-     * @param int|array $ids
751
-     *
752
-     * @throws \InvalidArgumentException
753
-     *
754
-     * @return int
755
-     */
756
-    public function detach($ids = [])
757
-    {
758
-        if ($ids instanceof EntityCollection) {
759
-            $ids = (array) $ids->modelKeys();
760
-        }
761
-
762
-        $query = $this->newPivotQuery();
763
-
764
-        // If associated IDs were passed to the method we will only delete those
765
-        // associations, otherwise all of the association ties will be broken.
766
-        // We'll return the numbers of affected rows when we do the deletes.
767
-        $ids = (array) $ids;
768
-
769
-        if (count($ids) > 0) {
770
-            $query->whereIn($this->otherKey, (array) $ids);
771
-        }
772
-
773
-        // Once we have all of the conditions set on the statement, we are ready
774
-        // to run the delete on the pivot table. Then, if the touch parameter
775
-        // is true, we will go ahead and touch all related models to sync.
776
-        return $query->delete();
777
-    }
778
-
779
-    /**
780
-     * Create a new query builder for the pivot table.
781
-     *
782
-     * @throws \InvalidArgumentException
783
-     *
784
-     * @return \Illuminate\Database\Query\Builder
785
-     */
786
-    protected function newPivotQuery()
787
-    {
788
-        $query = $this->newPivotStatement();
789
-
790
-        $parentKey = $this->parentMap->getKeyName();
791
-
792
-        return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey));
793
-    }
794
-
795
-    /**
796
-     * Get a new plain query builder for the pivot table.
797
-     *
798
-     * @return \Illuminate\Database\Query\Builder
799
-     */
800
-    public function newPivotStatement()
801
-    {
802
-        return $this->query->getQuery()->newQuery()->from($this->table);
803
-    }
804
-
805
-    /**
806
-     * Get a new pivot statement for a given "other" ID.
807
-     *
808
-     * @param mixed $id
809
-     *
810
-     * @throws \InvalidArgumentException
811
-     *
812
-     * @return \Illuminate\Database\Query\Builder
813
-     */
814
-    public function newPivotStatementForId($id)
815
-    {
816
-        $pivot = $this->newPivotStatement();
817
-
818
-        $parentKeyName = $this->parentMap->getKeyName();
819
-
820
-        $key = $this->parent->getEntityAttribute($parentKeyName);
821
-
822
-        return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id);
823
-    }
824
-
825
-    /**
826
-     * Create a new pivot model instance.
827
-     *
828
-     * @param array $attributes
829
-     * @param bool  $exists
830
-     *
831
-     * @return \Analogue\ORM\Relationships\Pivot
832
-     */
833
-    public function newPivot(array $attributes = [], $exists = false)
834
-    {
835
-        $pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
836
-
837
-        return $pivot->setPivotKeys($this->foreignKey, $this->otherKey);
838
-    }
839
-
840
-    /**
841
-     * Create a new existing pivot model instance.
842
-     *
843
-     * @param array $attributes
844
-     *
845
-     * @return \Analogue\ORM\Relationships\Pivot
846
-     */
847
-    public function newExistingPivot(array $attributes = [])
848
-    {
849
-        return $this->newPivot($attributes, true);
850
-    }
851
-
852
-    /**
853
-     * Set the columns on the pivot table to retrieve.
854
-     *
855
-     * @param array $columns
856
-     *
857
-     * @return $this
858
-     */
859
-    public function withPivot($columns)
860
-    {
861
-        $columns = is_array($columns) ? $columns : func_get_args();
862
-
863
-        $this->pivotColumns = array_merge($this->pivotColumns, $columns);
864
-
865
-        return $this;
866
-    }
867
-
868
-    /**
869
-     * Specify that the pivot table has creation and update timestamps.
870
-     *
871
-     * @param mixed $createdAt
872
-     * @param mixed $updatedAt
873
-     *
874
-     * @return \Analogue\ORM\Relationships\BelongsToMany
875
-     */
876
-    public function withTimestamps($createdAt = null, $updatedAt = null)
877
-    {
878
-        return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt());
879
-    }
880
-
881
-    /**
882
-     * Get the key for comparing against the parent key in "has" query.
883
-     *
884
-     * @return string
885
-     */
886
-    public function getHasCompareKey()
887
-    {
888
-        return $this->getForeignKey();
889
-    }
890
-
891
-    /**
892
-     * Get the fully qualified foreign key for the relation.
893
-     *
894
-     * @return string
895
-     */
896
-    public function getForeignKey()
897
-    {
898
-        return $this->table.'.'.$this->foreignKey;
899
-    }
900
-
901
-    /**
902
-     * Get the fully qualified "other key" for the relation.
903
-     *
904
-     * @return string
905
-     */
906
-    public function getOtherKey()
907
-    {
908
-        return $this->table.'.'.$this->otherKey;
909
-    }
910
-
911
-    /**
912
-     * Get the fully qualified parent key name.
913
-     *
914
-     * @return string
915
-     */
916
-    protected function getQualifiedParentKeyName()
917
-    {
918
-        return $this->parentMap->getQualifiedKeyName();
919
-    }
920
-
921
-    /**
922
-     * Get the intermediate table for the relationship.
923
-     *
924
-     * @return string
925
-     */
926
-    public function getTable()
927
-    {
928
-        return $this->table;
929
-    }
930
-
931
-    /**
932
-     * Get the relationship name for the relationship.
933
-     *
934
-     * @return string
935
-     */
936
-    public function getRelationName()
937
-    {
938
-        return $this->relationName;
939
-    }
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 bool
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
+	 *
90
+	 * @return array
91
+	 */
92
+	protected function getIdsFromHashes(array $hashes)
93
+	{
94
+		$ids = [];
95
+
96
+		foreach ($hashes as $hash) {
97
+			$split = explode('.', $hash);
98
+			$ids[] = $split[1];
99
+		}
100
+
101
+		return $ids;
102
+	}
103
+
104
+	/**
105
+	 * Get the results of the relationship.
106
+	 *
107
+	 * @param $relation
108
+	 *
109
+	 * @return EntityCollection
110
+	 */
111
+	public function getResults($relation)
112
+	{
113
+		$results = $this->get();
114
+
115
+		$this->cacheRelation($results, $relation);
116
+
117
+		return $results;
118
+	}
119
+
120
+	/**
121
+	 * Set a where clause for a pivot table column.
122
+	 *
123
+	 * @param string $column
124
+	 * @param string $operator
125
+	 * @param mixed  $value
126
+	 * @param string $boolean
127
+	 *
128
+	 * @return self
129
+	 */
130
+	public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
131
+	{
132
+		return $this->where($this->table.'.'.$column, $operator, $value, $boolean);
133
+	}
134
+
135
+	/**
136
+	 * Set an or where clause for a pivot table column.
137
+	 *
138
+	 * @param string $column
139
+	 * @param string $operator
140
+	 * @param mixed  $value
141
+	 *
142
+	 * @return self
143
+	 */
144
+	public function orWherePivot($column, $operator = null, $value = null)
145
+	{
146
+		return $this->wherePivot($column, $operator, $value, 'or');
147
+	}
148
+
149
+	/**
150
+	 * Return Pivot attributes when available on a relationship.
151
+	 *
152
+	 * @return array
153
+	 */
154
+	public function getPivotAttributes()
155
+	{
156
+		return $this->pivotColumns;
157
+	}
158
+
159
+	/**
160
+	 * Execute the query and get the first result.
161
+	 *
162
+	 * @param array $columns
163
+	 *
164
+	 * @return mixed
165
+	 */
166
+	public function first($columns = ['*'])
167
+	{
168
+		$results = $this->take(1)->get($columns);
169
+
170
+		return count($results) > 0 ? $results->first() : null;
171
+	}
172
+
173
+	/**
174
+	 * Execute the query and get the first result or throw an exception.
175
+	 *
176
+	 * @param array $columns
177
+	 *
178
+	 * @throws EntityNotFoundException
179
+	 *
180
+	 * @return Mappable|self
181
+	 */
182
+	public function firstOrFail($columns = ['*'])
183
+	{
184
+		if (!is_null($entity = $this->first($columns))) {
185
+			return $entity;
186
+		}
187
+
188
+		throw new EntityNotFoundException();
189
+	}
190
+
191
+	/**
192
+	 * Execute the query as a "select" statement.
193
+	 *
194
+	 * @param array $columns
195
+	 *
196
+	 * @return \Analogue\ORM\EntityCollection
197
+	 */
198
+	public function get($columns = ['*'])
199
+	{
200
+		// First we'll add the proper select columns onto the query so it is run with
201
+		// the proper columns. Then, we will get the results and hydrate out pivot
202
+		// models with the result of those columns as a separate model relation.
203
+		$columns = $this->query->getQuery()->columns ? [] : $columns;
204
+
205
+		$select = $this->getSelectColumns($columns);
206
+
207
+		$entities = $this->query->addSelect($select)->getEntities();
208
+
209
+		$entities = $this->hydratePivotRelation($entities);
210
+
211
+		return $this->relatedMap->newCollection($entities);
212
+	}
213
+
214
+	/**
215
+	 * Hydrate the pivot table relationship on the models.
216
+	 *
217
+	 * @param array $entities
218
+	 *
219
+	 * @return void
220
+	 */
221
+	protected function hydratePivotRelation(array $entities)
222
+	{
223
+		// TODO (note) We should definitely get rid of the pivot in a next
224
+		// release, as this is not quite relevant in a datamapper context.
225
+		$host = $this;
226
+
227
+		return array_map(function ($entity) use ($host) {
228
+			$entityWrapper = $this->factory->make($entity);
229
+
230
+			$pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
231
+			$entityWrapper->setEntityAttribute('pivot', $pivot);
232
+
233
+			return $entityWrapper->getObject();
234
+		}, $entities);
235
+	}
236
+
237
+	/**
238
+	 * Get the pivot attributes from a model.
239
+	 *
240
+	 * @param  $entity
241
+	 *
242
+	 * @return array
243
+	 */
244
+	protected function cleanPivotAttributes(InternallyMappable $entity)
245
+	{
246
+		$values = [];
247
+
248
+		$attributes = $entity->getEntityAttributes();
249
+
250
+		foreach ($attributes as $key => $value) {
251
+			// To get the pivots attributes we will just take any of the attributes which
252
+			// begin with "pivot_" and add those to this arrays, as well as unsetting
253
+			// them from the parent's models since they exist in a different table.
254
+			if (strpos($key, 'pivot_') === 0) {
255
+				$values[substr($key, 6)] = $value;
256
+
257
+				unset($attributes[$key]);
258
+			}
259
+		}
260
+
261
+		// Rehydrate Entity with cleaned array.
262
+		$entity->setEntityAttributes($attributes);
263
+
264
+		return $values;
265
+	}
266
+
267
+	/**
268
+	 * Set the base constraints on the relation query.
269
+	 *
270
+	 * @return void
271
+	 */
272
+	public function addConstraints()
273
+	{
274
+		$this->setJoin();
275
+
276
+		if (static::$constraints) {
277
+			$this->setWhere();
278
+		}
279
+	}
280
+
281
+	/**
282
+	 * Add the constraints for a relationship count query.
283
+	 *
284
+	 * @param Query $query
285
+	 * @param Query $parent
286
+	 *
287
+	 * @return Query
288
+	 */
289
+	public function getRelationCountQuery(Query $query, Query $parent)
290
+	{
291
+		if ($parent->getQuery()->from == $query->getQuery()->from) {
292
+			return $this->getRelationCountQueryForSelfJoin($query, $parent);
293
+		}
294
+
295
+		$this->setJoin($query);
296
+
297
+		return parent::getRelationCountQuery($query, $parent);
298
+	}
299
+
300
+	/**
301
+	 * Add the constraints for a relationship count query on the same table.
302
+	 *
303
+	 * @param Query $query
304
+	 * @param Query $parent
305
+	 *
306
+	 * @return Query
307
+	 */
308
+	public function getRelationCountQueryForSelfJoin(Query $query, Query $parent)
309
+	{
310
+		$query->select(new Expression('count(*)'));
311
+
312
+		$tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
313
+
314
+		$query->from($this->table.' as '.$tablePrefix.$hash = $this->getRelationCountHash());
315
+
316
+		$key = $this->wrap($this->getQualifiedParentKeyName());
317
+
318
+		return $query->where($hash.'.'.$this->foreignKey, '=', new Expression($key));
319
+	}
320
+
321
+	/**
322
+	 * Get a relationship join table hash.
323
+	 *
324
+	 * @return string
325
+	 */
326
+	public function getRelationCountHash()
327
+	{
328
+		return 'self_'.md5(microtime(true));
329
+	}
330
+
331
+	/**
332
+	 * Set the select clause for the relation query.
333
+	 *
334
+	 * @param array $columns
335
+	 *
336
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
337
+	 */
338
+	protected function getSelectColumns(array $columns = ['*'])
339
+	{
340
+		if ($columns == ['*']) {
341
+			$columns = [$this->relatedMap->getTable().'.*'];
342
+		}
343
+
344
+		return array_merge($columns, $this->getAliasedPivotColumns());
345
+	}
346
+
347
+	/**
348
+	 * Get the pivot columns for the relation.
349
+	 *
350
+	 * @return array
351
+	 */
352
+	protected function getAliasedPivotColumns()
353
+	{
354
+		$defaults = [$this->foreignKey, $this->otherKey];
355
+
356
+		// We need to alias all of the pivot columns with the "pivot_" prefix so we
357
+		// can easily extract them out of the models and put them into the pivot
358
+		// relationships when they are retrieved and hydrated into the models.
359
+		$columns = [];
360
+
361
+		foreach (array_merge($defaults, $this->pivotColumns) as $column) {
362
+			$columns[] = $this->table.'.'.$column.' as pivot_'.$column;
363
+		}
364
+
365
+		return array_unique($columns);
366
+	}
367
+
368
+	/**
369
+	 * Set the join clause for the relation query.
370
+	 *
371
+	 * @param  \Analogue\ORM\Query|null
372
+	 *
373
+	 * @return $this
374
+	 */
375
+	protected function setJoin($query = null)
376
+	{
377
+		$query = $query ?: $this->query;
378
+
379
+		// We need to join to the intermediate table on the related model's primary
380
+		// key column with the intermediate table's foreign key for the related
381
+		// model instance. Then we can set the "where" for the parent models.
382
+		$baseTable = $this->relatedMap->getTable();
383
+
384
+		$key = $baseTable.'.'.$this->relatedMap->getKeyName();
385
+
386
+		$query->join($this->table, $key, '=', $this->getOtherKey());
387
+
388
+		return $this;
389
+	}
390
+
391
+	/**
392
+	 * Set the where clause for the relation query.
393
+	 *
394
+	 * @return $this
395
+	 */
396
+	protected function setWhere()
397
+	{
398
+		$foreign = $this->getForeignKey();
399
+
400
+		$parentKey = $this->parentMap->getKeyName();
401
+
402
+		$this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey));
403
+
404
+		return $this;
405
+	}
406
+
407
+	/**
408
+	 * Set the constraints for an eager load of the relation.
409
+	 *
410
+	 * @param array $results
411
+	 *
412
+	 * @return void
413
+	 */
414
+	public function addEagerConstraints(array $results)
415
+	{
416
+		$this->query->whereIn($this->getForeignKey(), $this->getKeysFromResults($results));
417
+	}
418
+
419
+	/**
420
+	 * Match Eagerly loaded relation to result.
421
+	 *
422
+	 * @param array  $results
423
+	 * @param string $relation
424
+	 *
425
+	 * @return array
426
+	 */
427
+	public function match(array $results, $relation)
428
+	{
429
+		$entities = $this->getEager();
430
+
431
+		// TODO; optimize this operation
432
+		$dictionary = $this->buildDictionary($entities);
433
+
434
+		$keyName = $this->relatedMap->getKeyName();
435
+
436
+		$cache = $this->parentMapper->getEntityCache();
437
+
438
+		$host = $this;
439
+
440
+		// Once we have an array dictionary of child objects we can easily match the
441
+		// children back to their parent using the dictionary and the keys on the
442
+		// the parent models. Then we will return the hydrated models back out.
443
+		return array_map(function ($result) use ($dictionary, $keyName, $cache, $relation, $host) {
444
+			if (isset($dictionary[$key = $result[$keyName]])) {
445
+				$collection = $host->relatedMap->newCollection($dictionary[$key]);
446
+
447
+				$result[$relation] = $collection;
448
+
449
+				// TODO Refactor this
450
+				$cache->cacheLoadedRelationResult($key, $relation, $collection, $this);
451
+			} else {
452
+				$result[$relation] = $host->relatedMap->newCollection();
453
+			}
454
+
455
+			return $result;
456
+		}, $results);
457
+	}
458
+
459
+	/**
460
+	 * Build model dictionary keyed by the relation's foreign key.
461
+	 *
462
+	 * @param EntityCollection $results
463
+	 *
464
+	 * @return array
465
+	 */
466
+	protected function buildDictionary(EntityCollection $results)
467
+	{
468
+		$foreign = $this->foreignKey;
469
+
470
+		// First we will build a dictionary of child models keyed by the foreign key
471
+		// of the relation so that we will easily and quickly match them to their
472
+		// parents without having a possibly slow inner loops for every models.
473
+		$dictionary = [];
474
+
475
+		foreach ($results as $entity) {
476
+			$wrapper = $this->factory->make($entity);
477
+			$dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity;
478
+		}
479
+
480
+		return $dictionary;
481
+	}
482
+
483
+	/**
484
+	 * Get all of the IDs for the related models.
485
+	 *
486
+	 * @return array
487
+	 */
488
+	public function getRelatedIds()
489
+	{
490
+		$fullKey = $this->relatedMap->getQualifiedKeyName();
491
+
492
+		return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName());
493
+	}
494
+
495
+	/**
496
+	 * Update Pivot.
497
+	 *
498
+	 * @param \Analogue\ORM\Entity $entity
499
+	 *
500
+	 * @return void
501
+	 */
502
+	public function updatePivot($entity)
503
+	{
504
+		$keyName = $this->relatedMap->getKeyName();
505
+
506
+		$this->updateExistingPivot(
507
+			$entity->getEntityAttribute($keyName),
508
+			$entity->getEntityAttribute('pivot')->getEntityAttributes()
509
+		);
510
+	}
511
+
512
+	/**
513
+	 * Update Multiple pivot.
514
+	 *
515
+	 * @param  $relatedEntities
516
+	 *
517
+	 * @return void
518
+	 */
519
+	public function updatePivots($relatedEntities)
520
+	{
521
+		foreach ($relatedEntities as $entity) {
522
+			$this->updatePivot($entity);
523
+		}
524
+	}
525
+
526
+	/**
527
+	 * Create Pivot Records.
528
+	 *
529
+	 * @param \Analogue\ORM\Entity[] $relatedEntities
530
+	 *
531
+	 * @return void
532
+	 */
533
+	public function createPivots($relatedEntities)
534
+	{
535
+		$keys = [];
536
+		$attributes = [];
537
+
538
+		$keyName = $this->relatedMap->getKeyName();
539
+
540
+		foreach ($relatedEntities as $entity) {
541
+			$keys[] = $entity->getEntityAttribute($keyName);
542
+		}
543
+
544
+		$records = $this->createAttachRecords($keys, $attributes);
545
+
546
+		$this->query->getQuery()->from($this->table)->insert($records);
547
+	}
548
+
549
+	/**
550
+	 * Update an existing pivot record on the table.
551
+	 *
552
+	 * @param mixed $id
553
+	 * @param array $attributes
554
+	 *
555
+	 * @throws \InvalidArgumentException
556
+	 *
557
+	 * @return int
558
+	 */
559
+	public function updateExistingPivot($id, array $attributes)
560
+	{
561
+		if (in_array($this->updatedAt(), $this->pivotColumns)) {
562
+			$attributes = $this->setTimestampsOnAttach($attributes, true);
563
+		}
564
+
565
+		return $this->newPivotStatementForId($id)->update($attributes);
566
+	}
567
+
568
+	/**
569
+	 * Attach a model to the parent.
570
+	 *
571
+	 * @param mixed $id
572
+	 * @param array $attributes
573
+	 *
574
+	 * @return void
575
+	 */
576
+	public function attach($id, array $attributes = [])
577
+	{
578
+		$query = $this->newPivotStatement();
579
+
580
+		$query->insert($this->createAttachRecords((array) $id, $attributes));
581
+	}
582
+
583
+	/**
584
+	 * @param array $entities
585
+	 *
586
+	 * @throws \InvalidArgumentException
587
+	 */
588
+	public function sync(array $entities)
589
+	{
590
+		$this->detachExcept($entities);
591
+	}
592
+
593
+	/**
594
+	 * Detach related entities that are not in $id.
595
+	 *
596
+	 * @param array $entities
597
+	 *
598
+	 * @throws \InvalidArgumentException
599
+	 *
600
+	 * @return void
601
+	 */
602
+	protected function detachExcept(array $entities = [])
603
+	{
604
+		$query = $this->newPivotQuery();
605
+
606
+		if (count($entities) > 0) {
607
+			$keys = $this->getKeys($entities);
608
+
609
+			$query->whereNotIn($this->otherKey, $keys);
610
+		}
611
+		$parentKey = $this->parentMap->getKeyName();
612
+
613
+		$query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey));
614
+
615
+		$query->delete();
616
+
617
+		$query = $this->newPivotQuery();
618
+	}
619
+
620
+	/**
621
+	 * Create an array of records to insert into the pivot table.
622
+	 *
623
+	 * @param array $ids
624
+	 * @param array $attributes
625
+	 *
626
+	 * @return array
627
+	 */
628
+	protected function createAttachRecords($ids, array $attributes)
629
+	{
630
+		$records = [];
631
+
632
+		$timed = in_array($this->createdAt(), $this->pivotColumns);
633
+
634
+		// To create the attachment records, we will simply spin through the IDs given
635
+		// and create a new record to insert for each ID. Each ID may actually be a
636
+		// key in the array, with extra attributes to be placed in other columns.
637
+		foreach ($ids as $key => $value) {
638
+			$records[] = $this->attacher($key, $value, $attributes, $timed);
639
+		}
640
+
641
+		return $records;
642
+	}
643
+
644
+	/**
645
+	 * Create a full attachment record payload.
646
+	 *
647
+	 * @param int   $key
648
+	 * @param mixed $value
649
+	 * @param array $attributes
650
+	 * @param bool  $timed
651
+	 *
652
+	 * @return array
653
+	 */
654
+	protected function attacher($key, $value, $attributes, $timed)
655
+	{
656
+		list($id, $extra) = $this->getAttachId($key, $value, $attributes);
657
+
658
+		// To create the attachment records, we will simply spin through the IDs given
659
+		// and create a new record to insert for each ID. Each ID may actually be a
660
+		// key in the array, with extra attributes to be placed in other columns.
661
+		$record = $this->createAttachRecord($id, $timed);
662
+
663
+		return array_merge($record, $extra);
664
+	}
665
+
666
+	/**
667
+	 * Get the attach record ID and extra attributes.
668
+	 *
669
+	 * @param int   $key
670
+	 * @param mixed $value
671
+	 * @param array $attributes
672
+	 *
673
+	 * @return array
674
+	 */
675
+	protected function getAttachId($key, $value, array $attributes)
676
+	{
677
+		if (is_array($value)) {
678
+			return [$key, array_merge($value, $attributes)];
679
+		}
680
+
681
+		return [$value, $attributes];
682
+	}
683
+
684
+	/**
685
+	 * Create a new pivot attachment record.
686
+	 *
687
+	 * @param int  $id
688
+	 * @param bool $timed
689
+	 *
690
+	 * @return array
691
+	 */
692
+	protected function createAttachRecord($id, $timed)
693
+	{
694
+		$parentKey = $this->parentMap->getKeyName();
695
+
696
+		$record = [];
697
+
698
+		$record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey);
699
+
700
+		$record[$this->otherKey] = $id;
701
+
702
+		// If the record needs to have creation and update timestamps, we will make
703
+		// them by calling the parent model's "freshTimestamp" method which will
704
+		// provide us with a fresh timestamp in this model's preferred format.
705
+		if ($timed) {
706
+			$record = $this->setTimestampsOnAttach($record);
707
+		}
708
+
709
+		return $record;
710
+	}
711
+
712
+	/**
713
+	 * Set the creation and update timestamps on an attach record.
714
+	 *
715
+	 * @param array $record
716
+	 * @param bool  $exists
717
+	 *
718
+	 * @return array
719
+	 */
720
+	protected function setTimestampsOnAttach(array $record, $exists = false)
721
+	{
722
+		$fresh = $this->freshTimestamp();
723
+
724
+		if (!$exists) {
725
+			$record[$this->createdAt()] = $fresh;
726
+		}
727
+
728
+		$record[$this->updatedAt()] = $fresh;
729
+
730
+		return $record;
731
+	}
732
+
733
+	/**
734
+	 * @param EntityCollection $entities
735
+	 *
736
+	 * @return array
737
+	 */
738
+	protected function getModelKeysFromCollection(EntityCollection $entities)
739
+	{
740
+		$keyName = $this->relatedMap->getKeyName();
741
+
742
+		return array_map(function ($m) use ($keyName) {
743
+			return $m->$keyName;
744
+		}, $entities);
745
+	}
746
+
747
+	/**
748
+	 * Detach models from the relationship.
749
+	 *
750
+	 * @param int|array $ids
751
+	 *
752
+	 * @throws \InvalidArgumentException
753
+	 *
754
+	 * @return int
755
+	 */
756
+	public function detach($ids = [])
757
+	{
758
+		if ($ids instanceof EntityCollection) {
759
+			$ids = (array) $ids->modelKeys();
760
+		}
761
+
762
+		$query = $this->newPivotQuery();
763
+
764
+		// If associated IDs were passed to the method we will only delete those
765
+		// associations, otherwise all of the association ties will be broken.
766
+		// We'll return the numbers of affected rows when we do the deletes.
767
+		$ids = (array) $ids;
768
+
769
+		if (count($ids) > 0) {
770
+			$query->whereIn($this->otherKey, (array) $ids);
771
+		}
772
+
773
+		// Once we have all of the conditions set on the statement, we are ready
774
+		// to run the delete on the pivot table. Then, if the touch parameter
775
+		// is true, we will go ahead and touch all related models to sync.
776
+		return $query->delete();
777
+	}
778
+
779
+	/**
780
+	 * Create a new query builder for the pivot table.
781
+	 *
782
+	 * @throws \InvalidArgumentException
783
+	 *
784
+	 * @return \Illuminate\Database\Query\Builder
785
+	 */
786
+	protected function newPivotQuery()
787
+	{
788
+		$query = $this->newPivotStatement();
789
+
790
+		$parentKey = $this->parentMap->getKeyName();
791
+
792
+		return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey));
793
+	}
794
+
795
+	/**
796
+	 * Get a new plain query builder for the pivot table.
797
+	 *
798
+	 * @return \Illuminate\Database\Query\Builder
799
+	 */
800
+	public function newPivotStatement()
801
+	{
802
+		return $this->query->getQuery()->newQuery()->from($this->table);
803
+	}
804
+
805
+	/**
806
+	 * Get a new pivot statement for a given "other" ID.
807
+	 *
808
+	 * @param mixed $id
809
+	 *
810
+	 * @throws \InvalidArgumentException
811
+	 *
812
+	 * @return \Illuminate\Database\Query\Builder
813
+	 */
814
+	public function newPivotStatementForId($id)
815
+	{
816
+		$pivot = $this->newPivotStatement();
817
+
818
+		$parentKeyName = $this->parentMap->getKeyName();
819
+
820
+		$key = $this->parent->getEntityAttribute($parentKeyName);
821
+
822
+		return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id);
823
+	}
824
+
825
+	/**
826
+	 * Create a new pivot model instance.
827
+	 *
828
+	 * @param array $attributes
829
+	 * @param bool  $exists
830
+	 *
831
+	 * @return \Analogue\ORM\Relationships\Pivot
832
+	 */
833
+	public function newPivot(array $attributes = [], $exists = false)
834
+	{
835
+		$pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
836
+
837
+		return $pivot->setPivotKeys($this->foreignKey, $this->otherKey);
838
+	}
839
+
840
+	/**
841
+	 * Create a new existing pivot model instance.
842
+	 *
843
+	 * @param array $attributes
844
+	 *
845
+	 * @return \Analogue\ORM\Relationships\Pivot
846
+	 */
847
+	public function newExistingPivot(array $attributes = [])
848
+	{
849
+		return $this->newPivot($attributes, true);
850
+	}
851
+
852
+	/**
853
+	 * Set the columns on the pivot table to retrieve.
854
+	 *
855
+	 * @param array $columns
856
+	 *
857
+	 * @return $this
858
+	 */
859
+	public function withPivot($columns)
860
+	{
861
+		$columns = is_array($columns) ? $columns : func_get_args();
862
+
863
+		$this->pivotColumns = array_merge($this->pivotColumns, $columns);
864
+
865
+		return $this;
866
+	}
867
+
868
+	/**
869
+	 * Specify that the pivot table has creation and update timestamps.
870
+	 *
871
+	 * @param mixed $createdAt
872
+	 * @param mixed $updatedAt
873
+	 *
874
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
875
+	 */
876
+	public function withTimestamps($createdAt = null, $updatedAt = null)
877
+	{
878
+		return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt());
879
+	}
880
+
881
+	/**
882
+	 * Get the key for comparing against the parent key in "has" query.
883
+	 *
884
+	 * @return string
885
+	 */
886
+	public function getHasCompareKey()
887
+	{
888
+		return $this->getForeignKey();
889
+	}
890
+
891
+	/**
892
+	 * Get the fully qualified foreign key for the relation.
893
+	 *
894
+	 * @return string
895
+	 */
896
+	public function getForeignKey()
897
+	{
898
+		return $this->table.'.'.$this->foreignKey;
899
+	}
900
+
901
+	/**
902
+	 * Get the fully qualified "other key" for the relation.
903
+	 *
904
+	 * @return string
905
+	 */
906
+	public function getOtherKey()
907
+	{
908
+		return $this->table.'.'.$this->otherKey;
909
+	}
910
+
911
+	/**
912
+	 * Get the fully qualified parent key name.
913
+	 *
914
+	 * @return string
915
+	 */
916
+	protected function getQualifiedParentKeyName()
917
+	{
918
+		return $this->parentMap->getQualifiedKeyName();
919
+	}
920
+
921
+	/**
922
+	 * Get the intermediate table for the relationship.
923
+	 *
924
+	 * @return string
925
+	 */
926
+	public function getTable()
927
+	{
928
+		return $this->table;
929
+	}
930
+
931
+	/**
932
+	 * Get the relationship name for the relationship.
933
+	 *
934
+	 * @return string
935
+	 */
936
+	public function getRelationName()
937
+	{
938
+		return $this->relationName;
939
+	}
940 940
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -224,7 +224,7 @@  discard block
 block discarded – undo
224 224
         // release, as this is not quite relevant in a datamapper context.
225 225
         $host = $this;
226 226
 
227
-        return array_map(function ($entity) use ($host) {
227
+        return array_map(function($entity) use ($host) {
228 228
             $entityWrapper = $this->factory->make($entity);
229 229
 
230 230
             $pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
@@ -440,7 +440,7 @@  discard block
 block discarded – undo
440 440
         // Once we have an array dictionary of child objects we can easily match the
441 441
         // children back to their parent using the dictionary and the keys on the
442 442
         // the parent models. Then we will return the hydrated models back out.
443
-        return array_map(function ($result) use ($dictionary, $keyName, $cache, $relation, $host) {
443
+        return array_map(function($result) use ($dictionary, $keyName, $cache, $relation, $host) {
444 444
             if (isset($dictionary[$key = $result[$keyName]])) {
445 445
                 $collection = $host->relatedMap->newCollection($dictionary[$key]);
446 446
 
@@ -739,7 +739,7 @@  discard block
 block discarded – undo
739 739
     {
740 740
         $keyName = $this->relatedMap->getKeyName();
741 741
 
742
-        return array_map(function ($m) use ($keyName) {
742
+        return array_map(function($m) use ($keyName) {
743 743
             return $m->$keyName;
744 744
         }, $entities);
745 745
     }
Please login to merge, or discard this patch.
Doc Comments   +1 added lines patch added patch discarded remove patch
@@ -369,6 +369,7 @@
 block discarded – undo
369 369
      * Set the join clause for the relation query.
370 370
      *
371 371
      * @param  \Analogue\ORM\Query|null
372
+     * @param Query $query
372 373
      *
373 374
      * @return $this
374 375
      */
Please login to merge, or discard this patch.
src/Relationships/MorphToMany.php 1 patch
Indentation   +168 added lines, -168 removed lines patch added patch discarded remove patch
@@ -7,172 +7,172 @@
 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;
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
-     *
78
-     * @return Query
79
-     */
80
-    public function getRelationCountQuery(Query $query, Query $parent)
81
-    {
82
-        $query = parent::getRelationCountQuery($query, $parent);
83
-
84
-        return $query->where($this->table.'.'.$this->morphType, $this->morphClass);
85
-    }
86
-
87
-    /**
88
-     * Set the constraints for an eager load of the relation.
89
-     *
90
-     * @param array $results
91
-     *
92
-     * @return void
93
-     */
94
-    public function addEagerConstraints(array $results)
95
-    {
96
-        parent::addEagerConstraints($results);
97
-
98
-        $this->query->where($this->table.'.'.$this->morphType, $this->morphClass);
99
-    }
100
-
101
-    /**
102
-     * Create a new pivot attachment record.
103
-     *
104
-     * @param int  $id
105
-     * @param bool $timed
106
-     *
107
-     * @return array
108
-     */
109
-    protected function createAttachRecord($id, $timed)
110
-    {
111
-        $record = parent::createAttachRecord($id, $timed);
112
-
113
-        return array_add($record, $this->morphType, $this->morphClass);
114
-    }
115
-
116
-    /**
117
-     * Create a new query builder for the pivot table.
118
-     *
119
-     * @throws \InvalidArgumentException
120
-     *
121
-     * @return \Illuminate\Database\Query\Builder
122
-     */
123
-    protected function newPivotQuery()
124
-    {
125
-        $query = parent::newPivotQuery();
126
-
127
-        return $query->where($this->morphType, $this->morphClass);
128
-    }
129
-
130
-    /**
131
-     * Create a new pivot model instance.
132
-     *
133
-     * @param array $attributes
134
-     * @param bool  $exists
135
-     *
136
-     * @return Pivot
137
-     */
138
-    public function newPivot(array $attributes = [], $exists = false)
139
-    {
140
-        $pivot = new MorphPivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
141
-
142
-        $pivot->setPivotKeys($this->foreignKey, $this->otherKey)
143
-            ->setMorphType($this->morphType)
144
-            ->setMorphClass($this->morphClass);
145
-
146
-        return $pivot;
147
-    }
148
-
149
-    /**
150
-     * Return Pivot attributes when available on a relationship.
151
-     *
152
-     * @return array
153
-     */
154
-    public function getPivotAttributes()
155
-    {
156
-        return $this->pivotColumns;
157
-    }
158
-
159
-    /**
160
-     * Get the foreign key "type" name.
161
-     *
162
-     * @return string
163
-     */
164
-    public function getMorphType()
165
-    {
166
-        return $this->morphType;
167
-    }
168
-
169
-    /**
170
-     * Get the class name of the parent model.
171
-     *
172
-     * @return string
173
-     */
174
-    public function getMorphClass()
175
-    {
176
-        return $this->morphClass;
177
-    }
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
+
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
+	 *
78
+	 * @return Query
79
+	 */
80
+	public function getRelationCountQuery(Query $query, Query $parent)
81
+	{
82
+		$query = parent::getRelationCountQuery($query, $parent);
83
+
84
+		return $query->where($this->table.'.'.$this->morphType, $this->morphClass);
85
+	}
86
+
87
+	/**
88
+	 * Set the constraints for an eager load of the relation.
89
+	 *
90
+	 * @param array $results
91
+	 *
92
+	 * @return void
93
+	 */
94
+	public function addEagerConstraints(array $results)
95
+	{
96
+		parent::addEagerConstraints($results);
97
+
98
+		$this->query->where($this->table.'.'.$this->morphType, $this->morphClass);
99
+	}
100
+
101
+	/**
102
+	 * Create a new pivot attachment record.
103
+	 *
104
+	 * @param int  $id
105
+	 * @param bool $timed
106
+	 *
107
+	 * @return array
108
+	 */
109
+	protected function createAttachRecord($id, $timed)
110
+	{
111
+		$record = parent::createAttachRecord($id, $timed);
112
+
113
+		return array_add($record, $this->morphType, $this->morphClass);
114
+	}
115
+
116
+	/**
117
+	 * Create a new query builder for the pivot table.
118
+	 *
119
+	 * @throws \InvalidArgumentException
120
+	 *
121
+	 * @return \Illuminate\Database\Query\Builder
122
+	 */
123
+	protected function newPivotQuery()
124
+	{
125
+		$query = parent::newPivotQuery();
126
+
127
+		return $query->where($this->morphType, $this->morphClass);
128
+	}
129
+
130
+	/**
131
+	 * Create a new pivot model instance.
132
+	 *
133
+	 * @param array $attributes
134
+	 * @param bool  $exists
135
+	 *
136
+	 * @return Pivot
137
+	 */
138
+	public function newPivot(array $attributes = [], $exists = false)
139
+	{
140
+		$pivot = new MorphPivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
141
+
142
+		$pivot->setPivotKeys($this->foreignKey, $this->otherKey)
143
+			->setMorphType($this->morphType)
144
+			->setMorphClass($this->morphClass);
145
+
146
+		return $pivot;
147
+	}
148
+
149
+	/**
150
+	 * Return Pivot attributes when available on a relationship.
151
+	 *
152
+	 * @return array
153
+	 */
154
+	public function getPivotAttributes()
155
+	{
156
+		return $this->pivotColumns;
157
+	}
158
+
159
+	/**
160
+	 * Get the foreign key "type" name.
161
+	 *
162
+	 * @return string
163
+	 */
164
+	public function getMorphType()
165
+	{
166
+		return $this->morphType;
167
+	}
168
+
169
+	/**
170
+	 * Get the class name of the parent model.
171
+	 *
172
+	 * @return string
173
+	 */
174
+	public function getMorphClass()
175
+	{
176
+		return $this->morphClass;
177
+	}
178 178
 }
Please login to merge, or discard this patch.
src/Relationships/HasOne.php 1 patch
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -4,42 +4,42 @@
 block discarded – undo
4 4
 
5 5
 class HasOne extends HasOneOrMany
6 6
 {
7
-    /**
8
-     * Get the results of the relationship.
9
-     *
10
-     * @param $relation
11
-     *
12
-     * @return mixed
13
-     */
14
-    public function getResults($relation)
15
-    {
16
-        $result = $this->query->first();
7
+	/**
8
+	 * Get the results of the relationship.
9
+	 *
10
+	 * @param $relation
11
+	 *
12
+	 * @return mixed
13
+	 */
14
+	public function getResults($relation)
15
+	{
16
+		$result = $this->query->first();
17 17
 
18
-        $this->cacheRelation($result, $relation);
18
+		$this->cacheRelation($result, $relation);
19 19
 
20
-        return $result;
21
-    }
20
+		return $result;
21
+	}
22 22
 
23
-    /**
24
-     * Get the results of the relationship.
25
-     *
26
-     * @return mixed
27
-     */
28
-    public function fetch()
29
-    {
30
-        return $this->query->first();
31
-    }
23
+	/**
24
+	 * Get the results of the relationship.
25
+	 *
26
+	 * @return mixed
27
+	 */
28
+	public function fetch()
29
+	{
30
+		return $this->query->first();
31
+	}
32 32
 
33
-    /**
34
-     * Match the eagerly loaded results to their parents.
35
-     *
36
-     * @param array  $results
37
-     * @param string $relation
38
-     *
39
-     * @return array
40
-     */
41
-    public function match(array $results, $relation)
42
-    {
43
-        return $this->matchOne($results, $relation);
44
-    }
33
+	/**
34
+	 * Match the eagerly loaded results to their parents.
35
+	 *
36
+	 * @param array  $results
37
+	 * @param string $relation
38
+	 *
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 2 patches
Indentation   +305 added lines, -305 removed lines patch added patch discarded remove patch
@@ -7,309 +7,309 @@
 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
-     *
75
-     * @param mixed $entities
76
-     *
77
-     * @throws \InvalidArgumentException
78
-     *
79
-     * @return void
80
-     */
81
-    public function sync(array $entities)
82
-    {
83
-        $this->detachExcept($entities);
84
-    }
85
-
86
-    /**
87
-     * @param  $entities
88
-     *
89
-     * @throws \InvalidArgumentException
90
-     */
91
-    protected function detachExcept($entities)
92
-    {
93
-        $query = $this->query->getQuery()->from($this->relatedMap->getTable());
94
-
95
-        if (count($entities) > 0) {
96
-            $keys = $this->getKeys($entities);
97
-            $query->whereNotIn($this->relatedMap->getKeyName(), $keys);
98
-        }
99
-
100
-        $parentKey = $this->parentMap->getKeyName();
101
-
102
-        $query->where($this->getPlainForeignKey(), '=', $this->parent->getEntityAttribute($parentKey))
103
-            ->update([$this->getPlainForeignKey() => null]);
104
-    }
105
-
106
-    /**
107
-     * @param array $entityHashes
108
-     */
109
-    public function detachMany(array $entityHashes)
110
-    {
111
-        $keys = [];
112
-
113
-        foreach ($entityHashes as $hash) {
114
-            $split = explode('.', $hash);
115
-            $keys[] = $split[1];
116
-        }
117
-
118
-        $query = $this->query->getQuery()->from($this->relatedMap->getTable());
119
-
120
-        $query->whereIn($this->relatedMap->getKeyName(), $keys)
121
-            ->update([$this->getPlainForeignKey() => null]);
122
-    }
123
-
124
-    /**
125
-     * Set the base constraints on the relation query.
126
-     *
127
-     * @return void
128
-     */
129
-    public function addConstraints()
130
-    {
131
-        if (static::$constraints) {
132
-            $this->query->where($this->foreignKey, '=', $this->getParentKey());
133
-        }
134
-    }
135
-
136
-    /**
137
-     * Set the constraints for an eager load of the relation.
138
-     *
139
-     * @param array $results
140
-     *
141
-     * @return void
142
-     */
143
-    public function addEagerConstraints(array $results)
144
-    {
145
-        $this->query->whereIn($this->foreignKey, $this->getKeysFromResults($results, $this->localKey));
146
-    }
147
-
148
-    /**
149
-     * Match the eagerly loaded relationship to the current result set.
150
-     *
151
-     * @param array  $results
152
-     * @param string $relation
153
-     *
154
-     * @return array
155
-     */
156
-    public function matchOne(array $results, $relation)
157
-    {
158
-        return $this->matchOneOrMany($results, $relation, 'one');
159
-    }
160
-
161
-    /**
162
-     * Match the eagerly loaded results to their many parents.
163
-     *
164
-     * @param array  $results
165
-     * @param string $relation
166
-     *
167
-     * @return array
168
-     */
169
-    public function matchMany(array $results, $relation)
170
-    {
171
-        return $this->matchOneOrMany($results, $relation, 'many');
172
-    }
173
-
174
-    /**
175
-     * Match the eagerly loaded results to their many parents.
176
-     *
177
-     * @param array  $results
178
-     * @param string $relation
179
-     * @param string $type
180
-     *
181
-     * @return array
182
-     */
183
-    protected function matchOneOrMany(array $results, $relation, $type)
184
-    {
185
-        $entities = $this->getEager();
186
-
187
-        $dictionary = $this->buildDictionary($entities);
188
-
189
-        $cache = $this->parentMapper->getEntityCache();
190
-
191
-        $host = $this;
192
-
193
-        // Once we have the dictionary we can simply spin through the parent models to
194
-        // link them up with their children using the keyed dictionary to make the
195
-        // matching very convenient and easy work. Then we'll just return them.
196
-        return array_map(function ($result) use ($dictionary, $cache, $type, $relation, $host) {
197
-            $key = $result[$host->localKey];
198
-
199
-            if (isset($dictionary[$key])) {
200
-                $value = $host->getRelationValue($dictionary, $key, $type);
201
-
202
-                $result[$relation] = $value;
203
-
204
-                // TODO : Refactor This
205
-                $cache->cacheLoadedRelationResult($key, $relation, $value, $this);
206
-            } else {
207
-                // TODO set an empty collection or entityCollection if type is many ?
208
-                $result[$relation] = null;
209
-            }
210
-
211
-            return $result;
212
-        }, $results);
213
-    }
214
-
215
-    /**
216
-     * Get the value of a relationship by one or many type.
217
-     *
218
-     * @param array  $dictionary
219
-     * @param string $key
220
-     * @param string $type
221
-     *
222
-     * @return mixed
223
-     */
224
-    protected function getRelationValue(array $dictionary, $key, $type)
225
-    {
226
-        $value = $dictionary[$key];
227
-
228
-        return $type == 'one' ? reset($value) : $this->relatedMap->newCollection($value);
229
-    }
230
-
231
-    /**
232
-     * Build model dictionary keyed by the relation's foreign key.
233
-     *
234
-     * @param EntityCollection $results
235
-     *
236
-     * @return array
237
-     */
238
-    protected function buildDictionary(EntityCollection $results)
239
-    {
240
-        $dictionary = [];
241
-
242
-        $foreign = $this->getPlainForeignKey();
243
-
244
-        // First we will create a dictionary of models keyed by the foreign key of the
245
-        // relationship as this will allow us to quickly access all of the related
246
-        // models without having to do nested looping which will be quite slow.
247
-        foreach ($results as $result) {
248
-            $dictionary[$result->{$foreign}][] = $result;
249
-        }
250
-
251
-        return $dictionary;
252
-    }
253
-
254
-    /**
255
-     * Get the key for comparing against the parent key in "has" query.
256
-     *
257
-     * @return string
258
-     */
259
-    public function getHasCompareKey()
260
-    {
261
-        return $this->getForeignKey();
262
-    }
263
-
264
-    /**
265
-     * Get the foreign key for the relationship.
266
-     *
267
-     * @return string
268
-     */
269
-    public function getForeignKey()
270
-    {
271
-        return $this->foreignKey;
272
-    }
273
-
274
-    /**
275
-     * Get the plain foreign key.
276
-     *
277
-     * @return string
278
-     */
279
-    public function getPlainForeignKey()
280
-    {
281
-        $segments = explode('.', $this->getForeignKey());
282
-
283
-        return $segments[count($segments) - 1];
284
-    }
285
-
286
-    /**
287
-     * Get the key value of the parent's local key.
288
-     *
289
-     * @return mixed
290
-     */
291
-    public function getParentKey()
292
-    {
293
-        return $this->parent->getEntityAttribute($this->localKey);
294
-    }
295
-
296
-    /**
297
-     * Get the fully qualified parent key name.
298
-     *
299
-     * @return string
300
-     */
301
-    public function getQualifiedParentKeyName()
302
-    {
303
-        return $this->parentMap->getTable().'.'.$this->localKey;
304
-    }
305
-
306
-    /**
307
-     * Get the foreign key as value pair for this relation.
308
-     *
309
-     * @return array
310
-     */
311
-    public function getForeignKeyValuePair()
312
-    {
313
-        return [$this->getPlainForeignKey() => $this->getParentKey()];
314
-    }
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
+	 *
75
+	 * @param mixed $entities
76
+	 *
77
+	 * @throws \InvalidArgumentException
78
+	 *
79
+	 * @return void
80
+	 */
81
+	public function sync(array $entities)
82
+	{
83
+		$this->detachExcept($entities);
84
+	}
85
+
86
+	/**
87
+	 * @param  $entities
88
+	 *
89
+	 * @throws \InvalidArgumentException
90
+	 */
91
+	protected function detachExcept($entities)
92
+	{
93
+		$query = $this->query->getQuery()->from($this->relatedMap->getTable());
94
+
95
+		if (count($entities) > 0) {
96
+			$keys = $this->getKeys($entities);
97
+			$query->whereNotIn($this->relatedMap->getKeyName(), $keys);
98
+		}
99
+
100
+		$parentKey = $this->parentMap->getKeyName();
101
+
102
+		$query->where($this->getPlainForeignKey(), '=', $this->parent->getEntityAttribute($parentKey))
103
+			->update([$this->getPlainForeignKey() => null]);
104
+	}
105
+
106
+	/**
107
+	 * @param array $entityHashes
108
+	 */
109
+	public function detachMany(array $entityHashes)
110
+	{
111
+		$keys = [];
112
+
113
+		foreach ($entityHashes as $hash) {
114
+			$split = explode('.', $hash);
115
+			$keys[] = $split[1];
116
+		}
117
+
118
+		$query = $this->query->getQuery()->from($this->relatedMap->getTable());
119
+
120
+		$query->whereIn($this->relatedMap->getKeyName(), $keys)
121
+			->update([$this->getPlainForeignKey() => null]);
122
+	}
123
+
124
+	/**
125
+	 * Set the base constraints on the relation query.
126
+	 *
127
+	 * @return void
128
+	 */
129
+	public function addConstraints()
130
+	{
131
+		if (static::$constraints) {
132
+			$this->query->where($this->foreignKey, '=', $this->getParentKey());
133
+		}
134
+	}
135
+
136
+	/**
137
+	 * Set the constraints for an eager load of the relation.
138
+	 *
139
+	 * @param array $results
140
+	 *
141
+	 * @return void
142
+	 */
143
+	public function addEagerConstraints(array $results)
144
+	{
145
+		$this->query->whereIn($this->foreignKey, $this->getKeysFromResults($results, $this->localKey));
146
+	}
147
+
148
+	/**
149
+	 * Match the eagerly loaded relationship to the current result set.
150
+	 *
151
+	 * @param array  $results
152
+	 * @param string $relation
153
+	 *
154
+	 * @return array
155
+	 */
156
+	public function matchOne(array $results, $relation)
157
+	{
158
+		return $this->matchOneOrMany($results, $relation, 'one');
159
+	}
160
+
161
+	/**
162
+	 * Match the eagerly loaded results to their many parents.
163
+	 *
164
+	 * @param array  $results
165
+	 * @param string $relation
166
+	 *
167
+	 * @return array
168
+	 */
169
+	public function matchMany(array $results, $relation)
170
+	{
171
+		return $this->matchOneOrMany($results, $relation, 'many');
172
+	}
173
+
174
+	/**
175
+	 * Match the eagerly loaded results to their many parents.
176
+	 *
177
+	 * @param array  $results
178
+	 * @param string $relation
179
+	 * @param string $type
180
+	 *
181
+	 * @return array
182
+	 */
183
+	protected function matchOneOrMany(array $results, $relation, $type)
184
+	{
185
+		$entities = $this->getEager();
186
+
187
+		$dictionary = $this->buildDictionary($entities);
188
+
189
+		$cache = $this->parentMapper->getEntityCache();
190
+
191
+		$host = $this;
192
+
193
+		// Once we have the dictionary we can simply spin through the parent models to
194
+		// link them up with their children using the keyed dictionary to make the
195
+		// matching very convenient and easy work. Then we'll just return them.
196
+		return array_map(function ($result) use ($dictionary, $cache, $type, $relation, $host) {
197
+			$key = $result[$host->localKey];
198
+
199
+			if (isset($dictionary[$key])) {
200
+				$value = $host->getRelationValue($dictionary, $key, $type);
201
+
202
+				$result[$relation] = $value;
203
+
204
+				// TODO : Refactor This
205
+				$cache->cacheLoadedRelationResult($key, $relation, $value, $this);
206
+			} else {
207
+				// TODO set an empty collection or entityCollection if type is many ?
208
+				$result[$relation] = null;
209
+			}
210
+
211
+			return $result;
212
+		}, $results);
213
+	}
214
+
215
+	/**
216
+	 * Get the value of a relationship by one or many type.
217
+	 *
218
+	 * @param array  $dictionary
219
+	 * @param string $key
220
+	 * @param string $type
221
+	 *
222
+	 * @return mixed
223
+	 */
224
+	protected function getRelationValue(array $dictionary, $key, $type)
225
+	{
226
+		$value = $dictionary[$key];
227
+
228
+		return $type == 'one' ? reset($value) : $this->relatedMap->newCollection($value);
229
+	}
230
+
231
+	/**
232
+	 * Build model dictionary keyed by the relation's foreign key.
233
+	 *
234
+	 * @param EntityCollection $results
235
+	 *
236
+	 * @return array
237
+	 */
238
+	protected function buildDictionary(EntityCollection $results)
239
+	{
240
+		$dictionary = [];
241
+
242
+		$foreign = $this->getPlainForeignKey();
243
+
244
+		// First we will create a dictionary of models keyed by the foreign key of the
245
+		// relationship as this will allow us to quickly access all of the related
246
+		// models without having to do nested looping which will be quite slow.
247
+		foreach ($results as $result) {
248
+			$dictionary[$result->{$foreign}][] = $result;
249
+		}
250
+
251
+		return $dictionary;
252
+	}
253
+
254
+	/**
255
+	 * Get the key for comparing against the parent key in "has" query.
256
+	 *
257
+	 * @return string
258
+	 */
259
+	public function getHasCompareKey()
260
+	{
261
+		return $this->getForeignKey();
262
+	}
263
+
264
+	/**
265
+	 * Get the foreign key for the relationship.
266
+	 *
267
+	 * @return string
268
+	 */
269
+	public function getForeignKey()
270
+	{
271
+		return $this->foreignKey;
272
+	}
273
+
274
+	/**
275
+	 * Get the plain foreign key.
276
+	 *
277
+	 * @return string
278
+	 */
279
+	public function getPlainForeignKey()
280
+	{
281
+		$segments = explode('.', $this->getForeignKey());
282
+
283
+		return $segments[count($segments) - 1];
284
+	}
285
+
286
+	/**
287
+	 * Get the key value of the parent's local key.
288
+	 *
289
+	 * @return mixed
290
+	 */
291
+	public function getParentKey()
292
+	{
293
+		return $this->parent->getEntityAttribute($this->localKey);
294
+	}
295
+
296
+	/**
297
+	 * Get the fully qualified parent key name.
298
+	 *
299
+	 * @return string
300
+	 */
301
+	public function getQualifiedParentKeyName()
302
+	{
303
+		return $this->parentMap->getTable().'.'.$this->localKey;
304
+	}
305
+
306
+	/**
307
+	 * Get the foreign key as value pair for this relation.
308
+	 *
309
+	 * @return array
310
+	 */
311
+	public function getForeignKeyValuePair()
312
+	{
313
+		return [$this->getPlainForeignKey() => $this->getParentKey()];
314
+	}
315 315
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -193,7 +193,7 @@
 block discarded – undo
193 193
         // Once we have the dictionary we can simply spin through the parent models to
194 194
         // link them up with their children using the keyed dictionary to make the
195 195
         // matching very convenient and easy work. Then we'll just return them.
196
-        return array_map(function ($result) use ($dictionary, $cache, $type, $relation, $host) {
196
+        return array_map(function($result) use ($dictionary, $cache, $type, $relation, $host) {
197 197
             $key = $result[$host->localKey];
198 198
 
199 199
             if (isset($dictionary[$key])) {
Please login to merge, or discard this patch.
src/Relationships/Pivot.php 1 patch
Indentation   +158 added lines, -158 removed lines patch added patch discarded remove patch
@@ -6,162 +6,162 @@
 block discarded – undo
6 6
 
7 7
 class Pivot extends Entity
8 8
 {
9
-    /**
10
-     * @var bool
11
-     */
12
-    protected $exists;
13
-
14
-    /**
15
-     * Pivot's table.
16
-     *
17
-     * @var string
18
-     */
19
-    protected $table;
20
-
21
-    /**
22
-     * The parent entity of the relationship.
23
-     *
24
-     * @var object
25
-     */
26
-    protected $parent;
27
-
28
-    /**
29
-     * The parent entity of the relationship.
30
-     *
31
-     * @var \Analogue\ORM\EntityMap
32
-     */
33
-    protected $parentMap;
34
-
35
-    /**
36
-     * We may define a pivot mapping to deal with
37
-     * soft deletes, timestamps, etc.
38
-     *
39
-     * @var \Analogue\ORM\EntityMap
40
-     */
41
-    protected $pivotMap;
42
-
43
-    /**
44
-     * The name of the foreign key column.
45
-     *
46
-     * @var string
47
-     */
48
-    protected $foreignKey;
49
-
50
-    /**
51
-     * The name of the "other key" column.
52
-     *
53
-     * @var string
54
-     */
55
-    protected $otherKey;
56
-
57
-    /**
58
-     * The attributes that aren't mass assignable.
59
-     *
60
-     * @var array
61
-     */
62
-    protected $guarded = [];
63
-
64
-    /**
65
-     * Pivot uses timestamps ?
66
-     *
67
-     * @var bool
68
-     */
69
-    protected $timestamps;
70
-
71
-    /**
72
-     * Create a new pivot model instance.
73
-     *
74
-     * @param \Analogue\ORM\System\InternallyMappable $parent
75
-     * @param \Analogue\ORM\EntityMap                 $parentMap
76
-     * @param array                                   $attributes
77
-     * @param string                                  $table
78
-     * @param bool                                    $exists
79
-     */
80
-    public function __construct($parent, $parentMap, $attributes, $table, $exists = false)
81
-    {
82
-        // The pivot model is a "dynamic" model since we will set the tables dynamically
83
-        // for the instance. This allows it work for any intermediate tables for the
84
-        // many to many relationship that are defined by this developer's classes.
85
-        $this->setEntityAttributes($attributes, true);
86
-
87
-        $this->table = $table;
88
-
89
-        // We store off the parent instance so we will access the timestamp column names
90
-        // for the model, since the pivot model timestamps aren't easily configurable
91
-        // from the developer's point of view. We can use the parents to get these.
92
-        $this->parent = $parent;
93
-
94
-        $this->parentMap = $parentMap;
95
-
96
-        $this->exists = $exists;
97
-
98
-        $this->timestamps = $this->hasTimestampAttributes();
99
-    }
100
-
101
-    /**
102
-     * Get the foreign key column name.
103
-     *
104
-     * @return string
105
-     */
106
-    public function getForeignKey()
107
-    {
108
-        return $this->foreignKey;
109
-    }
110
-
111
-    /**
112
-     * Get the "other key" column name.
113
-     *
114
-     * @return string
115
-     */
116
-    public function getOtherKey()
117
-    {
118
-        return $this->otherKey;
119
-    }
120
-
121
-    /**
122
-     * Set the key names for the pivot model instance.
123
-     *
124
-     * @param string $foreignKey
125
-     * @param string $otherKey
126
-     *
127
-     * @return $this
128
-     */
129
-    public function setPivotKeys($foreignKey, $otherKey)
130
-    {
131
-        $this->foreignKey = $foreignKey;
132
-
133
-        $this->otherKey = $otherKey;
134
-
135
-        return $this;
136
-    }
137
-
138
-    /**
139
-     * Determine if the pivot model has timestamp attributes.
140
-     *
141
-     * @return bool
142
-     */
143
-    public function hasTimestampAttributes()
144
-    {
145
-        return array_key_exists($this->getCreatedAtColumn(), $this->attributes);
146
-    }
147
-
148
-    /**
149
-     * Get the name of the "created at" column.
150
-     *
151
-     * @return string
152
-     */
153
-    public function getCreatedAtColumn()
154
-    {
155
-        return $this->parentMap->getCreatedAtColumn();
156
-    }
157
-
158
-    /**
159
-     * Get the name of the "updated at" column.
160
-     *
161
-     * @return string
162
-     */
163
-    public function getUpdatedAtColumn()
164
-    {
165
-        return $this->parentMap->getUpdatedAtColumn();
166
-    }
9
+	/**
10
+	 * @var bool
11
+	 */
12
+	protected $exists;
13
+
14
+	/**
15
+	 * Pivot's table.
16
+	 *
17
+	 * @var string
18
+	 */
19
+	protected $table;
20
+
21
+	/**
22
+	 * The parent entity of the relationship.
23
+	 *
24
+	 * @var object
25
+	 */
26
+	protected $parent;
27
+
28
+	/**
29
+	 * The parent entity of the relationship.
30
+	 *
31
+	 * @var \Analogue\ORM\EntityMap
32
+	 */
33
+	protected $parentMap;
34
+
35
+	/**
36
+	 * We may define a pivot mapping to deal with
37
+	 * soft deletes, timestamps, etc.
38
+	 *
39
+	 * @var \Analogue\ORM\EntityMap
40
+	 */
41
+	protected $pivotMap;
42
+
43
+	/**
44
+	 * The name of the foreign key column.
45
+	 *
46
+	 * @var string
47
+	 */
48
+	protected $foreignKey;
49
+
50
+	/**
51
+	 * The name of the "other key" column.
52
+	 *
53
+	 * @var string
54
+	 */
55
+	protected $otherKey;
56
+
57
+	/**
58
+	 * The attributes that aren't mass assignable.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $guarded = [];
63
+
64
+	/**
65
+	 * Pivot uses timestamps ?
66
+	 *
67
+	 * @var bool
68
+	 */
69
+	protected $timestamps;
70
+
71
+	/**
72
+	 * Create a new pivot model instance.
73
+	 *
74
+	 * @param \Analogue\ORM\System\InternallyMappable $parent
75
+	 * @param \Analogue\ORM\EntityMap                 $parentMap
76
+	 * @param array                                   $attributes
77
+	 * @param string                                  $table
78
+	 * @param bool                                    $exists
79
+	 */
80
+	public function __construct($parent, $parentMap, $attributes, $table, $exists = false)
81
+	{
82
+		// The pivot model is a "dynamic" model since we will set the tables dynamically
83
+		// for the instance. This allows it work for any intermediate tables for the
84
+		// many to many relationship that are defined by this developer's classes.
85
+		$this->setEntityAttributes($attributes, true);
86
+
87
+		$this->table = $table;
88
+
89
+		// We store off the parent instance so we will access the timestamp column names
90
+		// for the model, since the pivot model timestamps aren't easily configurable
91
+		// from the developer's point of view. We can use the parents to get these.
92
+		$this->parent = $parent;
93
+
94
+		$this->parentMap = $parentMap;
95
+
96
+		$this->exists = $exists;
97
+
98
+		$this->timestamps = $this->hasTimestampAttributes();
99
+	}
100
+
101
+	/**
102
+	 * Get the foreign key column name.
103
+	 *
104
+	 * @return string
105
+	 */
106
+	public function getForeignKey()
107
+	{
108
+		return $this->foreignKey;
109
+	}
110
+
111
+	/**
112
+	 * Get the "other key" column name.
113
+	 *
114
+	 * @return string
115
+	 */
116
+	public function getOtherKey()
117
+	{
118
+		return $this->otherKey;
119
+	}
120
+
121
+	/**
122
+	 * Set the key names for the pivot model instance.
123
+	 *
124
+	 * @param string $foreignKey
125
+	 * @param string $otherKey
126
+	 *
127
+	 * @return $this
128
+	 */
129
+	public function setPivotKeys($foreignKey, $otherKey)
130
+	{
131
+		$this->foreignKey = $foreignKey;
132
+
133
+		$this->otherKey = $otherKey;
134
+
135
+		return $this;
136
+	}
137
+
138
+	/**
139
+	 * Determine if the pivot model has timestamp attributes.
140
+	 *
141
+	 * @return bool
142
+	 */
143
+	public function hasTimestampAttributes()
144
+	{
145
+		return array_key_exists($this->getCreatedAtColumn(), $this->attributes);
146
+	}
147
+
148
+	/**
149
+	 * Get the name of the "created at" column.
150
+	 *
151
+	 * @return string
152
+	 */
153
+	public function getCreatedAtColumn()
154
+	{
155
+		return $this->parentMap->getCreatedAtColumn();
156
+	}
157
+
158
+	/**
159
+	 * Get the name of the "updated at" column.
160
+	 *
161
+	 * @return string
162
+	 */
163
+	public function getUpdatedAtColumn()
164
+	{
165
+		return $this->parentMap->getUpdatedAtColumn();
166
+	}
167 167
 }
Please login to merge, or discard this patch.
src/Relationships/MorphTo.php 2 patches
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -8,226 +8,226 @@
 block discarded – undo
8 8
 
9 9
 class MorphTo extends BelongsTo
10 10
 {
11
-    /**
12
-     * The type of the polymorphic relation.
13
-     *
14
-     * @var string
15
-     */
16
-    protected $morphType;
17
-
18
-    /**
19
-     * The entities whose relations are being eager loaded.
20
-     *
21
-     * @var EntityCollection
22
-     */
23
-    protected $entities;
24
-
25
-    /**
26
-     * All of the result sets keyed by ID.
27
-     *
28
-     * @var array
29
-     */
30
-    protected $dictionary = [];
31
-
32
-    /**
33
-     * Indicates if soft-deleted model instances should be fetched.
34
-     *
35
-     * @var bool
36
-     */
37
-    protected $withTrashed = false;
38
-
39
-    /**
40
-     * Indicate if the parent entity hold the key for the relation.
41
-     *
42
-     * @var bool
43
-     */
44
-    protected static $ownForeignKey = true;
45
-
46
-    /**
47
-     * Create a new belongs to relationship instance.
48
-     *
49
-     * @param Mapper                 $mapper
50
-     * @param \Analogue\ORM\Mappable $parent
51
-     * @param string                 $foreignKey
52
-     * @param string                 $otherKey
53
-     * @param string                 $type
54
-     * @param string                 $relation
55
-     */
56
-    public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $type, $relation)
57
-    {
58
-        $this->morphType = $type;
59
-
60
-        parent::__construct($mapper, $parent, $foreignKey, $otherKey, $relation);
61
-    }
62
-
63
-    /**
64
-     * Set the constraints for an eager load of the relation.
65
-     *
66
-     * @param array $results
67
-     *
68
-     * @return void
69
-     */
70
-    public function addEagerConstraints(array $results)
71
-    {
72
-        $this->buildDictionary($results);
73
-    }
74
-
75
-    /**
76
-     * Build a dictionary with the entities.
77
-     *
78
-     * @param array $results
79
-     *
80
-     * @return void
81
-     */
82
-    protected function buildDictionary($results)
83
-    {
84
-        foreach ($results as $result) {
85
-            if ($result[$this->morphType]) {
86
-                $this->dictionary[$result[$this->morphType]][$result[$this->foreignKey]] = $result;
87
-            }
88
-        }
89
-    }
90
-
91
-    /**
92
-     * Match the eagerly loaded results to their parents.
93
-     *
94
-     * @param array  $results
95
-     * @param string $relation
96
-     *
97
-     * @return array
98
-     */
99
-    public function match(array $results, $relation)
100
-    {
101
-        return $entities;
102
-    }
103
-
104
-    /**
105
-     * Get the results of the relationship.
106
-     *
107
-     * @throws \Analogue\ORM\Exceptions\MappingException
108
-     *
109
-     * @return EntityCollection
110
-     */
111
-    public function getEager()
112
-    {
113
-        foreach (array_keys($this->dictionary) as $type) {
114
-            $this->matchToMorphParents($type, $this->getResultsByType($type));
115
-        }
116
-
117
-        return $this->entities;
118
-    }
119
-
120
-    /**
121
-     * Match the results for a given type to their parents.
122
-     *
123
-     * @param string           $type
124
-     * @param EntityCollection $results
125
-     *
126
-     * @return void
127
-     */
128
-    protected function matchToMorphParents($type, EntityCollection $results)
129
-    {
130
-        $mapper = $this->relatedMapper->getManager()->mapper($type);
131
-        $keyName = $mapper->getEntityMap()->getKeyName();
132
-
133
-        foreach ($results as $result) {
134
-            $key = $result[$keyName];
135
-
136
-            if (isset($this->dictionary[$type][$key])) {
137
-                foreach ($this->dictionary[$type][$key] as $result) {
138
-                    $result[$this->relation] = $result;
139
-                }
140
-            }
141
-        }
142
-    }
143
-
144
-    /**
145
-     * Get all of the relation results for a type.
146
-     *
147
-     * @param string $type
148
-     *
149
-     * @throws \Analogue\ORM\Exceptions\MappingException
150
-     *
151
-     * @return EntityCollection
152
-     */
153
-    protected function getResultsByType($type)
154
-    {
155
-        $mapper = $this->relatedMapper->getManager()->mapper($type);
156
-
157
-        $key = $mapper->getEntityMap()->getKeyName();
158
-
159
-        $query = $mapper->getQuery();
160
-
161
-        return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
162
-    }
163
-
164
-    /**
165
-     * Gather all of the foreign keys for a given type.
166
-     *
167
-     * @param string $type
168
-     *
169
-     * @return BaseCollection
170
-     */
171
-    protected function gatherKeysByType($type)
172
-    {
173
-        $foreign = $this->foreignKey;
174
-
175
-        return BaseCollection::make($this->dictionary[$type])->map(function ($entities) use ($foreign) {
176
-            return head($entities)->{$foreign};
177
-        })->unique();
178
-    }
179
-
180
-    /**
181
-     * Associate the model instance to the given parent.
182
-     *
183
-     * @param mixed $entity
184
-     *
185
-     * @return void
186
-     */
187
-    public function associate($entity)
188
-    {
189
-        // The Mapper will retrieve this association within the object model, we won't be using
190
-        // the foreign key attribute inside the parent Entity.
191
-        //
192
-        //$this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
193
-        //
194
-        // Instead, we'll just add the object to the Entity's attribute
195
-
196
-        $this->parent->setEntityAttribute($this->relation, $entity->getEntityObject());
197
-    }
198
-
199
-    /**
200
-     * Get the foreign key value pair for a related object.
201
-     *
202
-     * @var mixed
203
-     *
204
-     * @return array
205
-     */
206
-    public function getForeignKeyValuePair($related)
207
-    {
208
-        $foreignKey = $this->getForeignKey();
209
-
210
-        if ($related) {
211
-            $wrapper = $this->factory->make($related);
212
-
213
-            $relatedKey = $this->relatedMap->getKeyName();
214
-
215
-            return [
216
-                $foreignKey      => $wrapper->getEntityAttribute($relatedKey),
217
-                $this->morphType => $wrapper->getMap()->getMorphClass(),
218
-            ];
219
-        } else {
220
-            return [$foreignKey => null];
221
-        }
222
-    }
223
-
224
-    /**
225
-     * Get the dictionary used by the relationship.
226
-     *
227
-     * @return array
228
-     */
229
-    public function getDictionary()
230
-    {
231
-        return $this->dictionary;
232
-    }
11
+	/**
12
+	 * The type of the polymorphic relation.
13
+	 *
14
+	 * @var string
15
+	 */
16
+	protected $morphType;
17
+
18
+	/**
19
+	 * The entities whose relations are being eager loaded.
20
+	 *
21
+	 * @var EntityCollection
22
+	 */
23
+	protected $entities;
24
+
25
+	/**
26
+	 * All of the result sets keyed by ID.
27
+	 *
28
+	 * @var array
29
+	 */
30
+	protected $dictionary = [];
31
+
32
+	/**
33
+	 * Indicates if soft-deleted model instances should be fetched.
34
+	 *
35
+	 * @var bool
36
+	 */
37
+	protected $withTrashed = false;
38
+
39
+	/**
40
+	 * Indicate if the parent entity hold the key for the relation.
41
+	 *
42
+	 * @var bool
43
+	 */
44
+	protected static $ownForeignKey = true;
45
+
46
+	/**
47
+	 * Create a new belongs to relationship instance.
48
+	 *
49
+	 * @param Mapper                 $mapper
50
+	 * @param \Analogue\ORM\Mappable $parent
51
+	 * @param string                 $foreignKey
52
+	 * @param string                 $otherKey
53
+	 * @param string                 $type
54
+	 * @param string                 $relation
55
+	 */
56
+	public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $type, $relation)
57
+	{
58
+		$this->morphType = $type;
59
+
60
+		parent::__construct($mapper, $parent, $foreignKey, $otherKey, $relation);
61
+	}
62
+
63
+	/**
64
+	 * Set the constraints for an eager load of the relation.
65
+	 *
66
+	 * @param array $results
67
+	 *
68
+	 * @return void
69
+	 */
70
+	public function addEagerConstraints(array $results)
71
+	{
72
+		$this->buildDictionary($results);
73
+	}
74
+
75
+	/**
76
+	 * Build a dictionary with the entities.
77
+	 *
78
+	 * @param array $results
79
+	 *
80
+	 * @return void
81
+	 */
82
+	protected function buildDictionary($results)
83
+	{
84
+		foreach ($results as $result) {
85
+			if ($result[$this->morphType]) {
86
+				$this->dictionary[$result[$this->morphType]][$result[$this->foreignKey]] = $result;
87
+			}
88
+		}
89
+	}
90
+
91
+	/**
92
+	 * Match the eagerly loaded results to their parents.
93
+	 *
94
+	 * @param array  $results
95
+	 * @param string $relation
96
+	 *
97
+	 * @return array
98
+	 */
99
+	public function match(array $results, $relation)
100
+	{
101
+		return $entities;
102
+	}
103
+
104
+	/**
105
+	 * Get the results of the relationship.
106
+	 *
107
+	 * @throws \Analogue\ORM\Exceptions\MappingException
108
+	 *
109
+	 * @return EntityCollection
110
+	 */
111
+	public function getEager()
112
+	{
113
+		foreach (array_keys($this->dictionary) as $type) {
114
+			$this->matchToMorphParents($type, $this->getResultsByType($type));
115
+		}
116
+
117
+		return $this->entities;
118
+	}
119
+
120
+	/**
121
+	 * Match the results for a given type to their parents.
122
+	 *
123
+	 * @param string           $type
124
+	 * @param EntityCollection $results
125
+	 *
126
+	 * @return void
127
+	 */
128
+	protected function matchToMorphParents($type, EntityCollection $results)
129
+	{
130
+		$mapper = $this->relatedMapper->getManager()->mapper($type);
131
+		$keyName = $mapper->getEntityMap()->getKeyName();
132
+
133
+		foreach ($results as $result) {
134
+			$key = $result[$keyName];
135
+
136
+			if (isset($this->dictionary[$type][$key])) {
137
+				foreach ($this->dictionary[$type][$key] as $result) {
138
+					$result[$this->relation] = $result;
139
+				}
140
+			}
141
+		}
142
+	}
143
+
144
+	/**
145
+	 * Get all of the relation results for a type.
146
+	 *
147
+	 * @param string $type
148
+	 *
149
+	 * @throws \Analogue\ORM\Exceptions\MappingException
150
+	 *
151
+	 * @return EntityCollection
152
+	 */
153
+	protected function getResultsByType($type)
154
+	{
155
+		$mapper = $this->relatedMapper->getManager()->mapper($type);
156
+
157
+		$key = $mapper->getEntityMap()->getKeyName();
158
+
159
+		$query = $mapper->getQuery();
160
+
161
+		return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
162
+	}
163
+
164
+	/**
165
+	 * Gather all of the foreign keys for a given type.
166
+	 *
167
+	 * @param string $type
168
+	 *
169
+	 * @return BaseCollection
170
+	 */
171
+	protected function gatherKeysByType($type)
172
+	{
173
+		$foreign = $this->foreignKey;
174
+
175
+		return BaseCollection::make($this->dictionary[$type])->map(function ($entities) use ($foreign) {
176
+			return head($entities)->{$foreign};
177
+		})->unique();
178
+	}
179
+
180
+	/**
181
+	 * Associate the model instance to the given parent.
182
+	 *
183
+	 * @param mixed $entity
184
+	 *
185
+	 * @return void
186
+	 */
187
+	public function associate($entity)
188
+	{
189
+		// The Mapper will retrieve this association within the object model, we won't be using
190
+		// the foreign key attribute inside the parent Entity.
191
+		//
192
+		//$this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
193
+		//
194
+		// Instead, we'll just add the object to the Entity's attribute
195
+
196
+		$this->parent->setEntityAttribute($this->relation, $entity->getEntityObject());
197
+	}
198
+
199
+	/**
200
+	 * Get the foreign key value pair for a related object.
201
+	 *
202
+	 * @var mixed
203
+	 *
204
+	 * @return array
205
+	 */
206
+	public function getForeignKeyValuePair($related)
207
+	{
208
+		$foreignKey = $this->getForeignKey();
209
+
210
+		if ($related) {
211
+			$wrapper = $this->factory->make($related);
212
+
213
+			$relatedKey = $this->relatedMap->getKeyName();
214
+
215
+			return [
216
+				$foreignKey      => $wrapper->getEntityAttribute($relatedKey),
217
+				$this->morphType => $wrapper->getMap()->getMorphClass(),
218
+			];
219
+		} else {
220
+			return [$foreignKey => null];
221
+		}
222
+	}
223
+
224
+	/**
225
+	 * Get the dictionary used by the relationship.
226
+	 *
227
+	 * @return array
228
+	 */
229
+	public function getDictionary()
230
+	{
231
+		return $this->dictionary;
232
+	}
233 233
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -172,7 +172,7 @@
 block discarded – undo
172 172
     {
173 173
         $foreign = $this->foreignKey;
174 174
 
175
-        return BaseCollection::make($this->dictionary[$type])->map(function ($entities) use ($foreign) {
175
+        return BaseCollection::make($this->dictionary[$type])->map(function($entities) use ($foreign) {
176 176
             return head($entities)->{$foreign};
177 177
         })->unique();
178 178
     }
Please login to merge, or discard this patch.
src/Relationships/MorphMany.php 1 patch
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -4,32 +4,32 @@
 block discarded – undo
4 4
 
5 5
 class MorphMany extends MorphOneOrMany
6 6
 {
7
-    /**
8
-     * Get the results of the relationship.
9
-     *
10
-     * @param  $relation
11
-     *
12
-     * @return mixed
13
-     */
14
-    public function getResults($relation)
15
-    {
16
-        $results = $this->query->get();
7
+	/**
8
+	 * Get the results of the relationship.
9
+	 *
10
+	 * @param  $relation
11
+	 *
12
+	 * @return mixed
13
+	 */
14
+	public function getResults($relation)
15
+	{
16
+		$results = $this->query->get();
17 17
 
18
-        $this->cacheRelation($results, $relation);
18
+		$this->cacheRelation($results, $relation);
19 19
 
20
-        return $results;
21
-    }
20
+		return $results;
21
+	}
22 22
 
23
-    /**
24
-     * Match the eagerly loaded results to their parents.
25
-     *
26
-     * @param array  $results
27
-     * @param string $relation
28
-     *
29
-     * @return array
30
-     */
31
-    public function match(array $results, $relation)
32
-    {
33
-        return $this->matchMany($results, $relation);
34
-    }
23
+	/**
24
+	 * Match the eagerly loaded results to their parents.
25
+	 *
26
+	 * @param array  $results
27
+	 * @param string $relation
28
+	 *
29
+	 * @return array
30
+	 */
31
+	public function match(array $results, $relation)
32
+	{
33
+		return $this->matchMany($results, $relation);
34
+	}
35 35
 }
Please login to merge, or discard this patch.
src/Relationships/MorphOneOrMany.php 1 patch
Indentation   +118 added lines, -118 removed lines patch added patch discarded remove patch
@@ -7,122 +7,122 @@
 block discarded – undo
7 7
 
8 8
 abstract class MorphOneOrMany extends HasOneOrMany
9 9
 {
10
-    /**
11
-     * The foreign key type for the relationship.
12
-     *
13
-     * @var string
14
-     */
15
-    protected $morphType;
16
-
17
-    /**
18
-     * The class name of the parent model.
19
-     *
20
-     * @var string
21
-     */
22
-    protected $morphClass;
23
-
24
-    /**
25
-     * Create a new has many relationship instance.
26
-     *
27
-     * @param Mapper                 $mapper
28
-     * @param \Analogue\ORM\Mappable $parent
29
-     * @param string                 $type
30
-     * @param string                 $id
31
-     * @param string                 $localKey
32
-     *
33
-     * @throws \Analogue\ORM\Exceptions\MappingException
34
-     */
35
-    public function __construct(Mapper $mapper, $parent, $type, $id, $localKey)
36
-    {
37
-        $this->morphType = $type;
38
-        $this->morphClass = $mapper->getManager()->getMapper($parent)->getEntityMap()->getMorphClass();
39
-
40
-        parent::__construct($mapper, $parent, $id, $localKey);
41
-    }
42
-
43
-    /**
44
-     * Set the base constraints on the relation query.
45
-     *
46
-     * @return void
47
-     */
48
-    public function addConstraints()
49
-    {
50
-        if (static::$constraints) {
51
-            parent::addConstraints();
52
-
53
-            $this->query->where($this->morphType, $this->morphClass);
54
-        }
55
-    }
56
-
57
-    /**
58
-     * Get the relationship count query.
59
-     *
60
-     * @param Query $query
61
-     * @param Query $parent
62
-     *
63
-     * @return Query
64
-     */
65
-    public function getRelationCountQuery(Query $query, Query $parent)
66
-    {
67
-        $query = parent::getRelationCountQuery($query, $parent);
68
-
69
-        return $query->where($this->morphType, $this->morphClass);
70
-    }
71
-
72
-    /**
73
-     * Set the constraints for an eager load of the relation.
74
-     *
75
-     * @param array $results
76
-     *
77
-     * @return void
78
-     */
79
-    public function addEagerConstraints(array $results)
80
-    {
81
-        parent::addEagerConstraints($results);
82
-
83
-        $this->query->where($this->morphType, $this->morphClass);
84
-    }
85
-
86
-    /**
87
-     * Get the foreign key "type" name.
88
-     *
89
-     * @return string
90
-     */
91
-    public function getMorphType()
92
-    {
93
-        return $this->morphType;
94
-    }
95
-
96
-    /**
97
-     * Get the plain morph type name without the table.
98
-     *
99
-     * @return string
100
-     */
101
-    public function getPlainMorphType()
102
-    {
103
-        return last(explode('.', $this->morphType));
104
-    }
105
-
106
-    /**
107
-     * Get the class name of the parent model.
108
-     *
109
-     * @return string
110
-     */
111
-    public function getMorphClass()
112
-    {
113
-        return $this->morphClass;
114
-    }
115
-
116
-    /**
117
-     * Get the foreign key as value pair for this relation.
118
-     *
119
-     * @return array
120
-     */
121
-    public function getForeignKeyValuePair()
122
-    {
123
-        return [
124
-            $this->getPlainForeignKey() => $this->getParentKey(),
125
-            $this->getPlainMorphType()  => $this->morphClass,
126
-        ];
127
-    }
10
+	/**
11
+	 * The foreign key type for the relationship.
12
+	 *
13
+	 * @var string
14
+	 */
15
+	protected $morphType;
16
+
17
+	/**
18
+	 * The class name of the parent model.
19
+	 *
20
+	 * @var string
21
+	 */
22
+	protected $morphClass;
23
+
24
+	/**
25
+	 * Create a new has many relationship instance.
26
+	 *
27
+	 * @param Mapper                 $mapper
28
+	 * @param \Analogue\ORM\Mappable $parent
29
+	 * @param string                 $type
30
+	 * @param string                 $id
31
+	 * @param string                 $localKey
32
+	 *
33
+	 * @throws \Analogue\ORM\Exceptions\MappingException
34
+	 */
35
+	public function __construct(Mapper $mapper, $parent, $type, $id, $localKey)
36
+	{
37
+		$this->morphType = $type;
38
+		$this->morphClass = $mapper->getManager()->getMapper($parent)->getEntityMap()->getMorphClass();
39
+
40
+		parent::__construct($mapper, $parent, $id, $localKey);
41
+	}
42
+
43
+	/**
44
+	 * Set the base constraints on the relation query.
45
+	 *
46
+	 * @return void
47
+	 */
48
+	public function addConstraints()
49
+	{
50
+		if (static::$constraints) {
51
+			parent::addConstraints();
52
+
53
+			$this->query->where($this->morphType, $this->morphClass);
54
+		}
55
+	}
56
+
57
+	/**
58
+	 * Get the relationship count query.
59
+	 *
60
+	 * @param Query $query
61
+	 * @param Query $parent
62
+	 *
63
+	 * @return Query
64
+	 */
65
+	public function getRelationCountQuery(Query $query, Query $parent)
66
+	{
67
+		$query = parent::getRelationCountQuery($query, $parent);
68
+
69
+		return $query->where($this->morphType, $this->morphClass);
70
+	}
71
+
72
+	/**
73
+	 * Set the constraints for an eager load of the relation.
74
+	 *
75
+	 * @param array $results
76
+	 *
77
+	 * @return void
78
+	 */
79
+	public function addEagerConstraints(array $results)
80
+	{
81
+		parent::addEagerConstraints($results);
82
+
83
+		$this->query->where($this->morphType, $this->morphClass);
84
+	}
85
+
86
+	/**
87
+	 * Get the foreign key "type" name.
88
+	 *
89
+	 * @return string
90
+	 */
91
+	public function getMorphType()
92
+	{
93
+		return $this->morphType;
94
+	}
95
+
96
+	/**
97
+	 * Get the plain morph type name without the table.
98
+	 *
99
+	 * @return string
100
+	 */
101
+	public function getPlainMorphType()
102
+	{
103
+		return last(explode('.', $this->morphType));
104
+	}
105
+
106
+	/**
107
+	 * Get the class name of the parent model.
108
+	 *
109
+	 * @return string
110
+	 */
111
+	public function getMorphClass()
112
+	{
113
+		return $this->morphClass;
114
+	}
115
+
116
+	/**
117
+	 * Get the foreign key as value pair for this relation.
118
+	 *
119
+	 * @return array
120
+	 */
121
+	public function getForeignKeyValuePair()
122
+	{
123
+		return [
124
+			$this->getPlainForeignKey() => $this->getParentKey(),
125
+			$this->getPlainMorphType()  => $this->morphClass,
126
+		];
127
+	}
128 128
 }
Please login to merge, or discard this patch.