Completed
Branch master (901ac8)
by Rémi
11:17
created
src/System/EntityCache.php 2 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
     /**
199 199
      * Cache a many relationship.
200 200
      *
201
-     * @param                  $parentKey
201
+     * @param                  string $parentKey
202 202
      * @param string           $relation
203 203
      * @param EntityCollection $results
204 204
      * @param Relationship     $relationship
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
     /**
222 222
      * Cache a single relationship.
223 223
      *
224
-     * @param              $parentKey
224
+     * @param              string $parentKey
225 225
      * @param string       $relation
226 226
      * @param Mappable     $result
227 227
      * @param Relationship $relationship
Please login to merge, or discard this patch.
Indentation   +338 added lines, -338 removed lines patch added patch discarded remove patch
@@ -15,342 +15,342 @@
 block discarded – undo
15 15
  */
16 16
 class EntityCache
17 17
 {
18
-    /**
19
-     * Entity's raw attributes/relationships.
20
-     *
21
-     * @var array
22
-     */
23
-    protected $cache = [];
24
-
25
-    /**
26
-     * Entity Map for the current Entity Type.
27
-     *
28
-     * @var \Analogue\ORM\EntityMap
29
-     */
30
-    protected $entityMap;
31
-
32
-    /**
33
-     * Wrapper factory.
34
-     *
35
-     * @var \Analogue\ORM\System\Wrappers\Factory
36
-     */
37
-    protected $factory;
38
-
39
-    /**
40
-     * Associative array containing list of pivot attributes per relationship
41
-     * so we don't have to call relationship method on refresh.
42
-     *
43
-     * @var array
44
-     */
45
-    protected $pivotAttributes = [];
46
-
47
-    /**
48
-     * EntityCache constructor.
49
-     *
50
-     * @param EntityMap $entityMap
51
-     */
52
-    public function __construct(EntityMap $entityMap)
53
-    {
54
-        $this->entityMap = $entityMap;
55
-
56
-        $this->factory = new Factory();
57
-    }
58
-
59
-    /**
60
-     * Add an array of key=>attributes representing
61
-     * the initial state of loaded entities.
62
-     *
63
-     * @param array $results
64
-     */
65
-    public function add(array $results)
66
-    {
67
-        $cachedResults = [];
68
-
69
-        $keyColumn = $this->entityMap->getKeyName();
70
-
71
-        foreach ($results as $result) {
72
-            $id = $result[$keyColumn];
73
-
74
-            // Forget the ID field from the cache attributes
75
-            // to prevent any side effect.
76
-            // TODO : remove primary key check from dirty attributes parsing
77
-            //unset($result[$keyColumn]);
78
-            $cachedResults[$id] = $result;
79
-        }
80
-
81
-        if (count($this->cache) == 0) {
82
-            $this->cache = $cachedResults;
83
-        } else {
84
-            $this->mergeCacheResults($cachedResults);
85
-        }
86
-    }
87
-
88
-    /**
89
-     * Retrieve initial attributes for a single entity.
90
-     *
91
-     * @param string $id
92
-     *
93
-     * @return array
94
-     */
95
-    public function get($id)
96
-    {
97
-        if ($this->has($id)) {
98
-            return $this->cache[$id];
99
-        } else {
100
-            return [];
101
-        }
102
-    }
103
-
104
-    /**
105
-     * Check if a record for this id exists.
106
-     *
107
-     * @param string $id
108
-     *
109
-     * @return bool
110
-     */
111
-    public function has($id)
112
-    {
113
-        return array_key_exists($id, $this->cache);
114
-    }
115
-
116
-    /**
117
-     * Combine new result set with existing attributes in
118
-     * cache.
119
-     *
120
-     * @param array $entities
121
-     *
122
-     * @return void
123
-     */
124
-    protected function mergeCacheResults($entities)
125
-    {
126
-        foreach ($entities as $key => $entity) {
127
-            $this->cache[$key] = $entity;
128
-        }
129
-    }
130
-
131
-    /**
132
-     * Cache Relation's query result for an entity.
133
-     *
134
-     * @param string       $key          primary key of the cached entity
135
-     * @param string       $relation     name of the relation
136
-     * @param mixed        $results      results of the relationship's query
137
-     * @param Relationship $relationship
138
-     *
139
-     * @throws MappingException
140
-     *
141
-     * @return void
142
-     */
143
-    public function cacheLoadedRelationResult($key, $relation, $results, Relationship $relationship)
144
-    {
145
-        if ($results instanceof EntityCollection) {
146
-            $this->cacheManyRelationResults($key, $relation, $results, $relationship);
147
-        }
148
-
149
-        // TODO : As we support popo Entities, Maybe this check isn't needed anymore,
150
-        // or we have to check that $result is an object instead
151
-        if ($results instanceof Mappable) {
152
-            $this->cacheSingleRelationResult($key, $relation, $results, $relationship);
153
-        }
154
-    }
155
-
156
-    /**
157
-     * Create a cachedRelationship instance which will hold related entity's hash and pivot attributes, if any.
158
-     *
159
-     * @param string       $parentKey
160
-     * @param string       $relation
161
-     * @param array        $result
162
-     * @param Relationship $relationship
163
-     *
164
-     * @throws MappingException
165
-     *
166
-     * @return CachedRelationship
167
-     */
168
-    protected function getCachedRelationship($parentKey, $relation, $result, Relationship $relationship)
169
-    {
170
-        $pivotColumns = $relationship->getPivotAttributes();
171
-
172
-        if (!array_key_exists($relation, $this->pivotAttributes)) {
173
-            $this->pivotAttributes[$relation] = $pivotColumns;
174
-        }
175
-
176
-        $wrapper = $this->factory->make($result);
177
-
178
-        $hash = $wrapper->getEntityHash();
179
-
180
-        if (count($pivotColumns) > 0) {
181
-            $pivotAttributes = [];
182
-            foreach ($pivotColumns as $column) {
183
-                $pivot = $wrapper->getEntityAttribute('pivot');
184
-
185
-                $pivotWrapper = $this->factory->make($pivot);
186
-
187
-                $pivotAttributes[$column] = $pivotWrapper->getEntityAttribute($column);
188
-            }
189
-
190
-            $cachedRelationship = new CachedRelationship($hash, $pivotAttributes);
191
-        } else {
192
-            $cachedRelationship = new CachedRelationship($hash);
193
-        }
194
-
195
-        return $cachedRelationship;
196
-    }
197
-
198
-    /**
199
-     * Cache a many relationship.
200
-     *
201
-     * @param                  $parentKey
202
-     * @param string           $relation
203
-     * @param EntityCollection $results
204
-     * @param Relationship     $relationship
205
-     *
206
-     * @throws MappingException
207
-     */
208
-    protected function cacheManyRelationResults($parentKey, $relation, $results, Relationship $relationship)
209
-    {
210
-        $this->cache[$parentKey][$relation] = [];
211
-
212
-        foreach ($results as $result) {
213
-            $cachedRelationship = $this->getCachedRelationship($parentKey, $relation, $result, $relationship);
214
-
215
-            $relatedHash = $cachedRelationship->getHash();
216
-
217
-            $this->cache[$parentKey][$relation][$relatedHash] = $cachedRelationship;
218
-        }
219
-    }
220
-
221
-    /**
222
-     * Cache a single relationship.
223
-     *
224
-     * @param              $parentKey
225
-     * @param string       $relation
226
-     * @param Mappable     $result
227
-     * @param Relationship $relationship
228
-     *
229
-     * @throws MappingException
230
-     */
231
-    protected function cacheSingleRelationResult($parentKey, $relation, $result, Relationship $relationship)
232
-    {
233
-        $this->cache[$parentKey][$relation] = $this->getCachedRelationship($parentKey, $relation, $result, $relationship);
234
-    }
235
-
236
-    /**
237
-     * Get Entity's Hash.
238
-     *
239
-     * @param  $entity
240
-     *
241
-     * @throws MappingException
242
-     *
243
-     * @return string
244
-     */
245
-    protected function getEntityHash(InternallyMappable $entity)
246
-    {
247
-        $class = $entity->getEntityClass();
248
-
249
-        $mapper = Manager::getMapper($class);
250
-
251
-        $keyName = $mapper->getEntityMap()->getKeyName();
252
-
253
-        return $class.'.'.$entity->getEntityAttribute($keyName);
254
-    }
255
-
256
-    /**
257
-     * Refresh the cache record for an aggregated entity after a write operation.
258
-     *
259
-     * @param Aggregate $entity
260
-     */
261
-    public function refresh(Aggregate $entity)
262
-    {
263
-        $this->cache[$entity->getEntityId()] = $this->transform($entity);
264
-    }
265
-
266
-    /**
267
-     * Transform an Aggregated Entity into a cache record.
268
-     *
269
-     * @param Aggregate $aggregatedEntity
270
-     *
271
-     * @throws MappingException
272
-     *
273
-     * @return array
274
-     */
275
-    protected function transform(Aggregate $aggregatedEntity)
276
-    {
277
-        $baseAttributes = $aggregatedEntity->getRawAttributes();
278
-
279
-        $relationAttributes = [];
280
-
281
-        // First we'll handle each relationships that are a one to one
282
-        // relation, and which will be saved as a CachedRelationship
283
-        // object inside the cache.
284
-
285
-        // NOTE : storing localRelationships maybe useless has we store
286
-        // the foreign key in the attributes already.
287
-
288
-        foreach ($this->entityMap->getSingleRelationships() as $relation) {
289
-            $aggregates = $aggregatedEntity->getRelationship($relation);
290
-
291
-            if (count($aggregates) == 1) {
292
-                $related = $aggregates[0];
293
-                $relationAttributes[$relation] = new CachedRelationship($related->getEntityHash());
294
-            }
295
-            if (count($aggregates) > 1) {
296
-                throw new MappingException("Single Relationship '$relation' contains several related entities");
297
-            }
298
-        }
299
-
300
-        // Then we'll handle the 'many' relationships and store them as
301
-        // an array of CachedRelationship objects.
302
-
303
-        foreach ($this->entityMap->getManyRelationships() as $relation) {
304
-            $aggregates = $aggregatedEntity->getRelationship($relation);
305
-
306
-            $relationAttributes[$relation] = [];
307
-
308
-            foreach ($aggregates as $aggregate) {
309
-                $relationAttributes[$relation][] = new CachedRelationship(
310
-                    $aggregate->getEntityHash(),
311
-                    $aggregate->getPivotAttributes()
312
-                );
313
-            }
314
-        }
315
-
316
-        return $baseAttributes + $relationAttributes;
317
-    }
318
-
319
-    /**
320
-     * Get pivot attributes for a relation.
321
-     *
322
-     * @param string             $relation
323
-     * @param InternallyMappable $entity
324
-     *
325
-     * @return array
326
-     */
327
-    protected function getPivotValues($relation, InternallyMappable $entity)
328
-    {
329
-        $values = [];
330
-
331
-        $entityAttributes = $entity->getEntityAttributes();
332
-
333
-        if (array_key_exists($relation, $this->pivotAttributes)) {
334
-            foreach ($this->pivotAttributes[$relation] as $attribute) {
335
-                if (array_key_exists($attribute, $entityAttributes)) {
336
-                    $values[$attribute] = $entity->getEntityAttribute('pivot')->$attribute;
337
-                }
338
-            }
339
-        }
340
-
341
-        return $values;
342
-    }
343
-
344
-    /**
345
-     * Clear the entity Cache. Use with caution as it could result
346
-     * in impredictable behaviour if the cached entities are stored
347
-     * after the cache clear operation.
348
-     *
349
-     * @return void
350
-     */
351
-    public function clear()
352
-    {
353
-        $this->cache = [];
354
-        $this->pivotAttributes = [];
355
-    }
18
+	/**
19
+	 * Entity's raw attributes/relationships.
20
+	 *
21
+	 * @var array
22
+	 */
23
+	protected $cache = [];
24
+
25
+	/**
26
+	 * Entity Map for the current Entity Type.
27
+	 *
28
+	 * @var \Analogue\ORM\EntityMap
29
+	 */
30
+	protected $entityMap;
31
+
32
+	/**
33
+	 * Wrapper factory.
34
+	 *
35
+	 * @var \Analogue\ORM\System\Wrappers\Factory
36
+	 */
37
+	protected $factory;
38
+
39
+	/**
40
+	 * Associative array containing list of pivot attributes per relationship
41
+	 * so we don't have to call relationship method on refresh.
42
+	 *
43
+	 * @var array
44
+	 */
45
+	protected $pivotAttributes = [];
46
+
47
+	/**
48
+	 * EntityCache constructor.
49
+	 *
50
+	 * @param EntityMap $entityMap
51
+	 */
52
+	public function __construct(EntityMap $entityMap)
53
+	{
54
+		$this->entityMap = $entityMap;
55
+
56
+		$this->factory = new Factory();
57
+	}
58
+
59
+	/**
60
+	 * Add an array of key=>attributes representing
61
+	 * the initial state of loaded entities.
62
+	 *
63
+	 * @param array $results
64
+	 */
65
+	public function add(array $results)
66
+	{
67
+		$cachedResults = [];
68
+
69
+		$keyColumn = $this->entityMap->getKeyName();
70
+
71
+		foreach ($results as $result) {
72
+			$id = $result[$keyColumn];
73
+
74
+			// Forget the ID field from the cache attributes
75
+			// to prevent any side effect.
76
+			// TODO : remove primary key check from dirty attributes parsing
77
+			//unset($result[$keyColumn]);
78
+			$cachedResults[$id] = $result;
79
+		}
80
+
81
+		if (count($this->cache) == 0) {
82
+			$this->cache = $cachedResults;
83
+		} else {
84
+			$this->mergeCacheResults($cachedResults);
85
+		}
86
+	}
87
+
88
+	/**
89
+	 * Retrieve initial attributes for a single entity.
90
+	 *
91
+	 * @param string $id
92
+	 *
93
+	 * @return array
94
+	 */
95
+	public function get($id)
96
+	{
97
+		if ($this->has($id)) {
98
+			return $this->cache[$id];
99
+		} else {
100
+			return [];
101
+		}
102
+	}
103
+
104
+	/**
105
+	 * Check if a record for this id exists.
106
+	 *
107
+	 * @param string $id
108
+	 *
109
+	 * @return bool
110
+	 */
111
+	public function has($id)
112
+	{
113
+		return array_key_exists($id, $this->cache);
114
+	}
115
+
116
+	/**
117
+	 * Combine new result set with existing attributes in
118
+	 * cache.
119
+	 *
120
+	 * @param array $entities
121
+	 *
122
+	 * @return void
123
+	 */
124
+	protected function mergeCacheResults($entities)
125
+	{
126
+		foreach ($entities as $key => $entity) {
127
+			$this->cache[$key] = $entity;
128
+		}
129
+	}
130
+
131
+	/**
132
+	 * Cache Relation's query result for an entity.
133
+	 *
134
+	 * @param string       $key          primary key of the cached entity
135
+	 * @param string       $relation     name of the relation
136
+	 * @param mixed        $results      results of the relationship's query
137
+	 * @param Relationship $relationship
138
+	 *
139
+	 * @throws MappingException
140
+	 *
141
+	 * @return void
142
+	 */
143
+	public function cacheLoadedRelationResult($key, $relation, $results, Relationship $relationship)
144
+	{
145
+		if ($results instanceof EntityCollection) {
146
+			$this->cacheManyRelationResults($key, $relation, $results, $relationship);
147
+		}
148
+
149
+		// TODO : As we support popo Entities, Maybe this check isn't needed anymore,
150
+		// or we have to check that $result is an object instead
151
+		if ($results instanceof Mappable) {
152
+			$this->cacheSingleRelationResult($key, $relation, $results, $relationship);
153
+		}
154
+	}
155
+
156
+	/**
157
+	 * Create a cachedRelationship instance which will hold related entity's hash and pivot attributes, if any.
158
+	 *
159
+	 * @param string       $parentKey
160
+	 * @param string       $relation
161
+	 * @param array        $result
162
+	 * @param Relationship $relationship
163
+	 *
164
+	 * @throws MappingException
165
+	 *
166
+	 * @return CachedRelationship
167
+	 */
168
+	protected function getCachedRelationship($parentKey, $relation, $result, Relationship $relationship)
169
+	{
170
+		$pivotColumns = $relationship->getPivotAttributes();
171
+
172
+		if (!array_key_exists($relation, $this->pivotAttributes)) {
173
+			$this->pivotAttributes[$relation] = $pivotColumns;
174
+		}
175
+
176
+		$wrapper = $this->factory->make($result);
177
+
178
+		$hash = $wrapper->getEntityHash();
179
+
180
+		if (count($pivotColumns) > 0) {
181
+			$pivotAttributes = [];
182
+			foreach ($pivotColumns as $column) {
183
+				$pivot = $wrapper->getEntityAttribute('pivot');
184
+
185
+				$pivotWrapper = $this->factory->make($pivot);
186
+
187
+				$pivotAttributes[$column] = $pivotWrapper->getEntityAttribute($column);
188
+			}
189
+
190
+			$cachedRelationship = new CachedRelationship($hash, $pivotAttributes);
191
+		} else {
192
+			$cachedRelationship = new CachedRelationship($hash);
193
+		}
194
+
195
+		return $cachedRelationship;
196
+	}
197
+
198
+	/**
199
+	 * Cache a many relationship.
200
+	 *
201
+	 * @param                  $parentKey
202
+	 * @param string           $relation
203
+	 * @param EntityCollection $results
204
+	 * @param Relationship     $relationship
205
+	 *
206
+	 * @throws MappingException
207
+	 */
208
+	protected function cacheManyRelationResults($parentKey, $relation, $results, Relationship $relationship)
209
+	{
210
+		$this->cache[$parentKey][$relation] = [];
211
+
212
+		foreach ($results as $result) {
213
+			$cachedRelationship = $this->getCachedRelationship($parentKey, $relation, $result, $relationship);
214
+
215
+			$relatedHash = $cachedRelationship->getHash();
216
+
217
+			$this->cache[$parentKey][$relation][$relatedHash] = $cachedRelationship;
218
+		}
219
+	}
220
+
221
+	/**
222
+	 * Cache a single relationship.
223
+	 *
224
+	 * @param              $parentKey
225
+	 * @param string       $relation
226
+	 * @param Mappable     $result
227
+	 * @param Relationship $relationship
228
+	 *
229
+	 * @throws MappingException
230
+	 */
231
+	protected function cacheSingleRelationResult($parentKey, $relation, $result, Relationship $relationship)
232
+	{
233
+		$this->cache[$parentKey][$relation] = $this->getCachedRelationship($parentKey, $relation, $result, $relationship);
234
+	}
235
+
236
+	/**
237
+	 * Get Entity's Hash.
238
+	 *
239
+	 * @param  $entity
240
+	 *
241
+	 * @throws MappingException
242
+	 *
243
+	 * @return string
244
+	 */
245
+	protected function getEntityHash(InternallyMappable $entity)
246
+	{
247
+		$class = $entity->getEntityClass();
248
+
249
+		$mapper = Manager::getMapper($class);
250
+
251
+		$keyName = $mapper->getEntityMap()->getKeyName();
252
+
253
+		return $class.'.'.$entity->getEntityAttribute($keyName);
254
+	}
255
+
256
+	/**
257
+	 * Refresh the cache record for an aggregated entity after a write operation.
258
+	 *
259
+	 * @param Aggregate $entity
260
+	 */
261
+	public function refresh(Aggregate $entity)
262
+	{
263
+		$this->cache[$entity->getEntityId()] = $this->transform($entity);
264
+	}
265
+
266
+	/**
267
+	 * Transform an Aggregated Entity into a cache record.
268
+	 *
269
+	 * @param Aggregate $aggregatedEntity
270
+	 *
271
+	 * @throws MappingException
272
+	 *
273
+	 * @return array
274
+	 */
275
+	protected function transform(Aggregate $aggregatedEntity)
276
+	{
277
+		$baseAttributes = $aggregatedEntity->getRawAttributes();
278
+
279
+		$relationAttributes = [];
280
+
281
+		// First we'll handle each relationships that are a one to one
282
+		// relation, and which will be saved as a CachedRelationship
283
+		// object inside the cache.
284
+
285
+		// NOTE : storing localRelationships maybe useless has we store
286
+		// the foreign key in the attributes already.
287
+
288
+		foreach ($this->entityMap->getSingleRelationships() as $relation) {
289
+			$aggregates = $aggregatedEntity->getRelationship($relation);
290
+
291
+			if (count($aggregates) == 1) {
292
+				$related = $aggregates[0];
293
+				$relationAttributes[$relation] = new CachedRelationship($related->getEntityHash());
294
+			}
295
+			if (count($aggregates) > 1) {
296
+				throw new MappingException("Single Relationship '$relation' contains several related entities");
297
+			}
298
+		}
299
+
300
+		// Then we'll handle the 'many' relationships and store them as
301
+		// an array of CachedRelationship objects.
302
+
303
+		foreach ($this->entityMap->getManyRelationships() as $relation) {
304
+			$aggregates = $aggregatedEntity->getRelationship($relation);
305
+
306
+			$relationAttributes[$relation] = [];
307
+
308
+			foreach ($aggregates as $aggregate) {
309
+				$relationAttributes[$relation][] = new CachedRelationship(
310
+					$aggregate->getEntityHash(),
311
+					$aggregate->getPivotAttributes()
312
+				);
313
+			}
314
+		}
315
+
316
+		return $baseAttributes + $relationAttributes;
317
+	}
318
+
319
+	/**
320
+	 * Get pivot attributes for a relation.
321
+	 *
322
+	 * @param string             $relation
323
+	 * @param InternallyMappable $entity
324
+	 *
325
+	 * @return array
326
+	 */
327
+	protected function getPivotValues($relation, InternallyMappable $entity)
328
+	{
329
+		$values = [];
330
+
331
+		$entityAttributes = $entity->getEntityAttributes();
332
+
333
+		if (array_key_exists($relation, $this->pivotAttributes)) {
334
+			foreach ($this->pivotAttributes[$relation] as $attribute) {
335
+				if (array_key_exists($attribute, $entityAttributes)) {
336
+					$values[$attribute] = $entity->getEntityAttribute('pivot')->$attribute;
337
+				}
338
+			}
339
+		}
340
+
341
+		return $values;
342
+	}
343
+
344
+	/**
345
+	 * Clear the entity Cache. Use with caution as it could result
346
+	 * in impredictable behaviour if the cached entities are stored
347
+	 * after the cache clear operation.
348
+	 *
349
+	 * @return void
350
+	 */
351
+	public function clear()
352
+	{
353
+		$this->cache = [];
354
+		$this->pivotAttributes = [];
355
+	}
356 356
 }
Please login to merge, or discard this patch.
src/System/Proxies/CollectionProxy.php 2 patches
Doc Comments   +1 added lines, -3 removed lines patch added patch discarded remove patch
@@ -217,10 +217,8 @@
 block discarded – undo
217 217
     /**
218 218
      * Create a new collection consisting of every n-th element.
219 219
      *
220
-     * @param int $step
221
-     * @param int $offset
222 220
      *
223
-     * @return static
221
+     * @return boolean
224 222
      */
225 223
     public function every($key, $operator = null, $value = null)
226 224
     {
Please login to merge, or discard this patch.
Indentation   +1282 added lines, -1282 removed lines patch added patch discarded remove patch
@@ -9,1286 +9,1286 @@
 block discarded – undo
9 9
 
10 10
 class CollectionProxy extends EntityCollection implements ProxyInterface
11 11
 {
12
-    /**
13
-     * Indicate if the relationship has been lazy loaded.
14
-     *
15
-     * @var bool
16
-     */
17
-    protected $relationshipLoaded = false;
18
-
19
-    protected $addedItems = [];
20
-
21
-    /**
22
-     * Create a new collection.
23
-     *
24
-     * @param mixed  $entity
25
-     * @param string $relation
26
-     *
27
-     * @return void
28
-     */
29
-    public function __construct($entity, $relation)
30
-    {
31
-        $this->parentEntity = $entity;
32
-        $this->relationshipMethod = $relation;
33
-    }
34
-
35
-    /**
36
-     * Return Items that has been added without lady loading
37
-     * the underlying collection.
38
-     *
39
-     * @return array
40
-     */
41
-    public function getAddedItems()
42
-    {
43
-        return $this->addedItems;
44
-    }
45
-
46
-    /**
47
-     * Force initialization of the proxy.
48
-     *
49
-     * @return bool true if the proxy could be initialized
50
-     */
51
-    public function initializeProxy() : bool
52
-    {
53
-        if ($this->isProxyInitialized()) {
54
-            return true;
55
-        }
56
-
57
-        $relation = $this->relationshipMethod;
58
-        $entity = $this->parentEntity;
59
-
60
-        $entityMap = Manager::getMapper($entity)->getEntityMap();
61
-
62
-        $this->items = $entityMap->$relation($entity)->getResults($relation)->all() + $this->addedItems;
63
-
64
-        $this->relationshipLoaded = true;
65
-
66
-        return true;
67
-    }
68
-
69
-    /**
70
-     * Retrieves current initialization status of the proxy.
71
-     *
72
-     * @return bool
73
-     */
74
-    public function isProxyInitialized() : bool
75
-    {
76
-        return $this->relationshipLoaded;
77
-    }
78
-
79
-    /**
80
-     * Get all of the items in the collection.
81
-     *
82
-     * @return array
83
-     */
84
-    public function all()
85
-    {
86
-        $this->initializeProxy();
87
-
88
-        return parent::all();
89
-    }
90
-
91
-    /**
92
-     * Get the average value of a given key.
93
-     *
94
-     * @param callable|string|null $callback
95
-     *
96
-     * @return mixed
97
-     */
98
-    public function avg($callback = null)
99
-    {
100
-        $this->initializeProxy();
101
-
102
-        return parent::avg($callback);
103
-    }
104
-
105
-    /**
106
-     * Get the median of a given key.
107
-     *
108
-     * @param null $key
109
-     *
110
-     * @return mixed|null
111
-     */
112
-    public function median($key = null)
113
-    {
114
-        $this->initializeProxy();
115
-
116
-        return parent::median($key);
117
-    }
118
-
119
-    /**
120
-     * Get the mode of a given key.
121
-     *
122
-     * @param mixed $key
123
-     *
124
-     * @return array
125
-     */
126
-    public function mode($key = null)
127
-    {
128
-        $this->initializeProxy();
129
-
130
-        return parent::mode($key);
131
-    }
132
-
133
-    /**
134
-     * Collapse the collection of items into a single array.
135
-     *
136
-     * @return static
137
-     */
138
-    public function collapse()
139
-    {
140
-        $this->initializeProxy();
141
-
142
-        return parent::collapse();
143
-    }
144
-
145
-    /**
146
-     * Determine if an item exists in the collection.
147
-     *
148
-     * @param mixed $key
149
-     * @param mixed $value
150
-     *
151
-     * @return bool
152
-     */
153
-    public function contains($key, $operator = null, $value = null)
154
-    {
155
-        $this->initializeProxy();
156
-
157
-        return parent::contains($key, $operator, $value);
158
-    }
159
-
160
-    /**
161
-     * Determine if an item exists in the collection using strict comparison.
162
-     *
163
-     * @param mixed $key
164
-     * @param mixed $value
165
-     *
166
-     * @return bool
167
-     */
168
-    public function containsStrict($key, $value = null)
169
-    {
170
-        $this->initializeProxy();
171
-
172
-        return parent::containsStrict($key, $value);
173
-    }
174
-
175
-    /**
176
-     * Get the items in the collection that are not present in the given items.
177
-     *
178
-     * @param mixed $items
179
-     *
180
-     * @return static
181
-     */
182
-    public function diff($items)
183
-    {
184
-        $this->initializeProxy();
185
-
186
-        return parent::diff($items);
187
-    }
188
-
189
-    /**
190
-     * Get the items in the collection whose keys are not present in the given items.
191
-     *
192
-     * @param mixed $items
193
-     *
194
-     * @return static
195
-     */
196
-    public function diffKeys($items)
197
-    {
198
-        $this->initializeProxy();
199
-
200
-        return parent::diffKeys($items);
201
-    }
202
-
203
-    /**
204
-     * Execute a callback over each item.
205
-     *
206
-     * @param callable $callback
207
-     *
208
-     * @return $this
209
-     */
210
-    public function each(callable $callback)
211
-    {
212
-        $this->initializeProxy();
213
-
214
-        return parent::each($callback);
215
-    }
216
-
217
-    /**
218
-     * Create a new collection consisting of every n-th element.
219
-     *
220
-     * @param int $step
221
-     * @param int $offset
222
-     *
223
-     * @return static
224
-     */
225
-    public function every($key, $operator = null, $value = null)
226
-    {
227
-        $this->initializeProxy();
228
-
229
-        return parent::every($key, $operator, $value);
230
-    }
231
-
232
-    /**
233
-     * Get all items except for those with the specified keys.
234
-     *
235
-     * @param mixed $keys
236
-     *
237
-     * @return static
238
-     */
239
-    public function except($keys)
240
-    {
241
-        $this->initializeProxy();
242
-
243
-        return parent::except($keys);
244
-    }
245
-
246
-    /**
247
-     * Run a filter over each of the items.
248
-     *
249
-     * @param callable|null $callback
250
-     *
251
-     * @return static
252
-     */
253
-    public function filter(callable $callback = null)
254
-    {
255
-        $this->initializeProxy();
256
-
257
-        return parent::filter($callback);
258
-    }
259
-
260
-    /**
261
-     * Filter items by the given key value pair.
262
-     *
263
-     * @param string $key
264
-     * @param mixed  $operator
265
-     * @param mixed  $value
266
-     *
267
-     * @return static
268
-     */
269
-    public function where($key, $operator, $value = null)
270
-    {
271
-        $this->initializeProxy();
272
-
273
-        return parent::where($key, $operator, $value);
274
-    }
275
-
276
-    /**
277
-     * Filter items by the given key value pair using strict comparison.
278
-     *
279
-     * @param string $key
280
-     * @param mixed  $value
281
-     *
282
-     * @return static
283
-     */
284
-    public function whereStrict($key, $value)
285
-    {
286
-        $this->initializeProxy();
287
-
288
-        return parent::whereStrict($key, $value);
289
-    }
290
-
291
-    /**
292
-     * Filter items by the given key value pair.
293
-     *
294
-     * @param string $key
295
-     * @param mixed  $values
296
-     * @param bool   $strict
297
-     *
298
-     * @return static
299
-     */
300
-    public function whereIn($key, $values, $strict = false)
301
-    {
302
-        $this->initializeProxy();
303
-
304
-        return parent::whereIn($key, $values, $strict);
305
-    }
306
-
307
-    /**
308
-     * Filter items by the given key value pair using strict comparison.
309
-     *
310
-     * @param string $key
311
-     * @param mixed  $values
312
-     *
313
-     * @return static
314
-     */
315
-    public function whereInStrict($key, $values)
316
-    {
317
-        $this->initializeProxy();
318
-
319
-        return parent::whereInStrict($key, $values);
320
-    }
321
-
322
-    /**
323
-     * Get the first item from the collection.
324
-     *
325
-     * @param callable|null $callback
326
-     * @param mixed         $default
327
-     *
328
-     * @return mixed
329
-     */
330
-    public function first(callable $callback = null, $default = null)
331
-    {
332
-        // TODO Consider partial loading
333
-        $this->initializeProxy();
334
-
335
-        return parent::first($callback, $default);
336
-    }
337
-
338
-    /**
339
-     * Get a flattened array of the items in the collection.
340
-     *
341
-     * @param int $depth
342
-     *
343
-     * @return static
344
-     */
345
-    public function flatten($depth = INF)
346
-    {
347
-        $this->initializeProxy();
348
-
349
-        return parent::flatten($depth);
350
-    }
351
-
352
-    /**
353
-     * Flip the items in the collection.
354
-     *
355
-     * @return static
356
-     */
357
-    public function flip()
358
-    {
359
-        $this->initializeProxy();
360
-
361
-        return parent::flip();
362
-    }
363
-
364
-    /**
365
-     * Remove an item from the collection by key.
366
-     *
367
-     * @param string|array $keys
368
-     *
369
-     * @return $this
370
-     */
371
-    public function forget($keys)
372
-    {
373
-        // TODO, we could consider these as
374
-        // 'pending deletion', the same way that
375
-        // we treat added items
376
-        $this->initializeProxy();
377
-
378
-        return parent::forget($keys);
379
-    }
380
-
381
-    /**
382
-     * Get an item from the collection by key.
383
-     *
384
-     * @param mixed $key
385
-     * @param mixed $default
386
-     *
387
-     * @return mixed
388
-     */
389
-    public function get($key, $default = null)
390
-    {
391
-        // TODO : We could also consider partial loading
392
-        // here
393
-        $this->initializeProxy();
394
-
395
-        return parent::get($key, $default);
396
-    }
397
-
398
-    /**
399
-     * Group an associative array by a field or using a callback.
400
-     *
401
-     * @param callable|string $groupBy
402
-     * @param bool            $preserveKeys
403
-     *
404
-     * @return static
405
-     */
406
-    public function groupBy($groupBy, $preserveKeys = false)
407
-    {
408
-        $this->initializeProxy();
409
-
410
-        return parent::groupBy($groupBy, $preserveKeys);
411
-    }
412
-
413
-    /**
414
-     * Key an associative array by a field or using a callback.
415
-     *
416
-     * @param callable|string $keyBy
417
-     *
418
-     * @return static
419
-     */
420
-    public function keyBy($keyBy)
421
-    {
422
-        $this->initializeProxy();
423
-
424
-        return parent::keyBy($keyBy);
425
-    }
426
-
427
-    /**
428
-     * Determine if an item exists in the collection by key.
429
-     *
430
-     * @param mixed $key
431
-     *
432
-     * @return bool
433
-     */
434
-    public function has($key)
435
-    {
436
-        // TODO : we could do automagic here by directly
437
-        // calling the database if the collection hasn't
438
-        // been initialized yet.
439
-        // Potential issue is that several calls to this
440
-        // could cause a lot queries vs a single get query.
441
-        $this->initializeProxy();
442
-
443
-        return parent::has($key);
444
-    }
445
-
446
-    /**
447
-     * Concatenate values of a given key as a string.
448
-     *
449
-     * @param string $value
450
-     * @param string $glue
451
-     *
452
-     * @return string
453
-     */
454
-    public function implode($value, $glue = null)
455
-    {
456
-        $this->initializeProxy();
457
-
458
-        return parent::implode($value, $glue);
459
-    }
460
-
461
-    /**
462
-     * Intersect the collection with the given items.
463
-     *
464
-     * @param mixed $items
465
-     *
466
-     * @return static
467
-     */
468
-    public function intersect($items)
469
-    {
470
-        $this->initializeProxy();
471
-
472
-        return parent::intersect($items);
473
-    }
474
-
475
-    /**
476
-     * Determine if the collection is empty or not.
477
-     *
478
-     * @return bool
479
-     */
480
-    public function isEmpty()
481
-    {
482
-        $this->initializeProxy();
483
-
484
-        return parent::isEmpty();
485
-    }
486
-
487
-    /**
488
-     * Get the keys of the collection items.
489
-     *
490
-     * @return static
491
-     */
492
-    public function keys()
493
-    {
494
-        $this->initializeProxy();
495
-
496
-        return parent::keys();
497
-    }
498
-
499
-    /**
500
-     * Get the last item from the collection.
501
-     *
502
-     * @param callable|null $callback
503
-     * @param mixed         $default
504
-     *
505
-     * @return mixed
506
-     */
507
-    public function last(callable $callback = null, $default = null)
508
-    {
509
-        // TODO : we could do partial loading there as well
510
-        $this->initializeProxy();
511
-
512
-        return parent::last($callback, $default);
513
-    }
514
-
515
-    /**
516
-     * Get the values of a given key.
517
-     *
518
-     * @param string      $value
519
-     * @param string|null $key
520
-     *
521
-     * @return static
522
-     */
523
-    public function pluck($value, $key = null)
524
-    {
525
-        // TODO : automagic call to QB if not initialized
526
-        $this->initializeProxy();
527
-
528
-        return parent::pluck($value, $key);
529
-    }
530
-
531
-    /**
532
-     * Run a map over each of the items.
533
-     *
534
-     * @param callable $callback
535
-     *
536
-     * @return static
537
-     */
538
-    public function map(callable $callback)
539
-    {
540
-        $this->initializeProxy();
541
-
542
-        return parent::map($callback);
543
-    }
544
-
545
-    /**
546
-     * Run an associative map over each of the items.
547
-     *
548
-     * The callback should return an associative array with a single key/value pair.
549
-     *
550
-     * @param callable $callback
551
-     *
552
-     * @return static
553
-     */
554
-    public function mapWithKeys(callable $callback)
555
-    {
556
-        $this->initializeProxy();
557
-
558
-        return parent::mapWithKeys($callback);
559
-    }
560
-
561
-    /**
562
-     * Map a collection and flatten the result by a single level.
563
-     *
564
-     * @param callable $callback
565
-     *
566
-     * @return static
567
-     */
568
-    public function flatMap(callable $callback)
569
-    {
570
-        $this->initializeProxy();
571
-
572
-        return parent::flatMap($callback);
573
-    }
574
-
575
-    /**
576
-     * Get the max value of a given key.
577
-     *
578
-     * @param callable|string|null $callback
579
-     *
580
-     * @return mixed
581
-     */
582
-    public function max($callback = null)
583
-    {
584
-        $this->initializeProxy();
585
-
586
-        return parent::max($callback);
587
-    }
588
-
589
-    /**
590
-     * Merge the collection with the given items.
591
-     *
592
-     * @param mixed $items
593
-     *
594
-     * @return static
595
-     */
596
-    public function merge($items)
597
-    {
598
-        // TODO : Check if the EntityCollection
599
-        // returns a native Collection, as it
600
-        // is what we want here
601
-        $this->initializeProxy();
602
-
603
-        return parent::merge($items);
604
-    }
605
-
606
-    /**
607
-     * Create a collection by using this collection for keys and another for its values.
608
-     *
609
-     * @param mixed $values
610
-     *
611
-     * @return static
612
-     */
613
-    public function combine($values)
614
-    {
615
-        // TODO : Check if the EntityCollection
616
-        // returns a native Collection, as it
617
-        // is what we want here
618
-        $this->initializeProxy();
619
-
620
-        return parent::combine($values);
621
-    }
622
-
623
-    /**
624
-     * Union the collection with the given items.
625
-     *
626
-     * @param mixed $items
627
-     *
628
-     * @return static
629
-     */
630
-    public function union($items)
631
-    {
632
-        // TODO : Check if the EntityCollection
633
-        // returns a native Collection, as it
634
-        // is what we want here
635
-        $this->initializeProxy();
636
-
637
-        return parent::union($items);
638
-    }
639
-
640
-    /**
641
-     * Get the min value of a given key.
642
-     *
643
-     * @param callable|string|null $callback
644
-     *
645
-     * @return mixed
646
-     */
647
-    public function min($callback = null)
648
-    {
649
-        // TODO : we could rely on the QB
650
-        // for thos, if initialization has not
651
-        // take place yet
652
-        $this->initializeProxy();
653
-
654
-        return parent::min($callback);
655
-    }
656
-
657
-    /**
658
-     * Create a new collection consisting of every n-th element.
659
-     *
660
-     * @param int $step
661
-     * @param int $offset
662
-     *
663
-     * @return static
664
-     */
665
-    public function nth($step, $offset = 0)
666
-    {
667
-        $this->initializeProxy();
668
-
669
-        return parent::nth($step, $offset);
670
-    }
671
-
672
-    /**
673
-     * Get the items with the specified keys.
674
-     *
675
-     * @param mixed $keys
676
-     *
677
-     * @return static
678
-     */
679
-    public function only($keys)
680
-    {
681
-        // TODO : we could rely on the QB if
682
-        // the collection hasn't been initialized yet
683
-        $this->initializeProxy();
684
-
685
-        return parent::only($keys);
686
-    }
687
-
688
-    /**
689
-     * "Paginate" the collection by slicing it into a smaller collection.
690
-     *
691
-     * @param int $page
692
-     * @param int $perPage
693
-     *
694
-     * @return static
695
-     */
696
-    public function forPage($page, $perPage)
697
-    {
698
-        // TODO : check possibility of partial loading
699
-        // if not initialized
700
-        $this->initializeProxy();
701
-
702
-        return parent::forPage($page, $perPage);
703
-    }
704
-
705
-    /**
706
-     * Partition the collection into two arrays using the given callback or key.
707
-     *
708
-     * @param callable|string $callback
709
-     *
710
-     * @return static
711
-     */
712
-    public function partition($callback)
713
-    {
714
-        $this->initializeProxy();
715
-
716
-        return parent::partition($callback);
717
-    }
718
-
719
-    /**
720
-     * Pass the collection to the given callback and return the result.
721
-     *
722
-     * @param callable $callback
723
-     *
724
-     * @return mixed
725
-     */
726
-    public function pipe(callable $callback)
727
-    {
728
-        $this->initializeProxy();
729
-
730
-        return parent::pipe($callback);
731
-    }
732
-
733
-    /**
734
-     * Get and remove the last item from the collection.
735
-     *
736
-     * @return mixed
737
-     */
738
-    public function pop()
739
-    {
740
-        $this->initializeProxy();
741
-
742
-        return parent::pop();
743
-    }
744
-
745
-    /**
746
-     * Push an item onto the beginning of the collection.
747
-     *
748
-     * @param mixed $value
749
-     * @param mixed $key
750
-     *
751
-     * @return $this
752
-     */
753
-    public function prepend($value, $key = null)
754
-    {
755
-        // TODO : partial adding of values.
756
-        // we could have a $prepended , and $pushed arrays
757
-        // which we would combine at full initialization
758
-
759
-        $this->initializeProxy();
760
-
761
-        return parent::prepend($value, $key);
762
-    }
763
-
764
-    /**
765
-     * Push an item onto the end of the collection.
766
-     *
767
-     * @param mixed $value
768
-     *
769
-     * @return $this
770
-     */
771
-    public function push($value)
772
-    {
773
-        // TODO : partial adding of values.
774
-        // we could have a $prepended , and $pushed arrays
775
-        // which we would combine at full initialization
776
-
777
-        $this->initializeProxy();
778
-
779
-        return parent::push($value);
780
-    }
781
-
782
-    /**
783
-     * Get and remove an item from the collection.
784
-     *
785
-     * @param mixed $key
786
-     * @param mixed $default
787
-     *
788
-     * @return mixed
789
-     */
790
-    public function pull($key, $default = null)
791
-    {
792
-        // TODO : QB query if the collection
793
-        // hasn't been initialized yet
794
-
795
-        $this->initializeProxy();
796
-
797
-        return parent::pull($key, $default);
798
-    }
799
-
800
-    /**
801
-     * Put an item in the collection by key.
802
-     *
803
-     * @param mixed $key
804
-     * @param mixed $value
805
-     *
806
-     * @return $this
807
-     */
808
-    public function put($key, $value)
809
-    {
810
-        // TODO : Partial loading ?
811
-
812
-        $this->initializeProxy();
813
-
814
-        return parent::put($key, $value);
815
-    }
816
-
817
-    /**
818
-     * Get one or more items randomly from the collection.
819
-     *
820
-     * @param int $amount
821
-     *
822
-     * @throws \InvalidArgumentException
823
-     *
824
-     * @return mixed
825
-     */
826
-    public function random($amount = 1)
827
-    {
828
-        // TODO : we could optimize this by only
829
-        // fetching the keys from the database
830
-        // and performing partial loading
831
-
832
-        $this->initializeProxy();
833
-
834
-        return parent::random($amount);
835
-    }
836
-
837
-    /**
838
-     * Reduce the collection to a single value.
839
-     *
840
-     * @param callable $callback
841
-     * @param mixed    $initial
842
-     *
843
-     * @return mixed
844
-     */
845
-    public function reduce(callable $callback, $initial = null)
846
-    {
847
-        $this->initializeProxy();
848
-
849
-        return parent::reduce($callback, $initial);
850
-    }
851
-
852
-    /**
853
-     * Create a collection of all elements that do not pass a given truth test.
854
-     *
855
-     * @param callable|mixed $callback
856
-     *
857
-     * @return static
858
-     */
859
-    public function reject($callback)
860
-    {
861
-        $this->initializeProxy();
862
-
863
-        return parent::reject($callback);
864
-    }
865
-
866
-    /**
867
-     * Reverse items order.
868
-     *
869
-     * @return static
870
-     */
871
-    public function reverse()
872
-    {
873
-        $this->initializeProxy();
874
-
875
-        return parent::reverse();
876
-    }
877
-
878
-    /**
879
-     * Search the collection for a given value and return the corresponding key if successful.
880
-     *
881
-     * @param mixed $value
882
-     * @param bool  $strict
883
-     *
884
-     * @return mixed
885
-     */
886
-    public function search($value, $strict = false)
887
-    {
888
-        $this->initializeProxy();
889
-
890
-        return parent::search($value, $strict);
891
-    }
892
-
893
-    /**
894
-     * Get and remove the first item from the collection.
895
-     *
896
-     * @return mixed
897
-     */
898
-    public function shift()
899
-    {
900
-        // Todo : Partial Removing
901
-        // we could have a pending removal array
902
-        $this->initializeProxy();
903
-
904
-        return parent::shift();
905
-    }
906
-
907
-    /**
908
-     * Shuffle the items in the collection.
909
-     *
910
-     * @param int $seed
911
-     *
912
-     * @return static
913
-     */
914
-    public function shuffle($seed = null)
915
-    {
916
-        $this->initializeProxy();
917
-
918
-        return parent::shuffle($seed);
919
-    }
920
-
921
-    /**
922
-     * Slice the underlying collection array.
923
-     *
924
-     * @param int $offset
925
-     * @param int $length
926
-     *
927
-     * @return static
928
-     */
929
-    public function slice($offset, $length = null)
930
-    {
931
-        $this->initializeProxy();
932
-
933
-        return parent::slice($offset, $length);
934
-    }
935
-
936
-    /**
937
-     * Split a collection into a certain number of groups.
938
-     *
939
-     * @param int $numberOfGroups
940
-     *
941
-     * @return static
942
-     */
943
-    public function split($numberOfGroups)
944
-    {
945
-        $this->initializeProxy();
946
-
947
-        return parent::split($numberOfGroups);
948
-    }
949
-
950
-    /**
951
-     * Chunk the underlying collection array.
952
-     *
953
-     * @param int $size
954
-     *
955
-     * @return static
956
-     */
957
-    public function chunk($size)
958
-    {
959
-        // TODO : partial loading ?
960
-        $this->initializeProxy();
961
-
962
-        return parent::chunk($size);
963
-    }
964
-
965
-    /**
966
-     * Sort through each item with a callback.
967
-     *
968
-     * @param callable|null $callback
969
-     *
970
-     * @return static
971
-     */
972
-    public function sort(callable $callback = null)
973
-    {
974
-        $this->initializeProxy();
975
-
976
-        return parent::sort($callback);
977
-    }
978
-
979
-    /**
980
-     * Sort the collection using the given callback.
981
-     *
982
-     * @param callable|string $callback
983
-     * @param int             $options
984
-     * @param bool            $descending
985
-     *
986
-     * @return static
987
-     */
988
-    public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
989
-    {
990
-        $this->initializeProxy();
991
-
992
-        return parent::sort($callback, $options, $descending);
993
-    }
994
-
995
-    /**
996
-     * Splice a portion of the underlying collection array.
997
-     *
998
-     * @param int      $offset
999
-     * @param int|null $length
1000
-     * @param mixed    $replacement
1001
-     *
1002
-     * @return static
1003
-     */
1004
-    public function splice($offset, $length = null, $replacement = [])
1005
-    {
1006
-        $this->initializeProxy();
1007
-
1008
-        return parent::splice($offset, $length, $replacement);
1009
-    }
1010
-
1011
-    /**
1012
-     * Get the sum of the given values.
1013
-     *
1014
-     * @param callable|string|null $callback
1015
-     *
1016
-     * @return mixed
1017
-     */
1018
-    public function sum($callback = null)
1019
-    {
1020
-        $this->initializeProxy();
1021
-
1022
-        return parent::sum($callback);
1023
-    }
1024
-
1025
-    /**
1026
-     * Take the first or last {$limit} items.
1027
-     *
1028
-     * @param int $limit
1029
-     *
1030
-     * @return static
1031
-     */
1032
-    public function take($limit)
1033
-    {
1034
-        // TODO: partial loading
1035
-        $this->initializeProxy();
1036
-
1037
-        return parent::take($limit);
1038
-    }
1039
-
1040
-    /**
1041
-     * Transform each item in the collection using a callback.
1042
-     *
1043
-     * @param callable $callback
1044
-     *
1045
-     * @return $this
1046
-     */
1047
-    public function transform(callable $callback)
1048
-    {
1049
-        $this->initializeProxy();
1050
-
1051
-        return parent::transform($callback);
1052
-    }
1053
-
1054
-    /**
1055
-     * Return only unique items from the collection array.
1056
-     *
1057
-     * @param string|callable|null $key
1058
-     * @param bool                 $strict
1059
-     *
1060
-     * @return static
1061
-     */
1062
-    public function unique($key = null, $strict = false)
1063
-    {
1064
-        $this->initializeProxy();
1065
-
1066
-        return parent::unique($key, $strict);
1067
-    }
1068
-
1069
-    /**
1070
-     * Reset the keys on the underlying array.
1071
-     *
1072
-     * @return static
1073
-     */
1074
-    public function values()
1075
-    {
1076
-        $this->initializeProxy();
1077
-
1078
-        return parent::values();
1079
-    }
1080
-
1081
-    /**
1082
-     * Zip the collection together with one or more arrays.
1083
-     *
1084
-     * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1085
-     *      => [[1, 4], [2, 5], [3, 6]]
1086
-     *
1087
-     * @param mixed ...$items
1088
-     *
1089
-     * @return static
1090
-     */
1091
-    public function zip($items)
1092
-    {
1093
-        $this->initializeProxy();
1094
-
1095
-        return parent::zip($items);
1096
-    }
1097
-
1098
-    /**
1099
-     * Get the collection of items as a plain array.
1100
-     *
1101
-     * @return array
1102
-     */
1103
-    public function toArray()
1104
-    {
1105
-        // If this is called on all subsequent proxy,
1106
-        // this would eventually trigger all lazy loading,
1107
-        // which is NOT what we would expect...
1108
-        // TODO : must think of this.
1109
-        $this->initializeProxy();
1110
-
1111
-        return parent::toArray();
1112
-    }
1113
-
1114
-    /**
1115
-     * Convert the object into something JSON serializable.
1116
-     *
1117
-     * @return array
1118
-     */
1119
-    public function jsonSerialize()
1120
-    {
1121
-        // If this is called on all subsequent proxy,
1122
-        // this would eventually trigger all lazy loading,
1123
-        // which is NOT what we would expect...
1124
-        // TODO : must think of this.
1125
-        $this->initializeProxy();
1126
-
1127
-        return parent::jsonSerialize();
1128
-    }
1129
-
1130
-    /**
1131
-     * Get the collection of items as JSON.
1132
-     *
1133
-     * @param int $options
1134
-     *
1135
-     * @return string
1136
-     */
1137
-    public function toJson($options = 0)
1138
-    {
1139
-        // If this is called on all subsequent proxy,
1140
-        // this would eventually trigger all lazy loading,
1141
-        // which is NOT what we would expect...
1142
-        // TODO : must think of this.
1143
-        $this->initializeProxy();
1144
-
1145
-        return parent::toJson($options);
1146
-    }
1147
-
1148
-    /**
1149
-     * Get an iterator for the items.
1150
-     *
1151
-     * @return \ArrayIterator
1152
-     */
1153
-    public function getIterator()
1154
-    {
1155
-        $this->initializeProxy();
1156
-
1157
-        return parent::getIterator();
1158
-    }
1159
-
1160
-    /**
1161
-     * Get a CachingIterator instance.
1162
-     *
1163
-     * @param int $flags
1164
-     *
1165
-     * @return \CachingIterator
1166
-     */
1167
-    public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1168
-    {
1169
-        $this->initializeProxy();
1170
-
1171
-        return parent::getCachingIterator($flags);
1172
-    }
1173
-
1174
-    /**
1175
-     * Count the number of items in the collection.
1176
-     *
1177
-     * @return int
1178
-     */
1179
-    public function count()
1180
-    {
1181
-        // TODO rely on QB if not initialized
1182
-        $this->initializeProxy();
1183
-
1184
-        return parent::count();
1185
-    }
1186
-
1187
-    /**
1188
-     * Get a base Support collection instance from this collection.
1189
-     *
1190
-     * @return \Illuminate\Support\Collection
1191
-     */
1192
-    public function toBase()
1193
-    {
1194
-        $this->initializeProxy();
1195
-
1196
-        return parent::toBase();
1197
-    }
1198
-
1199
-    /**
1200
-     * Determine if an item exists at an offset.
1201
-     *
1202
-     * @param mixed $key
1203
-     *
1204
-     * @return bool
1205
-     */
1206
-    public function offsetExists($key)
1207
-    {
1208
-        // TODO rely on QB if no collection
1209
-        // initialized
1210
-        $this->initializeProxy();
1211
-
1212
-        return parent::offsetExists($key);
1213
-    }
1214
-
1215
-    /**
1216
-     * Get an item at a given offset.
1217
-     *
1218
-     * @param mixed $key
1219
-     *
1220
-     * @return mixed
1221
-     */
1222
-    public function offsetGet($key)
1223
-    {
1224
-        // TODO rely on partial init if no collection
1225
-        // initialized
1226
-        $this->initializeProxy();
1227
-
1228
-        return parent::offsetGet($key);
1229
-    }
1230
-
1231
-    /**
1232
-     * Set the item at a given offset.
1233
-     *
1234
-     * @param mixed $key
1235
-     * @param mixed $value
1236
-     *
1237
-     * @return void
1238
-     */
1239
-    public function offsetSet($key, $value)
1240
-    {
1241
-        // TODO : think of the use of it into a ProxyCollection
1242
-        // context
1243
-        $this->initializeProxy();
1244
-
1245
-        return parent::offsetSet($key, $value);
1246
-    }
1247
-
1248
-    /**
1249
-     * Unset the item at a given offset.
1250
-     *
1251
-     * @param string $key
1252
-     *
1253
-     * @return void
1254
-     */
1255
-    public function offsetUnset($key)
1256
-    {
1257
-        // TODO : think of the use of it into a ProxyCollection
1258
-        // context
1259
-        $this->initializeProxy();
1260
-
1261
-        return parent::offsetUnset($key);
1262
-    }
1263
-
1264
-    /**
1265
-     * Dynamically access collection proxies.
1266
-     *
1267
-     * @param string $key
1268
-     *
1269
-     * @throws \Exception
1270
-     *
1271
-     * @return mixed
1272
-     */
1273
-    public function __get($key)
1274
-    {
1275
-        parent::__get($key);
1276
-    }
1277
-
1278
-    /**
1279
-     * Dynamically handle calls to the class.
1280
-     *
1281
-     * @param string $method
1282
-     * @param array  $parameters
1283
-     *
1284
-     * @throws \BadMethodCallException
1285
-     *
1286
-     * @return mixed
1287
-     */
1288
-    public function __call($method, $parameters)
1289
-    {
1290
-        $this->initializeProxy();
1291
-
1292
-        return parent::__call($method, $parameters);
1293
-    }
12
+	/**
13
+	 * Indicate if the relationship has been lazy loaded.
14
+	 *
15
+	 * @var bool
16
+	 */
17
+	protected $relationshipLoaded = false;
18
+
19
+	protected $addedItems = [];
20
+
21
+	/**
22
+	 * Create a new collection.
23
+	 *
24
+	 * @param mixed  $entity
25
+	 * @param string $relation
26
+	 *
27
+	 * @return void
28
+	 */
29
+	public function __construct($entity, $relation)
30
+	{
31
+		$this->parentEntity = $entity;
32
+		$this->relationshipMethod = $relation;
33
+	}
34
+
35
+	/**
36
+	 * Return Items that has been added without lady loading
37
+	 * the underlying collection.
38
+	 *
39
+	 * @return array
40
+	 */
41
+	public function getAddedItems()
42
+	{
43
+		return $this->addedItems;
44
+	}
45
+
46
+	/**
47
+	 * Force initialization of the proxy.
48
+	 *
49
+	 * @return bool true if the proxy could be initialized
50
+	 */
51
+	public function initializeProxy() : bool
52
+	{
53
+		if ($this->isProxyInitialized()) {
54
+			return true;
55
+		}
56
+
57
+		$relation = $this->relationshipMethod;
58
+		$entity = $this->parentEntity;
59
+
60
+		$entityMap = Manager::getMapper($entity)->getEntityMap();
61
+
62
+		$this->items = $entityMap->$relation($entity)->getResults($relation)->all() + $this->addedItems;
63
+
64
+		$this->relationshipLoaded = true;
65
+
66
+		return true;
67
+	}
68
+
69
+	/**
70
+	 * Retrieves current initialization status of the proxy.
71
+	 *
72
+	 * @return bool
73
+	 */
74
+	public function isProxyInitialized() : bool
75
+	{
76
+		return $this->relationshipLoaded;
77
+	}
78
+
79
+	/**
80
+	 * Get all of the items in the collection.
81
+	 *
82
+	 * @return array
83
+	 */
84
+	public function all()
85
+	{
86
+		$this->initializeProxy();
87
+
88
+		return parent::all();
89
+	}
90
+
91
+	/**
92
+	 * Get the average value of a given key.
93
+	 *
94
+	 * @param callable|string|null $callback
95
+	 *
96
+	 * @return mixed
97
+	 */
98
+	public function avg($callback = null)
99
+	{
100
+		$this->initializeProxy();
101
+
102
+		return parent::avg($callback);
103
+	}
104
+
105
+	/**
106
+	 * Get the median of a given key.
107
+	 *
108
+	 * @param null $key
109
+	 *
110
+	 * @return mixed|null
111
+	 */
112
+	public function median($key = null)
113
+	{
114
+		$this->initializeProxy();
115
+
116
+		return parent::median($key);
117
+	}
118
+
119
+	/**
120
+	 * Get the mode of a given key.
121
+	 *
122
+	 * @param mixed $key
123
+	 *
124
+	 * @return array
125
+	 */
126
+	public function mode($key = null)
127
+	{
128
+		$this->initializeProxy();
129
+
130
+		return parent::mode($key);
131
+	}
132
+
133
+	/**
134
+	 * Collapse the collection of items into a single array.
135
+	 *
136
+	 * @return static
137
+	 */
138
+	public function collapse()
139
+	{
140
+		$this->initializeProxy();
141
+
142
+		return parent::collapse();
143
+	}
144
+
145
+	/**
146
+	 * Determine if an item exists in the collection.
147
+	 *
148
+	 * @param mixed $key
149
+	 * @param mixed $value
150
+	 *
151
+	 * @return bool
152
+	 */
153
+	public function contains($key, $operator = null, $value = null)
154
+	{
155
+		$this->initializeProxy();
156
+
157
+		return parent::contains($key, $operator, $value);
158
+	}
159
+
160
+	/**
161
+	 * Determine if an item exists in the collection using strict comparison.
162
+	 *
163
+	 * @param mixed $key
164
+	 * @param mixed $value
165
+	 *
166
+	 * @return bool
167
+	 */
168
+	public function containsStrict($key, $value = null)
169
+	{
170
+		$this->initializeProxy();
171
+
172
+		return parent::containsStrict($key, $value);
173
+	}
174
+
175
+	/**
176
+	 * Get the items in the collection that are not present in the given items.
177
+	 *
178
+	 * @param mixed $items
179
+	 *
180
+	 * @return static
181
+	 */
182
+	public function diff($items)
183
+	{
184
+		$this->initializeProxy();
185
+
186
+		return parent::diff($items);
187
+	}
188
+
189
+	/**
190
+	 * Get the items in the collection whose keys are not present in the given items.
191
+	 *
192
+	 * @param mixed $items
193
+	 *
194
+	 * @return static
195
+	 */
196
+	public function diffKeys($items)
197
+	{
198
+		$this->initializeProxy();
199
+
200
+		return parent::diffKeys($items);
201
+	}
202
+
203
+	/**
204
+	 * Execute a callback over each item.
205
+	 *
206
+	 * @param callable $callback
207
+	 *
208
+	 * @return $this
209
+	 */
210
+	public function each(callable $callback)
211
+	{
212
+		$this->initializeProxy();
213
+
214
+		return parent::each($callback);
215
+	}
216
+
217
+	/**
218
+	 * Create a new collection consisting of every n-th element.
219
+	 *
220
+	 * @param int $step
221
+	 * @param int $offset
222
+	 *
223
+	 * @return static
224
+	 */
225
+	public function every($key, $operator = null, $value = null)
226
+	{
227
+		$this->initializeProxy();
228
+
229
+		return parent::every($key, $operator, $value);
230
+	}
231
+
232
+	/**
233
+	 * Get all items except for those with the specified keys.
234
+	 *
235
+	 * @param mixed $keys
236
+	 *
237
+	 * @return static
238
+	 */
239
+	public function except($keys)
240
+	{
241
+		$this->initializeProxy();
242
+
243
+		return parent::except($keys);
244
+	}
245
+
246
+	/**
247
+	 * Run a filter over each of the items.
248
+	 *
249
+	 * @param callable|null $callback
250
+	 *
251
+	 * @return static
252
+	 */
253
+	public function filter(callable $callback = null)
254
+	{
255
+		$this->initializeProxy();
256
+
257
+		return parent::filter($callback);
258
+	}
259
+
260
+	/**
261
+	 * Filter items by the given key value pair.
262
+	 *
263
+	 * @param string $key
264
+	 * @param mixed  $operator
265
+	 * @param mixed  $value
266
+	 *
267
+	 * @return static
268
+	 */
269
+	public function where($key, $operator, $value = null)
270
+	{
271
+		$this->initializeProxy();
272
+
273
+		return parent::where($key, $operator, $value);
274
+	}
275
+
276
+	/**
277
+	 * Filter items by the given key value pair using strict comparison.
278
+	 *
279
+	 * @param string $key
280
+	 * @param mixed  $value
281
+	 *
282
+	 * @return static
283
+	 */
284
+	public function whereStrict($key, $value)
285
+	{
286
+		$this->initializeProxy();
287
+
288
+		return parent::whereStrict($key, $value);
289
+	}
290
+
291
+	/**
292
+	 * Filter items by the given key value pair.
293
+	 *
294
+	 * @param string $key
295
+	 * @param mixed  $values
296
+	 * @param bool   $strict
297
+	 *
298
+	 * @return static
299
+	 */
300
+	public function whereIn($key, $values, $strict = false)
301
+	{
302
+		$this->initializeProxy();
303
+
304
+		return parent::whereIn($key, $values, $strict);
305
+	}
306
+
307
+	/**
308
+	 * Filter items by the given key value pair using strict comparison.
309
+	 *
310
+	 * @param string $key
311
+	 * @param mixed  $values
312
+	 *
313
+	 * @return static
314
+	 */
315
+	public function whereInStrict($key, $values)
316
+	{
317
+		$this->initializeProxy();
318
+
319
+		return parent::whereInStrict($key, $values);
320
+	}
321
+
322
+	/**
323
+	 * Get the first item from the collection.
324
+	 *
325
+	 * @param callable|null $callback
326
+	 * @param mixed         $default
327
+	 *
328
+	 * @return mixed
329
+	 */
330
+	public function first(callable $callback = null, $default = null)
331
+	{
332
+		// TODO Consider partial loading
333
+		$this->initializeProxy();
334
+
335
+		return parent::first($callback, $default);
336
+	}
337
+
338
+	/**
339
+	 * Get a flattened array of the items in the collection.
340
+	 *
341
+	 * @param int $depth
342
+	 *
343
+	 * @return static
344
+	 */
345
+	public function flatten($depth = INF)
346
+	{
347
+		$this->initializeProxy();
348
+
349
+		return parent::flatten($depth);
350
+	}
351
+
352
+	/**
353
+	 * Flip the items in the collection.
354
+	 *
355
+	 * @return static
356
+	 */
357
+	public function flip()
358
+	{
359
+		$this->initializeProxy();
360
+
361
+		return parent::flip();
362
+	}
363
+
364
+	/**
365
+	 * Remove an item from the collection by key.
366
+	 *
367
+	 * @param string|array $keys
368
+	 *
369
+	 * @return $this
370
+	 */
371
+	public function forget($keys)
372
+	{
373
+		// TODO, we could consider these as
374
+		// 'pending deletion', the same way that
375
+		// we treat added items
376
+		$this->initializeProxy();
377
+
378
+		return parent::forget($keys);
379
+	}
380
+
381
+	/**
382
+	 * Get an item from the collection by key.
383
+	 *
384
+	 * @param mixed $key
385
+	 * @param mixed $default
386
+	 *
387
+	 * @return mixed
388
+	 */
389
+	public function get($key, $default = null)
390
+	{
391
+		// TODO : We could also consider partial loading
392
+		// here
393
+		$this->initializeProxy();
394
+
395
+		return parent::get($key, $default);
396
+	}
397
+
398
+	/**
399
+	 * Group an associative array by a field or using a callback.
400
+	 *
401
+	 * @param callable|string $groupBy
402
+	 * @param bool            $preserveKeys
403
+	 *
404
+	 * @return static
405
+	 */
406
+	public function groupBy($groupBy, $preserveKeys = false)
407
+	{
408
+		$this->initializeProxy();
409
+
410
+		return parent::groupBy($groupBy, $preserveKeys);
411
+	}
412
+
413
+	/**
414
+	 * Key an associative array by a field or using a callback.
415
+	 *
416
+	 * @param callable|string $keyBy
417
+	 *
418
+	 * @return static
419
+	 */
420
+	public function keyBy($keyBy)
421
+	{
422
+		$this->initializeProxy();
423
+
424
+		return parent::keyBy($keyBy);
425
+	}
426
+
427
+	/**
428
+	 * Determine if an item exists in the collection by key.
429
+	 *
430
+	 * @param mixed $key
431
+	 *
432
+	 * @return bool
433
+	 */
434
+	public function has($key)
435
+	{
436
+		// TODO : we could do automagic here by directly
437
+		// calling the database if the collection hasn't
438
+		// been initialized yet.
439
+		// Potential issue is that several calls to this
440
+		// could cause a lot queries vs a single get query.
441
+		$this->initializeProxy();
442
+
443
+		return parent::has($key);
444
+	}
445
+
446
+	/**
447
+	 * Concatenate values of a given key as a string.
448
+	 *
449
+	 * @param string $value
450
+	 * @param string $glue
451
+	 *
452
+	 * @return string
453
+	 */
454
+	public function implode($value, $glue = null)
455
+	{
456
+		$this->initializeProxy();
457
+
458
+		return parent::implode($value, $glue);
459
+	}
460
+
461
+	/**
462
+	 * Intersect the collection with the given items.
463
+	 *
464
+	 * @param mixed $items
465
+	 *
466
+	 * @return static
467
+	 */
468
+	public function intersect($items)
469
+	{
470
+		$this->initializeProxy();
471
+
472
+		return parent::intersect($items);
473
+	}
474
+
475
+	/**
476
+	 * Determine if the collection is empty or not.
477
+	 *
478
+	 * @return bool
479
+	 */
480
+	public function isEmpty()
481
+	{
482
+		$this->initializeProxy();
483
+
484
+		return parent::isEmpty();
485
+	}
486
+
487
+	/**
488
+	 * Get the keys of the collection items.
489
+	 *
490
+	 * @return static
491
+	 */
492
+	public function keys()
493
+	{
494
+		$this->initializeProxy();
495
+
496
+		return parent::keys();
497
+	}
498
+
499
+	/**
500
+	 * Get the last item from the collection.
501
+	 *
502
+	 * @param callable|null $callback
503
+	 * @param mixed         $default
504
+	 *
505
+	 * @return mixed
506
+	 */
507
+	public function last(callable $callback = null, $default = null)
508
+	{
509
+		// TODO : we could do partial loading there as well
510
+		$this->initializeProxy();
511
+
512
+		return parent::last($callback, $default);
513
+	}
514
+
515
+	/**
516
+	 * Get the values of a given key.
517
+	 *
518
+	 * @param string      $value
519
+	 * @param string|null $key
520
+	 *
521
+	 * @return static
522
+	 */
523
+	public function pluck($value, $key = null)
524
+	{
525
+		// TODO : automagic call to QB if not initialized
526
+		$this->initializeProxy();
527
+
528
+		return parent::pluck($value, $key);
529
+	}
530
+
531
+	/**
532
+	 * Run a map over each of the items.
533
+	 *
534
+	 * @param callable $callback
535
+	 *
536
+	 * @return static
537
+	 */
538
+	public function map(callable $callback)
539
+	{
540
+		$this->initializeProxy();
541
+
542
+		return parent::map($callback);
543
+	}
544
+
545
+	/**
546
+	 * Run an associative map over each of the items.
547
+	 *
548
+	 * The callback should return an associative array with a single key/value pair.
549
+	 *
550
+	 * @param callable $callback
551
+	 *
552
+	 * @return static
553
+	 */
554
+	public function mapWithKeys(callable $callback)
555
+	{
556
+		$this->initializeProxy();
557
+
558
+		return parent::mapWithKeys($callback);
559
+	}
560
+
561
+	/**
562
+	 * Map a collection and flatten the result by a single level.
563
+	 *
564
+	 * @param callable $callback
565
+	 *
566
+	 * @return static
567
+	 */
568
+	public function flatMap(callable $callback)
569
+	{
570
+		$this->initializeProxy();
571
+
572
+		return parent::flatMap($callback);
573
+	}
574
+
575
+	/**
576
+	 * Get the max value of a given key.
577
+	 *
578
+	 * @param callable|string|null $callback
579
+	 *
580
+	 * @return mixed
581
+	 */
582
+	public function max($callback = null)
583
+	{
584
+		$this->initializeProxy();
585
+
586
+		return parent::max($callback);
587
+	}
588
+
589
+	/**
590
+	 * Merge the collection with the given items.
591
+	 *
592
+	 * @param mixed $items
593
+	 *
594
+	 * @return static
595
+	 */
596
+	public function merge($items)
597
+	{
598
+		// TODO : Check if the EntityCollection
599
+		// returns a native Collection, as it
600
+		// is what we want here
601
+		$this->initializeProxy();
602
+
603
+		return parent::merge($items);
604
+	}
605
+
606
+	/**
607
+	 * Create a collection by using this collection for keys and another for its values.
608
+	 *
609
+	 * @param mixed $values
610
+	 *
611
+	 * @return static
612
+	 */
613
+	public function combine($values)
614
+	{
615
+		// TODO : Check if the EntityCollection
616
+		// returns a native Collection, as it
617
+		// is what we want here
618
+		$this->initializeProxy();
619
+
620
+		return parent::combine($values);
621
+	}
622
+
623
+	/**
624
+	 * Union the collection with the given items.
625
+	 *
626
+	 * @param mixed $items
627
+	 *
628
+	 * @return static
629
+	 */
630
+	public function union($items)
631
+	{
632
+		// TODO : Check if the EntityCollection
633
+		// returns a native Collection, as it
634
+		// is what we want here
635
+		$this->initializeProxy();
636
+
637
+		return parent::union($items);
638
+	}
639
+
640
+	/**
641
+	 * Get the min value of a given key.
642
+	 *
643
+	 * @param callable|string|null $callback
644
+	 *
645
+	 * @return mixed
646
+	 */
647
+	public function min($callback = null)
648
+	{
649
+		// TODO : we could rely on the QB
650
+		// for thos, if initialization has not
651
+		// take place yet
652
+		$this->initializeProxy();
653
+
654
+		return parent::min($callback);
655
+	}
656
+
657
+	/**
658
+	 * Create a new collection consisting of every n-th element.
659
+	 *
660
+	 * @param int $step
661
+	 * @param int $offset
662
+	 *
663
+	 * @return static
664
+	 */
665
+	public function nth($step, $offset = 0)
666
+	{
667
+		$this->initializeProxy();
668
+
669
+		return parent::nth($step, $offset);
670
+	}
671
+
672
+	/**
673
+	 * Get the items with the specified keys.
674
+	 *
675
+	 * @param mixed $keys
676
+	 *
677
+	 * @return static
678
+	 */
679
+	public function only($keys)
680
+	{
681
+		// TODO : we could rely on the QB if
682
+		// the collection hasn't been initialized yet
683
+		$this->initializeProxy();
684
+
685
+		return parent::only($keys);
686
+	}
687
+
688
+	/**
689
+	 * "Paginate" the collection by slicing it into a smaller collection.
690
+	 *
691
+	 * @param int $page
692
+	 * @param int $perPage
693
+	 *
694
+	 * @return static
695
+	 */
696
+	public function forPage($page, $perPage)
697
+	{
698
+		// TODO : check possibility of partial loading
699
+		// if not initialized
700
+		$this->initializeProxy();
701
+
702
+		return parent::forPage($page, $perPage);
703
+	}
704
+
705
+	/**
706
+	 * Partition the collection into two arrays using the given callback or key.
707
+	 *
708
+	 * @param callable|string $callback
709
+	 *
710
+	 * @return static
711
+	 */
712
+	public function partition($callback)
713
+	{
714
+		$this->initializeProxy();
715
+
716
+		return parent::partition($callback);
717
+	}
718
+
719
+	/**
720
+	 * Pass the collection to the given callback and return the result.
721
+	 *
722
+	 * @param callable $callback
723
+	 *
724
+	 * @return mixed
725
+	 */
726
+	public function pipe(callable $callback)
727
+	{
728
+		$this->initializeProxy();
729
+
730
+		return parent::pipe($callback);
731
+	}
732
+
733
+	/**
734
+	 * Get and remove the last item from the collection.
735
+	 *
736
+	 * @return mixed
737
+	 */
738
+	public function pop()
739
+	{
740
+		$this->initializeProxy();
741
+
742
+		return parent::pop();
743
+	}
744
+
745
+	/**
746
+	 * Push an item onto the beginning of the collection.
747
+	 *
748
+	 * @param mixed $value
749
+	 * @param mixed $key
750
+	 *
751
+	 * @return $this
752
+	 */
753
+	public function prepend($value, $key = null)
754
+	{
755
+		// TODO : partial adding of values.
756
+		// we could have a $prepended , and $pushed arrays
757
+		// which we would combine at full initialization
758
+
759
+		$this->initializeProxy();
760
+
761
+		return parent::prepend($value, $key);
762
+	}
763
+
764
+	/**
765
+	 * Push an item onto the end of the collection.
766
+	 *
767
+	 * @param mixed $value
768
+	 *
769
+	 * @return $this
770
+	 */
771
+	public function push($value)
772
+	{
773
+		// TODO : partial adding of values.
774
+		// we could have a $prepended , and $pushed arrays
775
+		// which we would combine at full initialization
776
+
777
+		$this->initializeProxy();
778
+
779
+		return parent::push($value);
780
+	}
781
+
782
+	/**
783
+	 * Get and remove an item from the collection.
784
+	 *
785
+	 * @param mixed $key
786
+	 * @param mixed $default
787
+	 *
788
+	 * @return mixed
789
+	 */
790
+	public function pull($key, $default = null)
791
+	{
792
+		// TODO : QB query if the collection
793
+		// hasn't been initialized yet
794
+
795
+		$this->initializeProxy();
796
+
797
+		return parent::pull($key, $default);
798
+	}
799
+
800
+	/**
801
+	 * Put an item in the collection by key.
802
+	 *
803
+	 * @param mixed $key
804
+	 * @param mixed $value
805
+	 *
806
+	 * @return $this
807
+	 */
808
+	public function put($key, $value)
809
+	{
810
+		// TODO : Partial loading ?
811
+
812
+		$this->initializeProxy();
813
+
814
+		return parent::put($key, $value);
815
+	}
816
+
817
+	/**
818
+	 * Get one or more items randomly from the collection.
819
+	 *
820
+	 * @param int $amount
821
+	 *
822
+	 * @throws \InvalidArgumentException
823
+	 *
824
+	 * @return mixed
825
+	 */
826
+	public function random($amount = 1)
827
+	{
828
+		// TODO : we could optimize this by only
829
+		// fetching the keys from the database
830
+		// and performing partial loading
831
+
832
+		$this->initializeProxy();
833
+
834
+		return parent::random($amount);
835
+	}
836
+
837
+	/**
838
+	 * Reduce the collection to a single value.
839
+	 *
840
+	 * @param callable $callback
841
+	 * @param mixed    $initial
842
+	 *
843
+	 * @return mixed
844
+	 */
845
+	public function reduce(callable $callback, $initial = null)
846
+	{
847
+		$this->initializeProxy();
848
+
849
+		return parent::reduce($callback, $initial);
850
+	}
851
+
852
+	/**
853
+	 * Create a collection of all elements that do not pass a given truth test.
854
+	 *
855
+	 * @param callable|mixed $callback
856
+	 *
857
+	 * @return static
858
+	 */
859
+	public function reject($callback)
860
+	{
861
+		$this->initializeProxy();
862
+
863
+		return parent::reject($callback);
864
+	}
865
+
866
+	/**
867
+	 * Reverse items order.
868
+	 *
869
+	 * @return static
870
+	 */
871
+	public function reverse()
872
+	{
873
+		$this->initializeProxy();
874
+
875
+		return parent::reverse();
876
+	}
877
+
878
+	/**
879
+	 * Search the collection for a given value and return the corresponding key if successful.
880
+	 *
881
+	 * @param mixed $value
882
+	 * @param bool  $strict
883
+	 *
884
+	 * @return mixed
885
+	 */
886
+	public function search($value, $strict = false)
887
+	{
888
+		$this->initializeProxy();
889
+
890
+		return parent::search($value, $strict);
891
+	}
892
+
893
+	/**
894
+	 * Get and remove the first item from the collection.
895
+	 *
896
+	 * @return mixed
897
+	 */
898
+	public function shift()
899
+	{
900
+		// Todo : Partial Removing
901
+		// we could have a pending removal array
902
+		$this->initializeProxy();
903
+
904
+		return parent::shift();
905
+	}
906
+
907
+	/**
908
+	 * Shuffle the items in the collection.
909
+	 *
910
+	 * @param int $seed
911
+	 *
912
+	 * @return static
913
+	 */
914
+	public function shuffle($seed = null)
915
+	{
916
+		$this->initializeProxy();
917
+
918
+		return parent::shuffle($seed);
919
+	}
920
+
921
+	/**
922
+	 * Slice the underlying collection array.
923
+	 *
924
+	 * @param int $offset
925
+	 * @param int $length
926
+	 *
927
+	 * @return static
928
+	 */
929
+	public function slice($offset, $length = null)
930
+	{
931
+		$this->initializeProxy();
932
+
933
+		return parent::slice($offset, $length);
934
+	}
935
+
936
+	/**
937
+	 * Split a collection into a certain number of groups.
938
+	 *
939
+	 * @param int $numberOfGroups
940
+	 *
941
+	 * @return static
942
+	 */
943
+	public function split($numberOfGroups)
944
+	{
945
+		$this->initializeProxy();
946
+
947
+		return parent::split($numberOfGroups);
948
+	}
949
+
950
+	/**
951
+	 * Chunk the underlying collection array.
952
+	 *
953
+	 * @param int $size
954
+	 *
955
+	 * @return static
956
+	 */
957
+	public function chunk($size)
958
+	{
959
+		// TODO : partial loading ?
960
+		$this->initializeProxy();
961
+
962
+		return parent::chunk($size);
963
+	}
964
+
965
+	/**
966
+	 * Sort through each item with a callback.
967
+	 *
968
+	 * @param callable|null $callback
969
+	 *
970
+	 * @return static
971
+	 */
972
+	public function sort(callable $callback = null)
973
+	{
974
+		$this->initializeProxy();
975
+
976
+		return parent::sort($callback);
977
+	}
978
+
979
+	/**
980
+	 * Sort the collection using the given callback.
981
+	 *
982
+	 * @param callable|string $callback
983
+	 * @param int             $options
984
+	 * @param bool            $descending
985
+	 *
986
+	 * @return static
987
+	 */
988
+	public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
989
+	{
990
+		$this->initializeProxy();
991
+
992
+		return parent::sort($callback, $options, $descending);
993
+	}
994
+
995
+	/**
996
+	 * Splice a portion of the underlying collection array.
997
+	 *
998
+	 * @param int      $offset
999
+	 * @param int|null $length
1000
+	 * @param mixed    $replacement
1001
+	 *
1002
+	 * @return static
1003
+	 */
1004
+	public function splice($offset, $length = null, $replacement = [])
1005
+	{
1006
+		$this->initializeProxy();
1007
+
1008
+		return parent::splice($offset, $length, $replacement);
1009
+	}
1010
+
1011
+	/**
1012
+	 * Get the sum of the given values.
1013
+	 *
1014
+	 * @param callable|string|null $callback
1015
+	 *
1016
+	 * @return mixed
1017
+	 */
1018
+	public function sum($callback = null)
1019
+	{
1020
+		$this->initializeProxy();
1021
+
1022
+		return parent::sum($callback);
1023
+	}
1024
+
1025
+	/**
1026
+	 * Take the first or last {$limit} items.
1027
+	 *
1028
+	 * @param int $limit
1029
+	 *
1030
+	 * @return static
1031
+	 */
1032
+	public function take($limit)
1033
+	{
1034
+		// TODO: partial loading
1035
+		$this->initializeProxy();
1036
+
1037
+		return parent::take($limit);
1038
+	}
1039
+
1040
+	/**
1041
+	 * Transform each item in the collection using a callback.
1042
+	 *
1043
+	 * @param callable $callback
1044
+	 *
1045
+	 * @return $this
1046
+	 */
1047
+	public function transform(callable $callback)
1048
+	{
1049
+		$this->initializeProxy();
1050
+
1051
+		return parent::transform($callback);
1052
+	}
1053
+
1054
+	/**
1055
+	 * Return only unique items from the collection array.
1056
+	 *
1057
+	 * @param string|callable|null $key
1058
+	 * @param bool                 $strict
1059
+	 *
1060
+	 * @return static
1061
+	 */
1062
+	public function unique($key = null, $strict = false)
1063
+	{
1064
+		$this->initializeProxy();
1065
+
1066
+		return parent::unique($key, $strict);
1067
+	}
1068
+
1069
+	/**
1070
+	 * Reset the keys on the underlying array.
1071
+	 *
1072
+	 * @return static
1073
+	 */
1074
+	public function values()
1075
+	{
1076
+		$this->initializeProxy();
1077
+
1078
+		return parent::values();
1079
+	}
1080
+
1081
+	/**
1082
+	 * Zip the collection together with one or more arrays.
1083
+	 *
1084
+	 * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1085
+	 *      => [[1, 4], [2, 5], [3, 6]]
1086
+	 *
1087
+	 * @param mixed ...$items
1088
+	 *
1089
+	 * @return static
1090
+	 */
1091
+	public function zip($items)
1092
+	{
1093
+		$this->initializeProxy();
1094
+
1095
+		return parent::zip($items);
1096
+	}
1097
+
1098
+	/**
1099
+	 * Get the collection of items as a plain array.
1100
+	 *
1101
+	 * @return array
1102
+	 */
1103
+	public function toArray()
1104
+	{
1105
+		// If this is called on all subsequent proxy,
1106
+		// this would eventually trigger all lazy loading,
1107
+		// which is NOT what we would expect...
1108
+		// TODO : must think of this.
1109
+		$this->initializeProxy();
1110
+
1111
+		return parent::toArray();
1112
+	}
1113
+
1114
+	/**
1115
+	 * Convert the object into something JSON serializable.
1116
+	 *
1117
+	 * @return array
1118
+	 */
1119
+	public function jsonSerialize()
1120
+	{
1121
+		// If this is called on all subsequent proxy,
1122
+		// this would eventually trigger all lazy loading,
1123
+		// which is NOT what we would expect...
1124
+		// TODO : must think of this.
1125
+		$this->initializeProxy();
1126
+
1127
+		return parent::jsonSerialize();
1128
+	}
1129
+
1130
+	/**
1131
+	 * Get the collection of items as JSON.
1132
+	 *
1133
+	 * @param int $options
1134
+	 *
1135
+	 * @return string
1136
+	 */
1137
+	public function toJson($options = 0)
1138
+	{
1139
+		// If this is called on all subsequent proxy,
1140
+		// this would eventually trigger all lazy loading,
1141
+		// which is NOT what we would expect...
1142
+		// TODO : must think of this.
1143
+		$this->initializeProxy();
1144
+
1145
+		return parent::toJson($options);
1146
+	}
1147
+
1148
+	/**
1149
+	 * Get an iterator for the items.
1150
+	 *
1151
+	 * @return \ArrayIterator
1152
+	 */
1153
+	public function getIterator()
1154
+	{
1155
+		$this->initializeProxy();
1156
+
1157
+		return parent::getIterator();
1158
+	}
1159
+
1160
+	/**
1161
+	 * Get a CachingIterator instance.
1162
+	 *
1163
+	 * @param int $flags
1164
+	 *
1165
+	 * @return \CachingIterator
1166
+	 */
1167
+	public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1168
+	{
1169
+		$this->initializeProxy();
1170
+
1171
+		return parent::getCachingIterator($flags);
1172
+	}
1173
+
1174
+	/**
1175
+	 * Count the number of items in the collection.
1176
+	 *
1177
+	 * @return int
1178
+	 */
1179
+	public function count()
1180
+	{
1181
+		// TODO rely on QB if not initialized
1182
+		$this->initializeProxy();
1183
+
1184
+		return parent::count();
1185
+	}
1186
+
1187
+	/**
1188
+	 * Get a base Support collection instance from this collection.
1189
+	 *
1190
+	 * @return \Illuminate\Support\Collection
1191
+	 */
1192
+	public function toBase()
1193
+	{
1194
+		$this->initializeProxy();
1195
+
1196
+		return parent::toBase();
1197
+	}
1198
+
1199
+	/**
1200
+	 * Determine if an item exists at an offset.
1201
+	 *
1202
+	 * @param mixed $key
1203
+	 *
1204
+	 * @return bool
1205
+	 */
1206
+	public function offsetExists($key)
1207
+	{
1208
+		// TODO rely on QB if no collection
1209
+		// initialized
1210
+		$this->initializeProxy();
1211
+
1212
+		return parent::offsetExists($key);
1213
+	}
1214
+
1215
+	/**
1216
+	 * Get an item at a given offset.
1217
+	 *
1218
+	 * @param mixed $key
1219
+	 *
1220
+	 * @return mixed
1221
+	 */
1222
+	public function offsetGet($key)
1223
+	{
1224
+		// TODO rely on partial init if no collection
1225
+		// initialized
1226
+		$this->initializeProxy();
1227
+
1228
+		return parent::offsetGet($key);
1229
+	}
1230
+
1231
+	/**
1232
+	 * Set the item at a given offset.
1233
+	 *
1234
+	 * @param mixed $key
1235
+	 * @param mixed $value
1236
+	 *
1237
+	 * @return void
1238
+	 */
1239
+	public function offsetSet($key, $value)
1240
+	{
1241
+		// TODO : think of the use of it into a ProxyCollection
1242
+		// context
1243
+		$this->initializeProxy();
1244
+
1245
+		return parent::offsetSet($key, $value);
1246
+	}
1247
+
1248
+	/**
1249
+	 * Unset the item at a given offset.
1250
+	 *
1251
+	 * @param string $key
1252
+	 *
1253
+	 * @return void
1254
+	 */
1255
+	public function offsetUnset($key)
1256
+	{
1257
+		// TODO : think of the use of it into a ProxyCollection
1258
+		// context
1259
+		$this->initializeProxy();
1260
+
1261
+		return parent::offsetUnset($key);
1262
+	}
1263
+
1264
+	/**
1265
+	 * Dynamically access collection proxies.
1266
+	 *
1267
+	 * @param string $key
1268
+	 *
1269
+	 * @throws \Exception
1270
+	 *
1271
+	 * @return mixed
1272
+	 */
1273
+	public function __get($key)
1274
+	{
1275
+		parent::__get($key);
1276
+	}
1277
+
1278
+	/**
1279
+	 * Dynamically handle calls to the class.
1280
+	 *
1281
+	 * @param string $method
1282
+	 * @param array  $parameters
1283
+	 *
1284
+	 * @throws \BadMethodCallException
1285
+	 *
1286
+	 * @return mixed
1287
+	 */
1288
+	public function __call($method, $parameters)
1289
+	{
1290
+		$this->initializeProxy();
1291
+
1292
+		return parent::__call($method, $parameters);
1293
+	}
1294 1294
 }
Please login to merge, or discard this patch.
src/EntityCollection.php 1 patch
Indentation   +445 added lines, -445 removed lines patch added patch discarded remove patch
@@ -10,449 +10,449 @@
 block discarded – undo
10 10
 
11 11
 class EntityCollection extends Collection
12 12
 {
13
-    /**
14
-     * Wrapper Factory.
15
-     *
16
-     * @var \Analogue\ORM\System\Wrappers\Factory
17
-     */
18
-    protected $factory;
19
-
20
-    /**
21
-     * EntityCollection constructor.
22
-     *
23
-     * @param array|null $entities
24
-     */
25
-    public function __construct(array $entities = null)
26
-    {
27
-        $this->factory = new Factory();
28
-
29
-        parent::__construct($entities);
30
-    }
31
-
32
-    /**
33
-     * Find an entity in the collection by key.
34
-     *
35
-     * @param mixed $key
36
-     * @param mixed $default
37
-     *
38
-     * @throws MappingException
39
-     *
40
-     * @return \Analogue\ORM\Entity
41
-     */
42
-    public function find($key, $default = null)
43
-    {
44
-        if ($key instanceof Mappable) {
45
-            $key = $this->getEntityKey($key);
46
-        }
47
-
48
-        return array_first($this->items, function ($entity, $itemKey) use ($key) {
49
-            return $this->getEntityKey($entity) == $key;
50
-        }, $default);
51
-    }
52
-
53
-    /**
54
-     * Add an entity to the collection.
55
-     *
56
-     * @param Mappable $entity
57
-     *
58
-     * @return $this
59
-     */
60
-    public function add($entity)
61
-    {
62
-        $this->push($entity);
63
-
64
-        return $this;
65
-    }
66
-
67
-    /**
68
-     * Remove an entity from the collection.
69
-     *
70
-     * @param $entity
71
-     *
72
-     * @throws MappingException
73
-     *
74
-     * @return mixed
75
-     */
76
-    public function remove($entity)
77
-    {
78
-        $key = $this->getEntityKey($entity);
79
-
80
-        return $this->pull($key);
81
-    }
82
-
83
-    /**
84
-     * Push an item onto the end of the collection.
85
-     *
86
-     * @param mixed $value
87
-     *
88
-     * @return void
89
-     */
90
-    public function push($value)
91
-    {
92
-        $this->offsetSet(null, $value);
93
-    }
94
-
95
-    /**
96
-     * Put an item in the collection by key.
97
-     *
98
-     * @param mixed $key
99
-     * @param mixed $value
100
-     *
101
-     * @return void
102
-     */
103
-    public function put($key, $value)
104
-    {
105
-        $this->offsetSet($key, $value);
106
-    }
107
-
108
-    /**
109
-     * Set the item at a given offset.
110
-     *
111
-     * @param mixed $key
112
-     * @param mixed $value
113
-     *
114
-     * @return void
115
-     */
116
-    public function offsetSet($key, $value)
117
-    {
118
-        if (is_null($key)) {
119
-            $this->items[] = $value;
120
-        } else {
121
-            $this->items[$key] = $value;
122
-        }
123
-    }
124
-
125
-    /**
126
-     * Determine if a key exists in the collection.
127
-     *
128
-     * @param mixed      $key
129
-     * @param mixed|null $value
130
-     *
131
-     * @return bool
132
-     */
133
-    // public function contains($key, $value = null)
134
-    // {
135
-    //     if (func_num_args() == 2) {
136
-    //         return !$this->where($key, $value)->isEmpty();
137
-    //     }
138
-
139
-    //     if ($this->useAsCallable($key)) {
140
-    //         return !is_null($this->first($key));
141
-    //     }
142
-
143
-    //     return !is_null($this->find($key));
144
-    // }
145
-
146
-    /**
147
-     * Fetch a nested element of the collection.
148
-     *
149
-     * @param string $key
150
-     *
151
-     * @return self
152
-     */
153
-    public function fetch($key)
154
-    {
155
-        return new static(array_fetch($this->toArray(), $key));
156
-    }
157
-
158
-    /**
159
-     * Generic function for returning class.key value pairs.
160
-     *
161
-     * @throws MappingException
162
-     *
163
-     * @return string
164
-     */
165
-    public function getEntityHashes()
166
-    {
167
-        return array_map(function ($entity) {
168
-            $class = get_class($entity);
169
-
170
-            $mapper = Manager::getMapper($class);
171
-
172
-            $keyName = $mapper->getEntityMap()->getKeyName();
173
-
174
-            return $class.'.'.$entity->getEntityAttribute($keyName);
175
-        },
176
-        $this->items);
177
-    }
178
-
179
-    /**
180
-     * Get a subset of the collection from entity hashes.
181
-     *
182
-     * @param array $hashes
183
-     *
184
-     * @throws MappingException
185
-     *
186
-     * @return array
187
-     */
188
-    public function getSubsetByHashes(array $hashes)
189
-    {
190
-        $subset = [];
191
-
192
-        foreach ($this->items as $item) {
193
-            $class = get_class($item);
194
-
195
-            $mapper = Manager::getMapper($class);
196
-
197
-            $keyName = $mapper->getEntityMap()->getKeyName();
198
-
199
-            if (in_array($class.'.'.$item->$keyName, $hashes)) {
200
-                $subset[] = $item;
201
-            }
202
-        }
203
-
204
-        return $subset;
205
-    }
206
-
207
-    /**
208
-     * Merge the collection with the given items.
209
-     *
210
-     * @param array $items
211
-     *
212
-     * @throws MappingException
213
-     *
214
-     * @return self
215
-     */
216
-    public function merge($items)
217
-    {
218
-        $dictionary = $this->getDictionary();
219
-
220
-        foreach ($items as $item) {
221
-            $dictionary[$this->getEntityKey($item)] = $item;
222
-        }
223
-
224
-        return new static(array_values($dictionary));
225
-    }
226
-
227
-    /**
228
-     * Diff the collection with the given items.
229
-     *
230
-     * @param \ArrayAccess|array $items
231
-     *
232
-     * @return self
233
-     */
234
-    public function diff($items)
235
-    {
236
-        $diff = new static();
237
-
238
-        $dictionary = $this->getDictionary($items);
239
-
240
-        foreach ($this->items as $item) {
241
-            if (!isset($dictionary[$this->getEntityKey($item)])) {
242
-                $diff->add($item);
243
-            }
244
-        }
245
-
246
-        return $diff;
247
-    }
248
-
249
-    /**
250
-     * Intersect the collection with the given items.
251
-     *
252
-     * @param \ArrayAccess|array $items
253
-     *
254
-     * @throws MappingException
255
-     *
256
-     * @return self
257
-     */
258
-    public function intersect($items)
259
-    {
260
-        $intersect = new static();
261
-
262
-        $dictionary = $this->getDictionary($items);
263
-
264
-        foreach ($this->items as $item) {
265
-            if (isset($dictionary[$this->getEntityKey($item)])) {
266
-                $intersect->add($item);
267
-            }
268
-        }
269
-
270
-        return $intersect;
271
-    }
272
-
273
-    /**
274
-     * Returns only the models from the collection with the specified keys.
275
-     *
276
-     * @param mixed $keys
277
-     *
278
-     * @return self
279
-     */
280
-    public function only($keys)
281
-    {
282
-        $dictionary = array_only($this->getDictionary(), $keys);
283
-
284
-        return new static(array_values($dictionary));
285
-    }
286
-
287
-    /**
288
-     * Returns all models in the collection except the models with specified keys.
289
-     *
290
-     * @param mixed $keys
291
-     *
292
-     * @return self
293
-     */
294
-    public function except($keys)
295
-    {
296
-        $dictionary = array_except($this->getDictionary(), $keys);
297
-
298
-        return new static(array_values($dictionary));
299
-    }
300
-
301
-    /**
302
-     * Get a dictionary keyed by primary keys.
303
-     *
304
-     * @param \ArrayAccess|array $items
305
-     *
306
-     * @throws MappingException
307
-     *
308
-     * @return array
309
-     */
310
-    public function getDictionary($items = null)
311
-    {
312
-        $items = is_null($items) ? $this->items : $items;
313
-
314
-        $dictionary = [];
315
-
316
-        foreach ($items as $value) {
317
-            $dictionary[$this->getEntityKey($value)] = $value;
318
-        }
319
-
320
-        return $dictionary;
321
-    }
322
-
323
-    /**
324
-     * @throws MappingException
325
-     *
326
-     * @return array
327
-     */
328
-    public function getEntityKeys()
329
-    {
330
-        return array_keys($this->getDictionary());
331
-    }
332
-
333
-    /**
334
-     * @param $entity
335
-     *
336
-     * @throws MappingException
337
-     *
338
-     * @return mixed
339
-     */
340
-    protected function getEntityKey($entity)
341
-    {
342
-        $keyName = Manager::getMapper($entity)->getEntityMap()->getKeyName();
343
-
344
-        $wrapper = $this->factory->make($entity);
345
-
346
-        return $wrapper->getEntityAttribute($keyName);
347
-    }
348
-
349
-    /**
350
-     * Get the max value of a given key.
351
-     *
352
-     * @param string|null $key
353
-     *
354
-     * @throws MappingException
355
-     *
356
-     * @return mixed
357
-     */
358
-    public function max($key = null)
359
-    {
360
-        return $this->reduce(function ($result, $item) use ($key) {
361
-            $wrapper = $this->factory->make($item);
362
-
363
-            return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
364
-                $wrapper->getEntityAttribute($key) : $result;
365
-        });
366
-    }
367
-
368
-    /**
369
-     * Get the min value of a given key.
370
-     *
371
-     * @param string|null $key
372
-     *
373
-     * @throws MappingException
374
-     *
375
-     * @return mixed
376
-     */
377
-    public function min($key = null)
378
-    {
379
-        return $this->reduce(function ($result, $item) use ($key) {
380
-            $wrapper = $this->factory->make($item);
381
-
382
-            return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
383
-                ? $wrapper->getEntityAttribute($key) : $result;
384
-        });
385
-    }
386
-
387
-    /**
388
-     * Get an array with the values of a given key.
389
-     *
390
-     * @param string      $value
391
-     * @param string|null $key
392
-     *
393
-     * @return self
394
-     */
395
-    public function pluck($value, $key = null)
396
-    {
397
-        return new Collection(Arr::pluck($this->items, $value, $key));
398
-    }
399
-
400
-    /**
401
-     * Alias for the "pluck" method.
402
-     *
403
-     * @param string      $value
404
-     * @param string|null $key
405
-     *
406
-     * @return self
407
-     */
408
-    public function lists($value, $key = null)
409
-    {
410
-        return $this->pluck($value, $key);
411
-    }
412
-
413
-    /**
414
-     * Return only unique items from the collection.
415
-     *
416
-     * @param string|null $key
417
-     * @param bool        $strict
418
-     *
419
-     * @throws MappingException
420
-     *
421
-     * @return self
422
-     */
423
-    public function unique($key = null, $strict = false)
424
-    {
425
-        $dictionary = $this->getDictionary();
426
-
427
-        return new static(array_values($dictionary));
428
-    }
429
-
430
-    /**
431
-     * Get a base Support collection instance from this collection.
432
-     *
433
-     * @return \Illuminate\Support\Collection
434
-     */
435
-    public function toBase()
436
-    {
437
-        return new Collection($this->items);
438
-    }
439
-
440
-    public function toArray()
441
-    {
442
-        return array_values(parent::toArray());
443
-    }
444
-
445
-    /**
446
-     * Get the collection of items as JSON.
447
-     *
448
-     * @param int $options
449
-     *
450
-     * @return string
451
-     */
452
-    public function toJson($options = 0)
453
-    {
454
-        $collection = new Collection(array_values($this->items));
455
-
456
-        return $collection->toJson($options);
457
-    }
13
+	/**
14
+	 * Wrapper Factory.
15
+	 *
16
+	 * @var \Analogue\ORM\System\Wrappers\Factory
17
+	 */
18
+	protected $factory;
19
+
20
+	/**
21
+	 * EntityCollection constructor.
22
+	 *
23
+	 * @param array|null $entities
24
+	 */
25
+	public function __construct(array $entities = null)
26
+	{
27
+		$this->factory = new Factory();
28
+
29
+		parent::__construct($entities);
30
+	}
31
+
32
+	/**
33
+	 * Find an entity in the collection by key.
34
+	 *
35
+	 * @param mixed $key
36
+	 * @param mixed $default
37
+	 *
38
+	 * @throws MappingException
39
+	 *
40
+	 * @return \Analogue\ORM\Entity
41
+	 */
42
+	public function find($key, $default = null)
43
+	{
44
+		if ($key instanceof Mappable) {
45
+			$key = $this->getEntityKey($key);
46
+		}
47
+
48
+		return array_first($this->items, function ($entity, $itemKey) use ($key) {
49
+			return $this->getEntityKey($entity) == $key;
50
+		}, $default);
51
+	}
52
+
53
+	/**
54
+	 * Add an entity to the collection.
55
+	 *
56
+	 * @param Mappable $entity
57
+	 *
58
+	 * @return $this
59
+	 */
60
+	public function add($entity)
61
+	{
62
+		$this->push($entity);
63
+
64
+		return $this;
65
+	}
66
+
67
+	/**
68
+	 * Remove an entity from the collection.
69
+	 *
70
+	 * @param $entity
71
+	 *
72
+	 * @throws MappingException
73
+	 *
74
+	 * @return mixed
75
+	 */
76
+	public function remove($entity)
77
+	{
78
+		$key = $this->getEntityKey($entity);
79
+
80
+		return $this->pull($key);
81
+	}
82
+
83
+	/**
84
+	 * Push an item onto the end of the collection.
85
+	 *
86
+	 * @param mixed $value
87
+	 *
88
+	 * @return void
89
+	 */
90
+	public function push($value)
91
+	{
92
+		$this->offsetSet(null, $value);
93
+	}
94
+
95
+	/**
96
+	 * Put an item in the collection by key.
97
+	 *
98
+	 * @param mixed $key
99
+	 * @param mixed $value
100
+	 *
101
+	 * @return void
102
+	 */
103
+	public function put($key, $value)
104
+	{
105
+		$this->offsetSet($key, $value);
106
+	}
107
+
108
+	/**
109
+	 * Set the item at a given offset.
110
+	 *
111
+	 * @param mixed $key
112
+	 * @param mixed $value
113
+	 *
114
+	 * @return void
115
+	 */
116
+	public function offsetSet($key, $value)
117
+	{
118
+		if (is_null($key)) {
119
+			$this->items[] = $value;
120
+		} else {
121
+			$this->items[$key] = $value;
122
+		}
123
+	}
124
+
125
+	/**
126
+	 * Determine if a key exists in the collection.
127
+	 *
128
+	 * @param mixed      $key
129
+	 * @param mixed|null $value
130
+	 *
131
+	 * @return bool
132
+	 */
133
+	// public function contains($key, $value = null)
134
+	// {
135
+	//     if (func_num_args() == 2) {
136
+	//         return !$this->where($key, $value)->isEmpty();
137
+	//     }
138
+
139
+	//     if ($this->useAsCallable($key)) {
140
+	//         return !is_null($this->first($key));
141
+	//     }
142
+
143
+	//     return !is_null($this->find($key));
144
+	// }
145
+
146
+	/**
147
+	 * Fetch a nested element of the collection.
148
+	 *
149
+	 * @param string $key
150
+	 *
151
+	 * @return self
152
+	 */
153
+	public function fetch($key)
154
+	{
155
+		return new static(array_fetch($this->toArray(), $key));
156
+	}
157
+
158
+	/**
159
+	 * Generic function for returning class.key value pairs.
160
+	 *
161
+	 * @throws MappingException
162
+	 *
163
+	 * @return string
164
+	 */
165
+	public function getEntityHashes()
166
+	{
167
+		return array_map(function ($entity) {
168
+			$class = get_class($entity);
169
+
170
+			$mapper = Manager::getMapper($class);
171
+
172
+			$keyName = $mapper->getEntityMap()->getKeyName();
173
+
174
+			return $class.'.'.$entity->getEntityAttribute($keyName);
175
+		},
176
+		$this->items);
177
+	}
178
+
179
+	/**
180
+	 * Get a subset of the collection from entity hashes.
181
+	 *
182
+	 * @param array $hashes
183
+	 *
184
+	 * @throws MappingException
185
+	 *
186
+	 * @return array
187
+	 */
188
+	public function getSubsetByHashes(array $hashes)
189
+	{
190
+		$subset = [];
191
+
192
+		foreach ($this->items as $item) {
193
+			$class = get_class($item);
194
+
195
+			$mapper = Manager::getMapper($class);
196
+
197
+			$keyName = $mapper->getEntityMap()->getKeyName();
198
+
199
+			if (in_array($class.'.'.$item->$keyName, $hashes)) {
200
+				$subset[] = $item;
201
+			}
202
+		}
203
+
204
+		return $subset;
205
+	}
206
+
207
+	/**
208
+	 * Merge the collection with the given items.
209
+	 *
210
+	 * @param array $items
211
+	 *
212
+	 * @throws MappingException
213
+	 *
214
+	 * @return self
215
+	 */
216
+	public function merge($items)
217
+	{
218
+		$dictionary = $this->getDictionary();
219
+
220
+		foreach ($items as $item) {
221
+			$dictionary[$this->getEntityKey($item)] = $item;
222
+		}
223
+
224
+		return new static(array_values($dictionary));
225
+	}
226
+
227
+	/**
228
+	 * Diff the collection with the given items.
229
+	 *
230
+	 * @param \ArrayAccess|array $items
231
+	 *
232
+	 * @return self
233
+	 */
234
+	public function diff($items)
235
+	{
236
+		$diff = new static();
237
+
238
+		$dictionary = $this->getDictionary($items);
239
+
240
+		foreach ($this->items as $item) {
241
+			if (!isset($dictionary[$this->getEntityKey($item)])) {
242
+				$diff->add($item);
243
+			}
244
+		}
245
+
246
+		return $diff;
247
+	}
248
+
249
+	/**
250
+	 * Intersect the collection with the given items.
251
+	 *
252
+	 * @param \ArrayAccess|array $items
253
+	 *
254
+	 * @throws MappingException
255
+	 *
256
+	 * @return self
257
+	 */
258
+	public function intersect($items)
259
+	{
260
+		$intersect = new static();
261
+
262
+		$dictionary = $this->getDictionary($items);
263
+
264
+		foreach ($this->items as $item) {
265
+			if (isset($dictionary[$this->getEntityKey($item)])) {
266
+				$intersect->add($item);
267
+			}
268
+		}
269
+
270
+		return $intersect;
271
+	}
272
+
273
+	/**
274
+	 * Returns only the models from the collection with the specified keys.
275
+	 *
276
+	 * @param mixed $keys
277
+	 *
278
+	 * @return self
279
+	 */
280
+	public function only($keys)
281
+	{
282
+		$dictionary = array_only($this->getDictionary(), $keys);
283
+
284
+		return new static(array_values($dictionary));
285
+	}
286
+
287
+	/**
288
+	 * Returns all models in the collection except the models with specified keys.
289
+	 *
290
+	 * @param mixed $keys
291
+	 *
292
+	 * @return self
293
+	 */
294
+	public function except($keys)
295
+	{
296
+		$dictionary = array_except($this->getDictionary(), $keys);
297
+
298
+		return new static(array_values($dictionary));
299
+	}
300
+
301
+	/**
302
+	 * Get a dictionary keyed by primary keys.
303
+	 *
304
+	 * @param \ArrayAccess|array $items
305
+	 *
306
+	 * @throws MappingException
307
+	 *
308
+	 * @return array
309
+	 */
310
+	public function getDictionary($items = null)
311
+	{
312
+		$items = is_null($items) ? $this->items : $items;
313
+
314
+		$dictionary = [];
315
+
316
+		foreach ($items as $value) {
317
+			$dictionary[$this->getEntityKey($value)] = $value;
318
+		}
319
+
320
+		return $dictionary;
321
+	}
322
+
323
+	/**
324
+	 * @throws MappingException
325
+	 *
326
+	 * @return array
327
+	 */
328
+	public function getEntityKeys()
329
+	{
330
+		return array_keys($this->getDictionary());
331
+	}
332
+
333
+	/**
334
+	 * @param $entity
335
+	 *
336
+	 * @throws MappingException
337
+	 *
338
+	 * @return mixed
339
+	 */
340
+	protected function getEntityKey($entity)
341
+	{
342
+		$keyName = Manager::getMapper($entity)->getEntityMap()->getKeyName();
343
+
344
+		$wrapper = $this->factory->make($entity);
345
+
346
+		return $wrapper->getEntityAttribute($keyName);
347
+	}
348
+
349
+	/**
350
+	 * Get the max value of a given key.
351
+	 *
352
+	 * @param string|null $key
353
+	 *
354
+	 * @throws MappingException
355
+	 *
356
+	 * @return mixed
357
+	 */
358
+	public function max($key = null)
359
+	{
360
+		return $this->reduce(function ($result, $item) use ($key) {
361
+			$wrapper = $this->factory->make($item);
362
+
363
+			return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
364
+				$wrapper->getEntityAttribute($key) : $result;
365
+		});
366
+	}
367
+
368
+	/**
369
+	 * Get the min value of a given key.
370
+	 *
371
+	 * @param string|null $key
372
+	 *
373
+	 * @throws MappingException
374
+	 *
375
+	 * @return mixed
376
+	 */
377
+	public function min($key = null)
378
+	{
379
+		return $this->reduce(function ($result, $item) use ($key) {
380
+			$wrapper = $this->factory->make($item);
381
+
382
+			return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
383
+				? $wrapper->getEntityAttribute($key) : $result;
384
+		});
385
+	}
386
+
387
+	/**
388
+	 * Get an array with the values of a given key.
389
+	 *
390
+	 * @param string      $value
391
+	 * @param string|null $key
392
+	 *
393
+	 * @return self
394
+	 */
395
+	public function pluck($value, $key = null)
396
+	{
397
+		return new Collection(Arr::pluck($this->items, $value, $key));
398
+	}
399
+
400
+	/**
401
+	 * Alias for the "pluck" method.
402
+	 *
403
+	 * @param string      $value
404
+	 * @param string|null $key
405
+	 *
406
+	 * @return self
407
+	 */
408
+	public function lists($value, $key = null)
409
+	{
410
+		return $this->pluck($value, $key);
411
+	}
412
+
413
+	/**
414
+	 * Return only unique items from the collection.
415
+	 *
416
+	 * @param string|null $key
417
+	 * @param bool        $strict
418
+	 *
419
+	 * @throws MappingException
420
+	 *
421
+	 * @return self
422
+	 */
423
+	public function unique($key = null, $strict = false)
424
+	{
425
+		$dictionary = $this->getDictionary();
426
+
427
+		return new static(array_values($dictionary));
428
+	}
429
+
430
+	/**
431
+	 * Get a base Support collection instance from this collection.
432
+	 *
433
+	 * @return \Illuminate\Support\Collection
434
+	 */
435
+	public function toBase()
436
+	{
437
+		return new Collection($this->items);
438
+	}
439
+
440
+	public function toArray()
441
+	{
442
+		return array_values(parent::toArray());
443
+	}
444
+
445
+	/**
446
+	 * Get the collection of items as JSON.
447
+	 *
448
+	 * @param int $options
449
+	 *
450
+	 * @return string
451
+	 */
452
+	public function toJson($options = 0)
453
+	{
454
+		$collection = new Collection(array_values($this->items));
455
+
456
+		return $collection->toJson($options);
457
+	}
458 458
 }
Please login to merge, or discard this patch.
src/Plugins/Timestamps/TimestampsPlugin.php 2 patches
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -11,54 +11,54 @@
 block discarded – undo
11 11
  */
12 12
 class TimestampsPlugin extends AnaloguePlugin
13 13
 {
14
-    /**
15
-     * Register the plugin.
16
-     *
17
-     * @throws \Exception
18
-     *
19
-     * @return void
20
-     */
21
-    public function register()
22
-    {
23
-        $this->manager->registerGlobalEvent('initialized', function ($event, $payload) {
24
-            $mapper = $payload[0];
25
-            $entityMap = $mapper->getEntityMap();
14
+	/**
15
+	 * Register the plugin.
16
+	 *
17
+	 * @throws \Exception
18
+	 *
19
+	 * @return void
20
+	 */
21
+	public function register()
22
+	{
23
+		$this->manager->registerGlobalEvent('initialized', function ($event, $payload) {
24
+			$mapper = $payload[0];
25
+			$entityMap = $mapper->getEntityMap();
26 26
 
27
-            if ($entityMap->usesTimestamps()) {
28
-                $mapper->registerEvent('creating', function ($entity) use ($entityMap) {
29
-                    $factory = new Factory();
30
-                    $wrappedEntity = $factory->make($entity);
27
+			if ($entityMap->usesTimestamps()) {
28
+				$mapper->registerEvent('creating', function ($entity) use ($entityMap) {
29
+					$factory = new Factory();
30
+					$wrappedEntity = $factory->make($entity);
31 31
 
32
-                    $createdAtField = $entityMap->getCreatedAtColumn();
33
-                    $updatedAtField = $entityMap->getUpdatedAtColumn();
32
+					$createdAtField = $entityMap->getCreatedAtColumn();
33
+					$updatedAtField = $entityMap->getUpdatedAtColumn();
34 34
 
35
-                    $time = new Carbon();
35
+					$time = new Carbon();
36 36
 
37
-                    $wrappedEntity->setEntityAttribute($createdAtField, $time);
38
-                    $wrappedEntity->setEntityAttribute($updatedAtField, $time);
39
-                });
37
+					$wrappedEntity->setEntityAttribute($createdAtField, $time);
38
+					$wrappedEntity->setEntityAttribute($updatedAtField, $time);
39
+				});
40 40
 
41
-                $mapper->registerEvent('updating', function ($entity) use ($entityMap) {
42
-                    $factory = new Factory();
43
-                    $wrappedEntity = $factory->make($entity);
41
+				$mapper->registerEvent('updating', function ($entity) use ($entityMap) {
42
+					$factory = new Factory();
43
+					$wrappedEntity = $factory->make($entity);
44 44
 
45
-                    $updatedAtField = $entityMap->getUpdatedAtColumn();
45
+					$updatedAtField = $entityMap->getUpdatedAtColumn();
46 46
 
47
-                    $time = new Carbon();
47
+					$time = new Carbon();
48 48
 
49
-                    $wrappedEntity->setEntityAttribute($updatedAtField, $time);
50
-                });
51
-            }
52
-        });
53
-    }
49
+					$wrappedEntity->setEntityAttribute($updatedAtField, $time);
50
+				});
51
+			}
52
+		});
53
+	}
54 54
 
55
-    /**
56
-     * Get custom events provided by the plugin.
57
-     *
58
-     * @return array
59
-     */
60
-    public function getCustomEvents()
61
-    {
62
-        return [];
63
-    }
55
+	/**
56
+	 * Get custom events provided by the plugin.
57
+	 *
58
+	 * @return array
59
+	 */
60
+	public function getCustomEvents()
61
+	{
62
+		return [];
63
+	}
64 64
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -20,12 +20,12 @@  discard block
 block discarded – undo
20 20
      */
21 21
     public function register()
22 22
     {
23
-        $this->manager->registerGlobalEvent('initialized', function ($event, $payload) {
23
+        $this->manager->registerGlobalEvent('initialized', function($event, $payload) {
24 24
             $mapper = $payload[0];
25 25
             $entityMap = $mapper->getEntityMap();
26 26
 
27 27
             if ($entityMap->usesTimestamps()) {
28
-                $mapper->registerEvent('creating', function ($entity) use ($entityMap) {
28
+                $mapper->registerEvent('creating', function($entity) use ($entityMap) {
29 29
                     $factory = new Factory();
30 30
                     $wrappedEntity = $factory->make($entity);
31 31
 
@@ -38,7 +38,7 @@  discard block
 block discarded – undo
38 38
                     $wrappedEntity->setEntityAttribute($updatedAtField, $time);
39 39
                 });
40 40
 
41
-                $mapper->registerEvent('updating', function ($entity) use ($entityMap) {
41
+                $mapper->registerEvent('updating', function($entity) use ($entityMap) {
42 42
                     $factory = new Factory();
43 43
                     $wrappedEntity = $factory->make($entity);
44 44
 
Please login to merge, or discard this patch.
src/Plugins/SoftDeletes/SoftDeletesPlugin.php 2 patches
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -13,82 +13,82 @@
 block discarded – undo
13 13
  */
14 14
 class SoftDeletesPlugin extends AnaloguePlugin
15 15
 {
16
-    /**
17
-     * Register the plugin.
18
-     *
19
-     * @throws \Exception
20
-     *
21
-     * @return void
22
-     */
23
-    public function register()
24
-    {
25
-        $host = $this;
26
-
27
-        // Hook any mapper init and check the mapping include soft deletes.
28
-        $this->manager->registerGlobalEvent('initialized', function ($event, $payload) use ($host) {
29
-            $mapper = $payload[0];
30
-            $entityMap = $mapper->getEntityMap();
31
-
32
-            if ($entityMap->usesSoftDeletes()) {
33
-                $host->registerSoftDelete($mapper);
34
-            }
35
-        });
36
-    }
37
-
38
-    /**
39
-     * By hooking to the mapper initialization event, we can extend it
40
-     * with the softDelete capacity.
41
-     *
42
-     * @param \Analogue\ORM\System\Mapper $mapper
43
-     *
44
-     * @throws \Analogue\ORM\Exceptions\MappingException
45
-     *
46
-     * @return bool|void
47
-     */
48
-    protected function registerSoftDelete(Mapper $mapper)
49
-    {
50
-        $entityMap = $mapper->getEntityMap();
51
-
52
-        // Add Scopes
53
-        $mapper->addGlobalScope(new SoftDeletingScope());
54
-
55
-        $host = $this;
56
-
57
-        // Register 'deleting' events
58
-        $mapper->registerEvent('deleting', function ($entity) use ($entityMap, $host) {
59
-
60
-            // Convert Entity into an EntityWrapper
61
-            $factory = new Factory();
62
-
63
-            $wrappedEntity = $factory->make($entity);
64
-
65
-            $deletedAtField = $entityMap->getQualifiedDeletedAtColumn();
66
-
67
-            if (!is_null($wrappedEntity->getEntityAttribute($deletedAtField))) {
68
-                return true;
69
-            } else {
70
-                $time = new Carbon();
71
-
72
-                $wrappedEntity->setEntityAttribute($deletedAtField, $time);
73
-
74
-                $plainObject = $wrappedEntity->getObject();
75
-                $host->manager->mapper(get_class($plainObject))->store($plainObject);
76
-
77
-                return false;
78
-            }
79
-        });
80
-
81
-        // Register RestoreCommand
82
-        $mapper->addCustomCommand('Analogue\ORM\Plugins\SoftDeletes\Restore');
83
-    }
84
-
85
-    /**
86
-     * Get custom events provided by the plugin.
87
-     *
88
-     * @return string[]
89
-     */
90
-    public function getCustomEvents()
91
-    {
92
-        return ['restoring', 'restored'];
93
-    }
16
+	/**
17
+	 * Register the plugin.
18
+	 *
19
+	 * @throws \Exception
20
+	 *
21
+	 * @return void
22
+	 */
23
+	public function register()
24
+	{
25
+		$host = $this;
26
+
27
+		// Hook any mapper init and check the mapping include soft deletes.
28
+		$this->manager->registerGlobalEvent('initialized', function ($event, $payload) use ($host) {
29
+			$mapper = $payload[0];
30
+			$entityMap = $mapper->getEntityMap();
31
+
32
+			if ($entityMap->usesSoftDeletes()) {
33
+				$host->registerSoftDelete($mapper);
34
+			}
35
+		});
36
+	}
37
+
38
+	/**
39
+	 * By hooking to the mapper initialization event, we can extend it
40
+	 * with the softDelete capacity.
41
+	 *
42
+	 * @param \Analogue\ORM\System\Mapper $mapper
43
+	 *
44
+	 * @throws \Analogue\ORM\Exceptions\MappingException
45
+	 *
46
+	 * @return bool|void
47
+	 */
48
+	protected function registerSoftDelete(Mapper $mapper)
49
+	{
50
+		$entityMap = $mapper->getEntityMap();
51
+
52
+		// Add Scopes
53
+		$mapper->addGlobalScope(new SoftDeletingScope());
54
+
55
+		$host = $this;
56
+
57
+		// Register 'deleting' events
58
+		$mapper->registerEvent('deleting', function ($entity) use ($entityMap, $host) {
59
+
60
+			// Convert Entity into an EntityWrapper
61
+			$factory = new Factory();
62
+
63
+			$wrappedEntity = $factory->make($entity);
64
+
65
+			$deletedAtField = $entityMap->getQualifiedDeletedAtColumn();
66
+
67
+			if (!is_null($wrappedEntity->getEntityAttribute($deletedAtField))) {
68
+				return true;
69
+			} else {
70
+				$time = new Carbon();
71
+
72
+				$wrappedEntity->setEntityAttribute($deletedAtField, $time);
73
+
74
+				$plainObject = $wrappedEntity->getObject();
75
+				$host->manager->mapper(get_class($plainObject))->store($plainObject);
76
+
77
+				return false;
78
+			}
79
+		});
80
+
81
+		// Register RestoreCommand
82
+		$mapper->addCustomCommand('Analogue\ORM\Plugins\SoftDeletes\Restore');
83
+	}
84
+
85
+	/**
86
+	 * Get custom events provided by the plugin.
87
+	 *
88
+	 * @return string[]
89
+	 */
90
+	public function getCustomEvents()
91
+	{
92
+		return ['restoring', 'restored'];
93
+	}
94 94
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -25,7 +25,7 @@  discard block
 block discarded – undo
25 25
         $host = $this;
26 26
 
27 27
         // Hook any mapper init and check the mapping include soft deletes.
28
-        $this->manager->registerGlobalEvent('initialized', function ($event, $payload) use ($host) {
28
+        $this->manager->registerGlobalEvent('initialized', function($event, $payload) use ($host) {
29 29
             $mapper = $payload[0];
30 30
             $entityMap = $mapper->getEntityMap();
31 31
 
@@ -55,7 +55,7 @@  discard block
 block discarded – undo
55 55
         $host = $this;
56 56
 
57 57
         // Register 'deleting' events
58
-        $mapper->registerEvent('deleting', function ($entity) use ($entityMap, $host) {
58
+        $mapper->registerEvent('deleting', function($entity) use ($entityMap, $host) {
59 59
 
60 60
             // Convert Entity into an EntityWrapper
61 61
             $factory = new Factory();
Please login to merge, or discard this patch.
src/EntityMap.php 1 patch
Indentation   +1432 added lines, -1432 removed lines patch added patch discarded remove patch
@@ -23,1436 +23,1436 @@
 block discarded – undo
23 23
  */
24 24
 class EntityMap
25 25
 {
26
-    /**
27
-     * The mapping driver to use with this entity.
28
-     *
29
-     * @var string
30
-     */
31
-    protected $driver = 'illuminate';
32
-
33
-    /**
34
-     * The Database Connection name for the model.
35
-     *
36
-     * @var string
37
-     */
38
-    protected $connection;
39
-
40
-    /**
41
-     * The table associated with the entity.
42
-     *
43
-     * @var string|null
44
-     */
45
-    protected $table = null;
46
-
47
-    /**
48
-     * The primary key for the model.
49
-     *
50
-     * @var string
51
-     */
52
-    protected $primaryKey = 'id';
53
-
54
-    /**
55
-     * Name of the entity's array property that should
56
-     * contain the attributes.
57
-     * If set to null, analogue will only hydrate object's properties.
58
-     *
59
-     * @var string|null
60
-     */
61
-    protected $arrayName = 'attributes';
62
-
63
-    /**
64
-     * Array containing the list of database columns to be mapped
65
-     * in the attributes array of the entity.
66
-     *
67
-     * @var array
68
-     */
69
-    protected $attributes = [];
70
-
71
-    /**
72
-     * Array containing the list of database columns to be mapped
73
-     * to the entity's class properties.
74
-     *
75
-     * @var array
76
-     */
77
-    protected $properties = [];
78
-
79
-    /**
80
-     * The Custom Domain Class to use with this mapping.
81
-     *
82
-     * @var string|null
83
-     */
84
-    protected $class = null;
85
-
86
-    /**
87
-     * Embedded Value Objects.
88
-     *
89
-     * @var array
90
-     */
91
-    protected $embeddables = [];
92
-
93
-    /**
94
-     * Determine the relationships method used on the entity.
95
-     * If not set, mapper will autodetect them.
96
-     *
97
-     * @var array
98
-     */
99
-    private $relationships = [];
100
-
101
-    /**
102
-     * Relationships that should be treated as collection.
103
-     *
104
-     * @var array
105
-     */
106
-    private $manyRelations = [];
107
-
108
-    /**
109
-     * Relationships that should be treated as single entity.
110
-     *
111
-     * @var array
112
-     */
113
-    private $singleRelations = [];
114
-
115
-    /**
116
-     * Relationships for which the key is stored in the Entity itself.
117
-     *
118
-     * @var array
119
-     */
120
-    private $localRelations = [];
121
-
122
-    /**
123
-     * List of local keys associated to local relation methods.
124
-     *
125
-     * @var array
126
-     */
127
-    private $localForeignKeys = [];
128
-
129
-    /**
130
-     * Relationships for which the key is stored in the Related Entity.
131
-     *
132
-     * @var array
133
-     */
134
-    private $foreignRelations = [];
135
-
136
-    /**
137
-     * Relationships which use a pivot record.
138
-     *
139
-     * @var array
140
-     */
141
-    private $pivotRelations = [];
142
-
143
-    /**
144
-     * Dynamic relationships.
145
-     *
146
-     * @var array
147
-     */
148
-    private $dynamicRelationships = [];
149
-
150
-    /**
151
-     * Targetted class for the relationship method. value is set to `null` for
152
-     * polymorphic relations.
153
-     *
154
-     * @var array
155
-     */
156
-    private $relatedClasses = [];
157
-
158
-    /**
159
-     * Some relation methods like embedded objects, or HasOne and MorphOne,
160
-     * will never have a proxy loaded on them.
161
-     *
162
-     * @var array
163
-     */
164
-    private $nonProxyRelationships = [];
165
-
166
-    /**
167
-     * The number of models to return for pagination.
168
-     *
169
-     * @var int
170
-     */
171
-    protected $perPage = 15;
172
-
173
-    /**
174
-     * The relations to eager load on every query.
175
-     *
176
-     * @var array
177
-     */
178
-    protected $with = [];
179
-
180
-    /**
181
-     * The class name to be used in polymorphic relations.
182
-     *
183
-     * @var string
184
-     */
185
-    protected $morphClass;
186
-
187
-    /**
188
-     * Sequence name, to be used with postgreSql
189
-     * defaults to %table_name%_id_seq.
190
-     *
191
-     * @var string|null
192
-     */
193
-    protected $sequence = null;
194
-
195
-    /**
196
-     * Indicates if the entity should be timestamped.
197
-     *
198
-     * @var bool
199
-     */
200
-    public $timestamps = false;
201
-
202
-    /**
203
-     * The name of the "created at" column.
204
-     *
205
-     * @var string
206
-     */
207
-    protected $createdAtColumn = 'created_at';
208
-
209
-    /**
210
-     * The name of the "updated at" column.
211
-     *
212
-     * @var string
213
-     */
214
-    protected $updatedAtColumn = 'updated_at';
215
-
216
-    /**
217
-     * Indicates if the entity uses softdeletes.
218
-     *
219
-     * @var bool
220
-     */
221
-    public $softDeletes = false;
222
-
223
-    /**
224
-     * The name of the "deleted at" column.
225
-     *
226
-     * @var string
227
-     */
228
-    protected $deletedAtColumn = 'deleted_at';
229
-
230
-    /**
231
-     * The date format to use with the current database connection.
232
-     *
233
-     * @var string
234
-     */
235
-    protected $dateFormat;
236
-
237
-    /**
238
-     * Set this property to true if the entity should be instantiated
239
-     * using the IoC Container.
240
-     *
241
-     * @var bool
242
-     */
243
-    protected $dependencyInjection = false;
244
-
245
-    /**
246
-     * Set the usage of inheritance, possible values are :
247
-     * "single_table"
248
-     * null.
249
-     *
250
-     * @var string | null
251
-     */
252
-    protected $inheritanceType = null;
253
-
254
-    /**
255
-     * Discriminator column name.
256
-     *
257
-     * @var string
258
-     */
259
-    protected $discriminatorColumn = 'type';
260
-
261
-    /**
262
-     * Allow using a string to define which entity type should be instantiated.
263
-     * If not set, analogue will uses entity's FQDN.
264
-     *
265
-     * @var array
266
-     */
267
-    protected $discriminatorColumnMap = [];
268
-
269
-    /**
270
-     * Return Domain class attributes, useful when mapping to a Plain PHP Object.
271
-     *
272
-     * @return array
273
-     */
274
-    public function getAttributes() : array
275
-    {
276
-        return $this->attributes;
277
-    }
278
-
279
-    /**
280
-     * Set the domain class attributes.
281
-     *
282
-     * @param array $attributeNames
283
-     */
284
-    public function setAttributes(array $attributeNames)
285
-    {
286
-        $this->attributes = $attributeNames;
287
-    }
288
-
289
-    /**
290
-     * Return true if the Entity has an 'attributes' array property.
291
-     *
292
-     * @return bool
293
-     */
294
-    public function usesAttributesArray() : bool
295
-    {
296
-        return $this->arrayName === null ? false : true;
297
-    }
298
-
299
-    /**
300
-     * Return the name of the Entity's attributes property.
301
-     *
302
-     * @return string|null
303
-     */
304
-    public function getAttributesArrayName()
305
-    {
306
-        return $this->arrayName;
307
-    }
308
-
309
-    /**
310
-     * Get all the attribute names for the class, including relationships, embeddables and primary key.
311
-     *
312
-     * @return array
313
-     */
314
-    public function getCompiledAttributes() : array
315
-    {
316
-        $key = $this->getKeyName();
317
-
318
-        $embeddables = array_keys($this->getEmbeddables());
319
-
320
-        $relationships = $this->getRelationships();
321
-
322
-        $attributes = $this->getAttributes();
323
-
324
-        return array_merge([$key], $embeddables, $relationships, $attributes);
325
-    }
326
-
327
-    /**
328
-     * Set the date format to use with the current database connection.
329
-     *
330
-     * @param string $format
331
-     */
332
-    public function setDateFormat($format)
333
-    {
334
-        $this->dateFormat = $format;
335
-    }
336
-
337
-    /**
338
-     * Get the date format to use with the current database connection.
339
-     *
340
-     *  @return string
341
-     */
342
-    public function getDateFormat() : string
343
-    {
344
-        return $this->dateFormat;
345
-    }
346
-
347
-    /**
348
-     * Set the Driver for this mapping.
349
-     *
350
-     * @param string $driver
351
-     */
352
-    public function setDriver($driver)
353
-    {
354
-        $this->driver = $driver;
355
-    }
356
-
357
-    /**
358
-     * Get the Driver for this mapping.
359
-     *
360
-     * @return string
361
-     */
362
-    public function getDriver() : string
363
-    {
364
-        return $this->driver;
365
-    }
366
-
367
-    /**
368
-     * Set the db connection to use on the table.
369
-     *
370
-     * @param $connection
371
-     */
372
-    public function setConnection($connection)
373
-    {
374
-        $this->connection = $connection;
375
-    }
376
-
377
-    /**
378
-     * Get the Database connection the Entity is stored on.
379
-     *
380
-     * @return string | null
381
-     */
382
-    public function getConnection()
383
-    {
384
-        return $this->connection;
385
-    }
386
-
387
-    /**
388
-     * Get the table associated with the entity.
389
-     *
390
-     * @return string
391
-     */
392
-    public function getTable() : string
393
-    {
394
-        if (!is_null($this->table)) {
395
-            return $this->table;
396
-        }
397
-
398
-        return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
399
-    }
400
-
401
-    /**
402
-     * Set the database table name.
403
-     *
404
-     * @param string $table
405
-     */
406
-    public function setTable($table)
407
-    {
408
-        $this->table = $table;
409
-    }
410
-
411
-    /**
412
-     * Get the pgSql sequence name.
413
-     *
414
-     * @return string
415
-     */
416
-    public function getSequence()
417
-    {
418
-        if (!is_null($this->sequence)) {
419
-            return $this->sequence;
420
-        } else {
421
-            return $this->getTable().'_id_seq';
422
-        }
423
-    }
424
-
425
-    /**
426
-     * Get the custom entity class.
427
-     *
428
-     * @return string namespaced class name
429
-     */
430
-    public function getClass() : string
431
-    {
432
-        return isset($this->class) ? $this->class : null;
433
-    }
434
-
435
-    /**
436
-     * Set the custom entity class.
437
-     *
438
-     * @param string $class namespaced class name
439
-     */
440
-    public function setClass($class)
441
-    {
442
-        $this->class = $class;
443
-    }
444
-
445
-    /**
446
-     * Get the embedded Value Objects.
447
-     *
448
-     * @return array
449
-     */
450
-    public function getEmbeddables() : array
451
-    {
452
-        return $this->embeddables;
453
-    }
454
-
455
-    /**
456
-     * Return attributes that should be mapped to class properties.
457
-     *
458
-     * @return array
459
-     */
460
-    public function getProperties() : array
461
-    {
462
-        return $this->properties;
463
-    }
464
-
465
-    /**
466
-     * Return the array property in which will be mapped all attributes
467
-     * that are not mapped to class properties.
468
-     *
469
-     * @return string
470
-     */
471
-    public function getAttributesPropertyName() : string
472
-    {
473
-    }
474
-
475
-    /**
476
-     * Set the embedded Value Objects.
477
-     *
478
-     * @param array $embeddables
479
-     */
480
-    public function setEmbeddables(array $embeddables)
481
-    {
482
-        $this->embeddables = $embeddables;
483
-    }
484
-
485
-    /**
486
-     * Get the relationships to map on a custom domain
487
-     * class.
488
-     *
489
-     * @return array
490
-     */
491
-    public function getRelationships() : array
492
-    {
493
-        return $this->relationships;
494
-    }
495
-
496
-    /**
497
-     * Get the relationships that will not have a proxy
498
-     * set on them.
499
-     *
500
-     * @return array
501
-     */
502
-    public function getRelationshipsWithoutProxy() : array
503
-    {
504
-        return $this->nonProxyRelationships;
505
-    }
506
-
507
-    /**
508
-     * Relationships of the Entity type.
509
-     *
510
-     * @return array
511
-     */
512
-    public function getSingleRelationships() : array
513
-    {
514
-        return $this->singleRelations;
515
-    }
516
-
517
-    /**
518
-     * Relationships of type Collection.
519
-     *
520
-     * @return array
521
-     */
522
-    public function getManyRelationships() : array
523
-    {
524
-        return $this->manyRelations;
525
-    }
526
-
527
-    /**
528
-     * Relationships with foreign key in the mapped entity record.
529
-     *
530
-     * @return array
531
-     */
532
-    public function getLocalRelationships() : array
533
-    {
534
-        return $this->localRelations;
535
-    }
536
-
537
-    /**
538
-     * Return the local keys associated to the relationship.
539
-     *
540
-     * @param string $relation
541
-     *
542
-     * @return string | array | null
543
-     */
544
-    public function getLocalKeys($relation)
545
-    {
546
-        return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
547
-    }
548
-
549
-    /**
550
-     * Relationships with foreign key in the related Entity record.
551
-     *
552
-     * @return array
553
-     */
554
-    public function getForeignRelationships() : array
555
-    {
556
-        return $this->foreignRelations;
557
-    }
558
-
559
-    /**
560
-     * Relationships which keys are stored in a pivot record.
561
-     *
562
-     * @return array
563
-     */
564
-    public function getPivotRelationships() : array
565
-    {
566
-        return $this->pivotRelations;
567
-    }
568
-
569
-    /**
570
-     * Get the targetted type for a relationship. Return null if polymorphic.
571
-     *
572
-     * @param string $relation
573
-     *
574
-     * @return string | null
575
-     */
576
-    public function getTargettedClass($relation)
577
-    {
578
-        return $this->relatedClasses[$relation];
579
-    }
580
-
581
-    /**
582
-     * Add a Dynamic Relationship method at runtime. This has to be done
583
-     * by hooking the 'initializing' event, before entityMap is initialized.
584
-     *
585
-     * @param string   $name         Relation name
586
-     * @param \Closure $relationship
587
-     *
588
-     * @return void
589
-     */
590
-    public function addRelationshipMethod($name, \Closure $relationship)
591
-    {
592
-        $this->dynamicRelationships[$name] = $relationship;
593
-    }
594
-
595
-    /**
596
-     * Get the dynamic relationship method names.
597
-     *
598
-     * @return array
599
-     */
600
-    public function getDynamicRelationships() : array
601
-    {
602
-        return array_keys($this->dynamicRelationships);
603
-    }
604
-
605
-    /**
606
-     * Get the relationships that have to be eager loaded
607
-     * on each request.
608
-     *
609
-     * @return array
610
-     */
611
-    public function getEagerloadedRelationships() : array
612
-    {
613
-        return $this->with;
614
-    }
615
-
616
-    /**
617
-     * Get the primary key attribute for the entity.
618
-     *
619
-     * @return string
620
-     */
621
-    public function getKeyName() : string
622
-    {
623
-        return $this->primaryKey;
624
-    }
625
-
626
-    /**
627
-     * Set the primary key for the entity.
628
-     *
629
-     * @param $key
630
-     *
631
-     * @return void
632
-     */
633
-    public function setKeyName($key)
634
-    {
635
-        $this->primaryKey = $key;
636
-    }
637
-
638
-    /**
639
-     * Get the table qualified key name.
640
-     *
641
-     * @return string
642
-     */
643
-    public function getQualifiedKeyName() : string
644
-    {
645
-        return $this->getTable().'.'.$this->getKeyName();
646
-    }
647
-
648
-    /**
649
-     * Get the number of models to return per page.
650
-     *
651
-     * @return int
652
-     */
653
-    public function getPerPage() : int
654
-    {
655
-        return $this->perPage;
656
-    }
657
-
658
-    /**
659
-     * Set the number of models to return per page.
660
-     *
661
-     * @param int $perPage
662
-     *
663
-     * @return void
664
-     */
665
-    public function setPerPage($perPage)
666
-    {
667
-        $this->perPage = $perPage;
668
-    }
669
-
670
-    /**
671
-     * Determine if the entity uses get.
672
-     *
673
-     * @return bool
674
-     */
675
-    public function usesTimestamps() : bool
676
-    {
677
-        return $this->timestamps;
678
-    }
679
-
680
-    /**
681
-     * Determine if the entity uses soft deletes.
682
-     *
683
-     * @return bool
684
-     */
685
-    public function usesSoftDeletes() : bool
686
-    {
687
-        return $this->softDeletes;
688
-    }
689
-
690
-    /**
691
-     * Get the 'created_at' column name.
692
-     *
693
-     * @return string
694
-     */
695
-    public function getCreatedAtColumn() : string
696
-    {
697
-        return $this->createdAtColumn;
698
-    }
699
-
700
-    /**
701
-     * Get the 'updated_at' column name.
702
-     *
703
-     * @return string
704
-     */
705
-    public function getUpdatedAtColumn() : string
706
-    {
707
-        return $this->updatedAtColumn;
708
-    }
709
-
710
-    /**
711
-     * Get the deleted_at column.
712
-     *
713
-     * @return string
714
-     */
715
-    public function getQualifiedDeletedAtColumn() : string
716
-    {
717
-        return $this->deletedAtColumn;
718
-    }
719
-
720
-    /**
721
-     * Get the default foreign key name for the model.
722
-     *
723
-     * @return string
724
-     */
725
-    public function getForeignKey() : string
726
-    {
727
-        return snake_case(class_basename($this->getClass())).'_id';
728
-    }
729
-
730
-    /**
731
-     * Return the inheritance type used by the entity.
732
-     *
733
-     * @return string|null
734
-     */
735
-    public function getInheritanceType()
736
-    {
737
-        return $this->inheritanceType;
738
-    }
739
-
740
-    /**
741
-     * Return the discriminator column name on the entity that's
742
-     * used for table inheritance.
743
-     *
744
-     * @return string
745
-     */
746
-    public function getDiscriminatorColumn() : string
747
-    {
748
-        return $this->discriminatorColumn;
749
-    }
750
-
751
-    /**
752
-     * Return the mapping of discriminator column values to
753
-     * entity class names that are used for table inheritance.
754
-     *
755
-     * @return array
756
-     */
757
-    public function getDiscriminatorColumnMap() : array
758
-    {
759
-        return $this->discriminatorColumnMap;
760
-    }
761
-
762
-    /**
763
-     * Return true if the entity should be instanciated using
764
-     * the IoC Container.
765
-     *
766
-     * @return bool
767
-     */
768
-    public function useDependencyInjection() : bool
769
-    {
770
-        return $this->dependencyInjection;
771
-    }
772
-
773
-    /**
774
-     * Add a single relation method name once.
775
-     *
776
-     * @param string $relation
777
-     */
778
-    protected function addSingleRelation($relation)
779
-    {
780
-        if (!in_array($relation, $this->singleRelations)) {
781
-            $this->singleRelations[] = $relation;
782
-        }
783
-    }
784
-
785
-    /**
786
-     * Add a foreign relation method name once.
787
-     *
788
-     * @param string $relation
789
-     */
790
-    protected function addForeignRelation($relation)
791
-    {
792
-        if (!in_array($relation, $this->foreignRelations)) {
793
-            $this->foreignRelations[] = $relation;
794
-        }
795
-    }
796
-
797
-    /**
798
-     * Add a non proxy relation method name once.
799
-     *
800
-     * @param string $relation
801
-     */
802
-    protected function addNonProxyRelation($relation)
803
-    {
804
-        if (!in_array($relation, $this->nonProxyRelationships)) {
805
-            $this->nonProxyRelationships[] = $relation;
806
-        }
807
-    }
808
-
809
-    /**
810
-     * Add a local relation method name once.
811
-     *
812
-     * @param string $relation
813
-     */
814
-    protected function addLocalRelation($relation)
815
-    {
816
-        if (!in_array($relation, $this->localRelations)) {
817
-            $this->localRelations[] = $relation;
818
-        }
819
-    }
820
-
821
-    /**
822
-     * Add a many relation method name once.
823
-     *
824
-     * @param string $relation
825
-     */
826
-    protected function addManyRelation($relation)
827
-    {
828
-        if (!in_array($relation, $this->manyRelations)) {
829
-            $this->manyRelations[] = $relation;
830
-        }
831
-    }
832
-
833
-    /**
834
-     * Add a pivot relation method name once.
835
-     *
836
-     * @param string $relation
837
-     */
838
-    protected function addPivotRelation($relation)
839
-    {
840
-        if (!in_array($relation, $this->pivotRelations)) {
841
-            $this->pivotRelations[] = $relation;
842
-        }
843
-    }
844
-
845
-    /**
846
-     * Define a one-to-one relationship.
847
-     *
848
-     * @param mixed  $entity
849
-     * @param string $related    entity class
850
-     * @param string $foreignKey
851
-     * @param string $localKey
852
-     *
853
-     * @throws MappingException
854
-     *
855
-     * @return \Analogue\ORM\Relationships\HasOne
856
-     */
857
-    public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
858
-    {
859
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
860
-
861
-        $relatedMapper = Manager::getInstance()->mapper($related);
862
-
863
-        $relatedMap = $relatedMapper->getEntityMap();
864
-
865
-        $localKey = $localKey ?: $this->getKeyName();
866
-
867
-        // Add the relation to the definition in map
868
-        list(, $caller) = debug_backtrace(false);
869
-        $relation = $caller['function'];
870
-        $this->relatedClasses[$relation] = $related;
871
-
872
-        $this->addSingleRelation($relation);
873
-        $this->addForeignRelation($relation);
874
-        $this->addNonProxyRelation($relation);
875
-
876
-        // This relationship will always be eager loaded, as proxying it would
877
-        // mean having an object that doesn't actually exists.
878
-        if (!in_array($relation, $this->with)) {
879
-            $this->with[] = $relation;
880
-        }
881
-
882
-        return new HasOne($relatedMapper, $entity, $relatedMap->getTable().'.'.$foreignKey, $localKey);
883
-    }
884
-
885
-    /**
886
-     * Define a polymorphic one-to-one relationship.
887
-     *
888
-     * @param mixed       $entity
889
-     * @param string      $related
890
-     * @param string      $name
891
-     * @param string|null $type
892
-     * @param string|null $id
893
-     * @param string|null $localKey
894
-     *
895
-     * @throws MappingException
896
-     *
897
-     * @return \Analogue\ORM\Relationships\MorphOne
898
-     */
899
-    public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
900
-    {
901
-        list($type, $id) = $this->getMorphs($name, $type, $id);
902
-
903
-        $localKey = $localKey ?: $this->getKeyName();
904
-
905
-        $relatedMapper = Manager::getInstance()->mapper($related);
906
-
907
-        $table = $relatedMapper->getEntityMap()->getTable();
908
-
909
-        // Add the relation to the definition in map
910
-        list(, $caller) = debug_backtrace(false);
911
-        $relation = $caller['function'];
912
-        $this->relatedClasses[$relation] = $related;
913
-
914
-        $this->addSingleRelation($relation);
915
-        $this->addForeignRelation($relation);
916
-        $this->addNonProxyRelation($relation);
917
-
918
-        // This relationship will always be eager loaded, as proxying it would
919
-        // mean having an object that doesn't actually exists.
920
-        if (!in_array($relation, $this->with)) {
921
-            $this->with[] = $relation;
922
-        }
923
-
924
-        return new MorphOne($relatedMapper, $entity, $table.'.'.$type, $table.'.'.$id, $localKey);
925
-    }
926
-
927
-    /**
928
-     * Define an inverse one-to-one or many relationship.
929
-     *
930
-     * @param mixed       $entity
931
-     * @param string      $related
932
-     * @param string|null $foreignKey
933
-     * @param string|null $otherKey
934
-     * @param string|null $relation
935
-     *
936
-     * @throws MappingException
937
-     *
938
-     * @return \Analogue\ORM\Relationships\BelongsTo
939
-     */
940
-    public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
941
-    {
942
-        // Add the relation to the definition in map
943
-        list(, $caller) = debug_backtrace(false);
944
-        $relation = $caller['function'];
945
-        $this->relatedClasses[$relation] = $related;
946
-
947
-        $this->addSingleRelation($relation);
948
-        $this->addLocalRelation($relation);
949
-
950
-        // If no foreign key was supplied, we can use a backtrace to guess the proper
951
-        // foreign key name by using the name of the relationship function, which
952
-        // when combined with an "_id" should conventionally match the columns.
953
-        if (is_null($foreignKey)) {
954
-            $foreignKey = snake_case($relation).'_id';
955
-        }
956
-
957
-        $this->localForeignKeys[$relation] = $foreignKey;
958
-
959
-        $relatedMapper = Manager::getInstance()->mapper($related);
960
-
961
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
962
-
963
-        return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
964
-    }
965
-
966
-    /**
967
-     * Define a polymorphic, inverse one-to-one or many relationship.
968
-     *
969
-     * @param mixed       $entity
970
-     * @param string|null $name
971
-     * @param string|null $type
972
-     * @param string|null $id
973
-     *
974
-     * @throws MappingException
975
-     *
976
-     * @return \Analogue\ORM\Relationships\MorphTo
977
-     */
978
-    public function morphTo($entity, $name = null, $type = null, $id = null)
979
-    {
980
-        // If no name is provided, we will use the backtrace to get the function name
981
-        // since that is most likely the name of the polymorphic interface. We can
982
-        // use that to get both the class and foreign key that will be utilized.
983
-        if (is_null($name)) {
984
-            list(, $caller) = debug_backtrace(false);
985
-
986
-            $name = snake_case($caller['function']);
987
-        }
988
-        $this->addSingleRelations($name);
989
-        $this->addLocalRelation($name);
990
-        $this->addForeignRelations($name);
991
-        $this->relatedClass[$relation] = null;
992
-
993
-        list($type, $id) = $this->getMorphs($name, $type, $id);
994
-
995
-        // Store the foreign key in the entity map.
996
-        // We might want to store the (key, type) as we might need it
997
-        // to build a MorphTo proxy
998
-        $this->localForeignKeys[$name] = [
999
-            'id'   => $id,
1000
-            'type' => $type,
1001
-        ];
1002
-
1003
-        $mapper = Manager::getInstance()->mapper(get_class($entity));
1004
-
1005
-        // If the type value is null it is probably safe to assume we're eager loading
1006
-        // the relationship. When that is the case we will pass in a dummy query as
1007
-        // there are multiple types in the morph and we can't use single queries.
1008
-        $factory = new Factory();
1009
-        $wrapper = $factory->make($entity);
1010
-
1011
-        if (is_null($class = $wrapper->getEntityAttribute($type))) {
1012
-            return new MorphTo(
1013
-                $mapper, $entity, $id, null, $type, $name
1014
-            );
1015
-        }
1016
-
1017
-        // If we are not eager loading the relationship we will essentially treat this
1018
-        // as a belongs-to style relationship since morph-to extends that class and
1019
-        // we will pass in the appropriate values so that it behaves as expected.
1020
-        else {
1021
-            $class = Manager::getInstance()->getInverseMorphMap($class);
1022
-            $relatedMapper = Manager::getInstance()->mapper($class);
1023
-
1024
-            $foreignKey = $relatedMapper->getEntityMap()->getKeyName();
1025
-
1026
-            return new MorphTo(
1027
-                $relatedMapper, $entity, $id, $foreignKey, $type, $name
1028
-            );
1029
-        }
1030
-    }
1031
-
1032
-    /**
1033
-     * Define a one-to-many relationship.
1034
-     *
1035
-     * @param mixed       $entity
1036
-     * @param string      $related
1037
-     * @param string|null $foreignKey
1038
-     * @param string|null $localKey
1039
-     *
1040
-     * @throws MappingException
1041
-     *
1042
-     * @return \Analogue\ORM\Relationships\HasMany
1043
-     */
1044
-    public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
1045
-    {
1046
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1047
-
1048
-        $relatedMapper = Manager::getInstance()->mapper($related);
1049
-
1050
-        $table = $relatedMapper->getEntityMap()->getTable().'.'.$foreignKey;
1051
-
1052
-        $localKey = $localKey ?: $this->getKeyName();
1053
-
1054
-        // Add the relation to the definition in map
1055
-        list(, $caller) = debug_backtrace(false);
1056
-        $relation = $caller['function'];
1057
-        $this->relatedClasses[$relation] = $related;
1058
-
1059
-        $this->addManyRelation($relation);
1060
-        $this->addForeignRelation($relation);
1061
-
1062
-        return new HasMany($relatedMapper, $entity, $table, $localKey);
1063
-    }
1064
-
1065
-    /**
1066
-     * Define a has-many-through relationship.
1067
-     *
1068
-     * @param mixed       $entity
1069
-     * @param string      $related
1070
-     * @param string      $through
1071
-     * @param string|null $firstKey
1072
-     * @param string|null $secondKey
1073
-     *
1074
-     * @throws MappingException
1075
-     *
1076
-     * @return \Analogue\ORM\Relationships\HasManyThrough
1077
-     */
1078
-    public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
1079
-    {
1080
-        $relatedMapper = Manager::getInstance()->mapper($related);
1081
-
1082
-        $throughMapper = Manager::getInstance()->mapper($through);
1083
-
1084
-        $firstKey = $firstKey ?: $this->getForeignKey();
1085
-
1086
-        $throughMap = $throughMapper->getEntityMap();
1087
-
1088
-        $secondKey = $secondKey ?: $throughMap->getForeignKey();
1089
-
1090
-        // Add the relation to the definition in map
1091
-        list(, $caller) = debug_backtrace(false);
1092
-        $relation = $caller['function'];
1093
-        $this->relatedClasses[$relation] = $related;
1094
-
1095
-        $this->addManyRelation($relation);
1096
-        $this->addForeignRelation($relation);
1097
-
1098
-        return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
1099
-    }
1100
-
1101
-    /**
1102
-     * Define a polymorphic one-to-many relationship.
1103
-     *
1104
-     * @param mixed       $entity
1105
-     * @param string      $related
1106
-     * @param string      $name
1107
-     * @param string|null $type
1108
-     * @param string|null $id
1109
-     * @param string|null $localKey
1110
-     *
1111
-     * @return \Analogue\ORM\Relationships\MorphMany
1112
-     */
1113
-    public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
1114
-    {
1115
-        // Here we will gather up the morph type and ID for the relationship so that we
1116
-        // can properly query the intermediate table of a relation. Finally, we will
1117
-        // get the table and create the relationship instances for the developers.
1118
-        list($type, $id) = $this->getMorphs($name, $type, $id);
1119
-
1120
-        $relatedMapper = Manager::getInstance()->mapper($related);
1121
-
1122
-        $table = $relatedMapper->getEntityMap()->getTable();
1123
-
1124
-        $localKey = $localKey ?: $this->getKeyName();
1125
-
1126
-        // Add the relation to the definition in map
1127
-        list(, $caller) = debug_backtrace(false);
1128
-        $relation = $caller['function'];
1129
-        $this->relatedClasses[$relation] = $related;
1130
-
1131
-        $this->addManyRelation($relation);
1132
-        $this->addForeignRelation($relation);
1133
-
1134
-        return new MorphMany($relatedMapper, $entity, $table.'.'.$type, $table.'.'.$id, $localKey);
1135
-    }
1136
-
1137
-    /**
1138
-     * Define a many-to-many relationship.
1139
-     *
1140
-     * @param mixed       $entity
1141
-     * @param string      $relatedClass
1142
-     * @param string|null $table
1143
-     * @param string|null $foreignKey
1144
-     * @param string|null $otherKey
1145
-     * @param string|null $relation
1146
-     *
1147
-     * @throws MappingException
1148
-     *
1149
-     * @return \Analogue\ORM\Relationships\BelongsToMany
1150
-     */
1151
-    public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1152
-    {
1153
-        // Add the relation to the definition in map
1154
-        list(, $caller) = debug_backtrace(false);
1155
-        $relation = $caller['function'];
1156
-        $this->relatedClasses[$relation] = $related;
1157
-
1158
-        $this->addManyRelation($relation);
1159
-        $this->addForeignRelation($relation);
1160
-        $this->addPivotRelation($relation);
1161
-
1162
-        // First, we'll need to determine the foreign key and "other key" for the
1163
-        // relationship. Once we have determined the keys we'll make the query
1164
-        // instances as well as the relationship instances we need for this.
1165
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1166
-
1167
-        $relatedMapper = Manager::getInstance()->mapper($related);
1168
-
1169
-        $relatedMap = $relatedMapper->getEntityMap();
1170
-
1171
-        $otherKey = $otherKey ?: $relatedMap->getForeignKey();
1172
-
1173
-        // If no table name was provided, we can guess it by concatenating the two
1174
-        // models using underscores in alphabetical order. The two model names
1175
-        // are transformed to snake case from their default CamelCase also.
1176
-        if (is_null($table)) {
1177
-            $table = $this->joiningTable($relatedMap);
1178
-        }
1179
-
1180
-        return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1181
-    }
1182
-
1183
-    /**
1184
-     * Define a polymorphic many-to-many relationship.
1185
-     *
1186
-     * @param mixed       $entity
1187
-     * @param string      $related
1188
-     * @param string      $name
1189
-     * @param string|null $table
1190
-     * @param string|null $foreignKey
1191
-     * @param string|null $otherKey
1192
-     * @param bool        $inverse
1193
-     *
1194
-     * @throws MappingException
1195
-     *
1196
-     * @return \Analogue\ORM\Relationships\MorphToMany
1197
-     */
1198
-    public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1199
-    {
1200
-        // Add the relation to the definition in map
1201
-        list(, $caller) = debug_backtrace(false);
1202
-        $relation = $caller['function'];
1203
-        $this->relatedClasses[$relation] = $related;
1204
-
1205
-        $this->addManyRelation($relation);
1206
-        $this->addForeignRelation($relation);
1207
-        $this->addPivotRelation($relation);
1208
-
1209
-        // First, we will need to determine the foreign key and "other key" for the
1210
-        // relationship. Once we have determined the keys we will make the query
1211
-        // instances, as well as the relationship instances we need for these.
1212
-        $foreignKey = $foreignKey ?: $name.'_id';
1213
-
1214
-        $relatedMapper = Manager::getInstance()->mapper($related);
1215
-
1216
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1217
-
1218
-        $table = $table ?: str_plural($name);
1219
-
1220
-        return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1221
-    }
1222
-
1223
-    /**
1224
-     * Define a polymorphic, inverse many-to-many relationship.
1225
-     *
1226
-     * @param mixed       $entity
1227
-     * @param string      $related
1228
-     * @param string      $name
1229
-     * @param string|null $table
1230
-     * @param string|null $foreignKey
1231
-     * @param string|null $otherKey
1232
-     *
1233
-     * @throws MappingException
1234
-     *
1235
-     * @return \Analogue\ORM\Relationships\MorphToMany
1236
-     */
1237
-    public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1238
-    {
1239
-        // Add the relation to the definition in map
1240
-        list(, $caller) = debug_backtrace(false);
1241
-        $relation = $caller['function'];
1242
-        $this->relatedClasses[$relation] = $related;
1243
-
1244
-        $this->addManyRelation($relation);
1245
-        $this->addForeignRelation($relation);
1246
-
1247
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1248
-
1249
-        // For the inverse of the polymorphic many-to-many relations, we will change
1250
-        // the way we determine the foreign and other keys, as it is the opposite
1251
-        // of the morph-to-many method since we're figuring out these inverses.
1252
-        $otherKey = $otherKey ?: $name.'_id';
1253
-
1254
-        return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1255
-    }
1256
-
1257
-    /**
1258
-     * Get the joining table name for a many-to-many relation.
1259
-     *
1260
-     * @param EntityMap $relatedMap
1261
-     *
1262
-     * @return string
1263
-     */
1264
-    public function joiningTable($relatedMap)
1265
-    {
1266
-        // The joining table name, by convention, is simply the snake cased models
1267
-        // sorted alphabetically and concatenated with an underscore, so we can
1268
-        // just sort the models and join them together to get the table name.
1269
-        $base = $this->getTable();
1270
-
1271
-        $related = $relatedMap->getTable();
1272
-
1273
-        $tables = [$related, $base];
1274
-
1275
-        // Now that we have the model names in an array we can just sort them and
1276
-        // use the implode function to join them together with an underscores,
1277
-        // which is typically used by convention within the database system.
1278
-        sort($tables);
1279
-
1280
-        return strtolower(implode('_', $tables));
1281
-    }
1282
-
1283
-    /**
1284
-     * Get the polymorphic relationship columns.
1285
-     *
1286
-     * @param string $name
1287
-     * @param string $type
1288
-     * @param string $id
1289
-     *
1290
-     * @return string[]
1291
-     */
1292
-    protected function getMorphs($name, $type, $id)
1293
-    {
1294
-        $type = $type ?: $name.'_type';
1295
-
1296
-        $id = $id ?: $name.'_id';
1297
-
1298
-        return [$type, $id];
1299
-    }
1300
-
1301
-    /**
1302
-     * Get the class name for polymorphic relations.
1303
-     *
1304
-     * @return string
1305
-     */
1306
-    public function getMorphClass()
1307
-    {
1308
-        $morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1309
-
1310
-        return $this->morphClass ?: $morphClass;
1311
-    }
1312
-
1313
-    /**
1314
-     * Create a new Entity Collection instance.
1315
-     *
1316
-     * @param array $entities
1317
-     *
1318
-     * @return \Analogue\ORM\EntityCollection
1319
-     */
1320
-    public function newCollection(array $entities = [])
1321
-    {
1322
-        $collection = new EntityCollection($entities, $this);
1323
-
1324
-        return $collection->keyBy($this->getKeyName());
1325
-    }
1326
-
1327
-    /**
1328
-     * Process EntityMap parsing at initialization time.
1329
-     *
1330
-     * @return void
1331
-     */
1332
-    public function initialize()
1333
-    {
1334
-        $userMethods = $this->getCustomMethods();
1335
-
1336
-        // Parse EntityMap for method based relationship
1337
-        if (count($userMethods) > 0) {
1338
-            $this->relationships = $this->parseMethodsForRelationship($userMethods);
1339
-        }
1340
-
1341
-        // Parse EntityMap for dynamic relationships
1342
-        if (count($this->dynamicRelationships) > 0) {
1343
-            $this->relationships = $this->relationships + $this->getDynamicRelationships();
1344
-        }
1345
-    }
1346
-
1347
-    /**
1348
-     * Parse every relationships on the EntityMap and sort
1349
-     * them by type.
1350
-     *
1351
-     * @return void
1352
-     */
1353
-    public function boot()
1354
-    {
1355
-        if (count($this->relationships > 0)) {
1356
-            $this->sortRelationshipsByType();
1357
-        }
1358
-    }
1359
-
1360
-    /**
1361
-     * Get Methods that has been added in the child class.
1362
-     *
1363
-     * @return array
1364
-     */
1365
-    protected function getCustomMethods()
1366
-    {
1367
-        $mapMethods = get_class_methods($this);
1368
-
1369
-        $parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1370
-
1371
-        return array_diff($mapMethods, $parentsMethods);
1372
-    }
1373
-
1374
-    /**
1375
-     * Parse user's class methods for relationships.
1376
-     *
1377
-     * @param array $customMethods
1378
-     *
1379
-     * @return array
1380
-     */
1381
-    protected function parseMethodsForRelationship(array $customMethods)
1382
-    {
1383
-        $relationships = [];
1384
-
1385
-        $class = new ReflectionClass(get_class($this));
1386
-
1387
-        // Get the mapped Entity class, as we will detect relationships
1388
-        // methods by testing that the first argument is type-hinted to
1389
-        // the same class as the mapped Entity.
1390
-        $entityClass = $this->getClass();
1391
-
1392
-        foreach ($customMethods as $methodName) {
1393
-            $method = $class->getMethod($methodName);
1394
-
1395
-            if ($method->getNumberOfParameters() > 0) {
1396
-                $params = $method->getParameters();
1397
-
1398
-                if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1399
-                    $relationships[] = $methodName;
1400
-                }
1401
-            }
1402
-        }
1403
-
1404
-        return $relationships;
1405
-    }
1406
-
1407
-    /**
1408
-     * Sort Relationships methods by type.
1409
-     *
1410
-     * TODO : replace this by direclty setting these value
1411
-     * in the corresponding methods, so we won't need
1412
-     * the correpondancy tabble
1413
-     *
1414
-     * @return void
1415
-     */
1416
-    protected function sortRelationshipsByType()
1417
-    {
1418
-        $entityClass = $this->getClass();
1419
-
1420
-        // Instantiate a dummy entity which we will pass to relationship methods.
1421
-        $entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1422
-
1423
-        foreach ($this->relationships as $relation) {
1424
-            $this->$relation($entity);
1425
-        }
1426
-    }
1427
-
1428
-    /**
1429
-     * Override this method for custom entity instantiation.
1430
-     *
1431
-     * @return null
1432
-     */
1433
-    public function activator()
1434
-    {
1435
-    }
1436
-
1437
-    /**
1438
-     * Magic call to dynamic relationships.
1439
-     *
1440
-     * @param string $method
1441
-     * @param array  $parameters
1442
-     *
1443
-     * @throws Exception
1444
-     *
1445
-     * @return mixed
1446
-     */
1447
-    public function __call($method, $parameters)
1448
-    {
1449
-        if (!array_key_exists($method, $this->dynamicRelationships)) {
1450
-            throw new Exception(get_class($this)." has no method $method");
1451
-        }
1452
-
1453
-        // Add $this to parameters so the closure can call relationship method on the map.
1454
-        $parameters[] = $this;
1455
-
1456
-        return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1457
-    }
26
+	/**
27
+	 * The mapping driver to use with this entity.
28
+	 *
29
+	 * @var string
30
+	 */
31
+	protected $driver = 'illuminate';
32
+
33
+	/**
34
+	 * The Database Connection name for the model.
35
+	 *
36
+	 * @var string
37
+	 */
38
+	protected $connection;
39
+
40
+	/**
41
+	 * The table associated with the entity.
42
+	 *
43
+	 * @var string|null
44
+	 */
45
+	protected $table = null;
46
+
47
+	/**
48
+	 * The primary key for the model.
49
+	 *
50
+	 * @var string
51
+	 */
52
+	protected $primaryKey = 'id';
53
+
54
+	/**
55
+	 * Name of the entity's array property that should
56
+	 * contain the attributes.
57
+	 * If set to null, analogue will only hydrate object's properties.
58
+	 *
59
+	 * @var string|null
60
+	 */
61
+	protected $arrayName = 'attributes';
62
+
63
+	/**
64
+	 * Array containing the list of database columns to be mapped
65
+	 * in the attributes array of the entity.
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $attributes = [];
70
+
71
+	/**
72
+	 * Array containing the list of database columns to be mapped
73
+	 * to the entity's class properties.
74
+	 *
75
+	 * @var array
76
+	 */
77
+	protected $properties = [];
78
+
79
+	/**
80
+	 * The Custom Domain Class to use with this mapping.
81
+	 *
82
+	 * @var string|null
83
+	 */
84
+	protected $class = null;
85
+
86
+	/**
87
+	 * Embedded Value Objects.
88
+	 *
89
+	 * @var array
90
+	 */
91
+	protected $embeddables = [];
92
+
93
+	/**
94
+	 * Determine the relationships method used on the entity.
95
+	 * If not set, mapper will autodetect them.
96
+	 *
97
+	 * @var array
98
+	 */
99
+	private $relationships = [];
100
+
101
+	/**
102
+	 * Relationships that should be treated as collection.
103
+	 *
104
+	 * @var array
105
+	 */
106
+	private $manyRelations = [];
107
+
108
+	/**
109
+	 * Relationships that should be treated as single entity.
110
+	 *
111
+	 * @var array
112
+	 */
113
+	private $singleRelations = [];
114
+
115
+	/**
116
+	 * Relationships for which the key is stored in the Entity itself.
117
+	 *
118
+	 * @var array
119
+	 */
120
+	private $localRelations = [];
121
+
122
+	/**
123
+	 * List of local keys associated to local relation methods.
124
+	 *
125
+	 * @var array
126
+	 */
127
+	private $localForeignKeys = [];
128
+
129
+	/**
130
+	 * Relationships for which the key is stored in the Related Entity.
131
+	 *
132
+	 * @var array
133
+	 */
134
+	private $foreignRelations = [];
135
+
136
+	/**
137
+	 * Relationships which use a pivot record.
138
+	 *
139
+	 * @var array
140
+	 */
141
+	private $pivotRelations = [];
142
+
143
+	/**
144
+	 * Dynamic relationships.
145
+	 *
146
+	 * @var array
147
+	 */
148
+	private $dynamicRelationships = [];
149
+
150
+	/**
151
+	 * Targetted class for the relationship method. value is set to `null` for
152
+	 * polymorphic relations.
153
+	 *
154
+	 * @var array
155
+	 */
156
+	private $relatedClasses = [];
157
+
158
+	/**
159
+	 * Some relation methods like embedded objects, or HasOne and MorphOne,
160
+	 * will never have a proxy loaded on them.
161
+	 *
162
+	 * @var array
163
+	 */
164
+	private $nonProxyRelationships = [];
165
+
166
+	/**
167
+	 * The number of models to return for pagination.
168
+	 *
169
+	 * @var int
170
+	 */
171
+	protected $perPage = 15;
172
+
173
+	/**
174
+	 * The relations to eager load on every query.
175
+	 *
176
+	 * @var array
177
+	 */
178
+	protected $with = [];
179
+
180
+	/**
181
+	 * The class name to be used in polymorphic relations.
182
+	 *
183
+	 * @var string
184
+	 */
185
+	protected $morphClass;
186
+
187
+	/**
188
+	 * Sequence name, to be used with postgreSql
189
+	 * defaults to %table_name%_id_seq.
190
+	 *
191
+	 * @var string|null
192
+	 */
193
+	protected $sequence = null;
194
+
195
+	/**
196
+	 * Indicates if the entity should be timestamped.
197
+	 *
198
+	 * @var bool
199
+	 */
200
+	public $timestamps = false;
201
+
202
+	/**
203
+	 * The name of the "created at" column.
204
+	 *
205
+	 * @var string
206
+	 */
207
+	protected $createdAtColumn = 'created_at';
208
+
209
+	/**
210
+	 * The name of the "updated at" column.
211
+	 *
212
+	 * @var string
213
+	 */
214
+	protected $updatedAtColumn = 'updated_at';
215
+
216
+	/**
217
+	 * Indicates if the entity uses softdeletes.
218
+	 *
219
+	 * @var bool
220
+	 */
221
+	public $softDeletes = false;
222
+
223
+	/**
224
+	 * The name of the "deleted at" column.
225
+	 *
226
+	 * @var string
227
+	 */
228
+	protected $deletedAtColumn = 'deleted_at';
229
+
230
+	/**
231
+	 * The date format to use with the current database connection.
232
+	 *
233
+	 * @var string
234
+	 */
235
+	protected $dateFormat;
236
+
237
+	/**
238
+	 * Set this property to true if the entity should be instantiated
239
+	 * using the IoC Container.
240
+	 *
241
+	 * @var bool
242
+	 */
243
+	protected $dependencyInjection = false;
244
+
245
+	/**
246
+	 * Set the usage of inheritance, possible values are :
247
+	 * "single_table"
248
+	 * null.
249
+	 *
250
+	 * @var string | null
251
+	 */
252
+	protected $inheritanceType = null;
253
+
254
+	/**
255
+	 * Discriminator column name.
256
+	 *
257
+	 * @var string
258
+	 */
259
+	protected $discriminatorColumn = 'type';
260
+
261
+	/**
262
+	 * Allow using a string to define which entity type should be instantiated.
263
+	 * If not set, analogue will uses entity's FQDN.
264
+	 *
265
+	 * @var array
266
+	 */
267
+	protected $discriminatorColumnMap = [];
268
+
269
+	/**
270
+	 * Return Domain class attributes, useful when mapping to a Plain PHP Object.
271
+	 *
272
+	 * @return array
273
+	 */
274
+	public function getAttributes() : array
275
+	{
276
+		return $this->attributes;
277
+	}
278
+
279
+	/**
280
+	 * Set the domain class attributes.
281
+	 *
282
+	 * @param array $attributeNames
283
+	 */
284
+	public function setAttributes(array $attributeNames)
285
+	{
286
+		$this->attributes = $attributeNames;
287
+	}
288
+
289
+	/**
290
+	 * Return true if the Entity has an 'attributes' array property.
291
+	 *
292
+	 * @return bool
293
+	 */
294
+	public function usesAttributesArray() : bool
295
+	{
296
+		return $this->arrayName === null ? false : true;
297
+	}
298
+
299
+	/**
300
+	 * Return the name of the Entity's attributes property.
301
+	 *
302
+	 * @return string|null
303
+	 */
304
+	public function getAttributesArrayName()
305
+	{
306
+		return $this->arrayName;
307
+	}
308
+
309
+	/**
310
+	 * Get all the attribute names for the class, including relationships, embeddables and primary key.
311
+	 *
312
+	 * @return array
313
+	 */
314
+	public function getCompiledAttributes() : array
315
+	{
316
+		$key = $this->getKeyName();
317
+
318
+		$embeddables = array_keys($this->getEmbeddables());
319
+
320
+		$relationships = $this->getRelationships();
321
+
322
+		$attributes = $this->getAttributes();
323
+
324
+		return array_merge([$key], $embeddables, $relationships, $attributes);
325
+	}
326
+
327
+	/**
328
+	 * Set the date format to use with the current database connection.
329
+	 *
330
+	 * @param string $format
331
+	 */
332
+	public function setDateFormat($format)
333
+	{
334
+		$this->dateFormat = $format;
335
+	}
336
+
337
+	/**
338
+	 * Get the date format to use with the current database connection.
339
+	 *
340
+	 *  @return string
341
+	 */
342
+	public function getDateFormat() : string
343
+	{
344
+		return $this->dateFormat;
345
+	}
346
+
347
+	/**
348
+	 * Set the Driver for this mapping.
349
+	 *
350
+	 * @param string $driver
351
+	 */
352
+	public function setDriver($driver)
353
+	{
354
+		$this->driver = $driver;
355
+	}
356
+
357
+	/**
358
+	 * Get the Driver for this mapping.
359
+	 *
360
+	 * @return string
361
+	 */
362
+	public function getDriver() : string
363
+	{
364
+		return $this->driver;
365
+	}
366
+
367
+	/**
368
+	 * Set the db connection to use on the table.
369
+	 *
370
+	 * @param $connection
371
+	 */
372
+	public function setConnection($connection)
373
+	{
374
+		$this->connection = $connection;
375
+	}
376
+
377
+	/**
378
+	 * Get the Database connection the Entity is stored on.
379
+	 *
380
+	 * @return string | null
381
+	 */
382
+	public function getConnection()
383
+	{
384
+		return $this->connection;
385
+	}
386
+
387
+	/**
388
+	 * Get the table associated with the entity.
389
+	 *
390
+	 * @return string
391
+	 */
392
+	public function getTable() : string
393
+	{
394
+		if (!is_null($this->table)) {
395
+			return $this->table;
396
+		}
397
+
398
+		return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
399
+	}
400
+
401
+	/**
402
+	 * Set the database table name.
403
+	 *
404
+	 * @param string $table
405
+	 */
406
+	public function setTable($table)
407
+	{
408
+		$this->table = $table;
409
+	}
410
+
411
+	/**
412
+	 * Get the pgSql sequence name.
413
+	 *
414
+	 * @return string
415
+	 */
416
+	public function getSequence()
417
+	{
418
+		if (!is_null($this->sequence)) {
419
+			return $this->sequence;
420
+		} else {
421
+			return $this->getTable().'_id_seq';
422
+		}
423
+	}
424
+
425
+	/**
426
+	 * Get the custom entity class.
427
+	 *
428
+	 * @return string namespaced class name
429
+	 */
430
+	public function getClass() : string
431
+	{
432
+		return isset($this->class) ? $this->class : null;
433
+	}
434
+
435
+	/**
436
+	 * Set the custom entity class.
437
+	 *
438
+	 * @param string $class namespaced class name
439
+	 */
440
+	public function setClass($class)
441
+	{
442
+		$this->class = $class;
443
+	}
444
+
445
+	/**
446
+	 * Get the embedded Value Objects.
447
+	 *
448
+	 * @return array
449
+	 */
450
+	public function getEmbeddables() : array
451
+	{
452
+		return $this->embeddables;
453
+	}
454
+
455
+	/**
456
+	 * Return attributes that should be mapped to class properties.
457
+	 *
458
+	 * @return array
459
+	 */
460
+	public function getProperties() : array
461
+	{
462
+		return $this->properties;
463
+	}
464
+
465
+	/**
466
+	 * Return the array property in which will be mapped all attributes
467
+	 * that are not mapped to class properties.
468
+	 *
469
+	 * @return string
470
+	 */
471
+	public function getAttributesPropertyName() : string
472
+	{
473
+	}
474
+
475
+	/**
476
+	 * Set the embedded Value Objects.
477
+	 *
478
+	 * @param array $embeddables
479
+	 */
480
+	public function setEmbeddables(array $embeddables)
481
+	{
482
+		$this->embeddables = $embeddables;
483
+	}
484
+
485
+	/**
486
+	 * Get the relationships to map on a custom domain
487
+	 * class.
488
+	 *
489
+	 * @return array
490
+	 */
491
+	public function getRelationships() : array
492
+	{
493
+		return $this->relationships;
494
+	}
495
+
496
+	/**
497
+	 * Get the relationships that will not have a proxy
498
+	 * set on them.
499
+	 *
500
+	 * @return array
501
+	 */
502
+	public function getRelationshipsWithoutProxy() : array
503
+	{
504
+		return $this->nonProxyRelationships;
505
+	}
506
+
507
+	/**
508
+	 * Relationships of the Entity type.
509
+	 *
510
+	 * @return array
511
+	 */
512
+	public function getSingleRelationships() : array
513
+	{
514
+		return $this->singleRelations;
515
+	}
516
+
517
+	/**
518
+	 * Relationships of type Collection.
519
+	 *
520
+	 * @return array
521
+	 */
522
+	public function getManyRelationships() : array
523
+	{
524
+		return $this->manyRelations;
525
+	}
526
+
527
+	/**
528
+	 * Relationships with foreign key in the mapped entity record.
529
+	 *
530
+	 * @return array
531
+	 */
532
+	public function getLocalRelationships() : array
533
+	{
534
+		return $this->localRelations;
535
+	}
536
+
537
+	/**
538
+	 * Return the local keys associated to the relationship.
539
+	 *
540
+	 * @param string $relation
541
+	 *
542
+	 * @return string | array | null
543
+	 */
544
+	public function getLocalKeys($relation)
545
+	{
546
+		return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
547
+	}
548
+
549
+	/**
550
+	 * Relationships with foreign key in the related Entity record.
551
+	 *
552
+	 * @return array
553
+	 */
554
+	public function getForeignRelationships() : array
555
+	{
556
+		return $this->foreignRelations;
557
+	}
558
+
559
+	/**
560
+	 * Relationships which keys are stored in a pivot record.
561
+	 *
562
+	 * @return array
563
+	 */
564
+	public function getPivotRelationships() : array
565
+	{
566
+		return $this->pivotRelations;
567
+	}
568
+
569
+	/**
570
+	 * Get the targetted type for a relationship. Return null if polymorphic.
571
+	 *
572
+	 * @param string $relation
573
+	 *
574
+	 * @return string | null
575
+	 */
576
+	public function getTargettedClass($relation)
577
+	{
578
+		return $this->relatedClasses[$relation];
579
+	}
580
+
581
+	/**
582
+	 * Add a Dynamic Relationship method at runtime. This has to be done
583
+	 * by hooking the 'initializing' event, before entityMap is initialized.
584
+	 *
585
+	 * @param string   $name         Relation name
586
+	 * @param \Closure $relationship
587
+	 *
588
+	 * @return void
589
+	 */
590
+	public function addRelationshipMethod($name, \Closure $relationship)
591
+	{
592
+		$this->dynamicRelationships[$name] = $relationship;
593
+	}
594
+
595
+	/**
596
+	 * Get the dynamic relationship method names.
597
+	 *
598
+	 * @return array
599
+	 */
600
+	public function getDynamicRelationships() : array
601
+	{
602
+		return array_keys($this->dynamicRelationships);
603
+	}
604
+
605
+	/**
606
+	 * Get the relationships that have to be eager loaded
607
+	 * on each request.
608
+	 *
609
+	 * @return array
610
+	 */
611
+	public function getEagerloadedRelationships() : array
612
+	{
613
+		return $this->with;
614
+	}
615
+
616
+	/**
617
+	 * Get the primary key attribute for the entity.
618
+	 *
619
+	 * @return string
620
+	 */
621
+	public function getKeyName() : string
622
+	{
623
+		return $this->primaryKey;
624
+	}
625
+
626
+	/**
627
+	 * Set the primary key for the entity.
628
+	 *
629
+	 * @param $key
630
+	 *
631
+	 * @return void
632
+	 */
633
+	public function setKeyName($key)
634
+	{
635
+		$this->primaryKey = $key;
636
+	}
637
+
638
+	/**
639
+	 * Get the table qualified key name.
640
+	 *
641
+	 * @return string
642
+	 */
643
+	public function getQualifiedKeyName() : string
644
+	{
645
+		return $this->getTable().'.'.$this->getKeyName();
646
+	}
647
+
648
+	/**
649
+	 * Get the number of models to return per page.
650
+	 *
651
+	 * @return int
652
+	 */
653
+	public function getPerPage() : int
654
+	{
655
+		return $this->perPage;
656
+	}
657
+
658
+	/**
659
+	 * Set the number of models to return per page.
660
+	 *
661
+	 * @param int $perPage
662
+	 *
663
+	 * @return void
664
+	 */
665
+	public function setPerPage($perPage)
666
+	{
667
+		$this->perPage = $perPage;
668
+	}
669
+
670
+	/**
671
+	 * Determine if the entity uses get.
672
+	 *
673
+	 * @return bool
674
+	 */
675
+	public function usesTimestamps() : bool
676
+	{
677
+		return $this->timestamps;
678
+	}
679
+
680
+	/**
681
+	 * Determine if the entity uses soft deletes.
682
+	 *
683
+	 * @return bool
684
+	 */
685
+	public function usesSoftDeletes() : bool
686
+	{
687
+		return $this->softDeletes;
688
+	}
689
+
690
+	/**
691
+	 * Get the 'created_at' column name.
692
+	 *
693
+	 * @return string
694
+	 */
695
+	public function getCreatedAtColumn() : string
696
+	{
697
+		return $this->createdAtColumn;
698
+	}
699
+
700
+	/**
701
+	 * Get the 'updated_at' column name.
702
+	 *
703
+	 * @return string
704
+	 */
705
+	public function getUpdatedAtColumn() : string
706
+	{
707
+		return $this->updatedAtColumn;
708
+	}
709
+
710
+	/**
711
+	 * Get the deleted_at column.
712
+	 *
713
+	 * @return string
714
+	 */
715
+	public function getQualifiedDeletedAtColumn() : string
716
+	{
717
+		return $this->deletedAtColumn;
718
+	}
719
+
720
+	/**
721
+	 * Get the default foreign key name for the model.
722
+	 *
723
+	 * @return string
724
+	 */
725
+	public function getForeignKey() : string
726
+	{
727
+		return snake_case(class_basename($this->getClass())).'_id';
728
+	}
729
+
730
+	/**
731
+	 * Return the inheritance type used by the entity.
732
+	 *
733
+	 * @return string|null
734
+	 */
735
+	public function getInheritanceType()
736
+	{
737
+		return $this->inheritanceType;
738
+	}
739
+
740
+	/**
741
+	 * Return the discriminator column name on the entity that's
742
+	 * used for table inheritance.
743
+	 *
744
+	 * @return string
745
+	 */
746
+	public function getDiscriminatorColumn() : string
747
+	{
748
+		return $this->discriminatorColumn;
749
+	}
750
+
751
+	/**
752
+	 * Return the mapping of discriminator column values to
753
+	 * entity class names that are used for table inheritance.
754
+	 *
755
+	 * @return array
756
+	 */
757
+	public function getDiscriminatorColumnMap() : array
758
+	{
759
+		return $this->discriminatorColumnMap;
760
+	}
761
+
762
+	/**
763
+	 * Return true if the entity should be instanciated using
764
+	 * the IoC Container.
765
+	 *
766
+	 * @return bool
767
+	 */
768
+	public function useDependencyInjection() : bool
769
+	{
770
+		return $this->dependencyInjection;
771
+	}
772
+
773
+	/**
774
+	 * Add a single relation method name once.
775
+	 *
776
+	 * @param string $relation
777
+	 */
778
+	protected function addSingleRelation($relation)
779
+	{
780
+		if (!in_array($relation, $this->singleRelations)) {
781
+			$this->singleRelations[] = $relation;
782
+		}
783
+	}
784
+
785
+	/**
786
+	 * Add a foreign relation method name once.
787
+	 *
788
+	 * @param string $relation
789
+	 */
790
+	protected function addForeignRelation($relation)
791
+	{
792
+		if (!in_array($relation, $this->foreignRelations)) {
793
+			$this->foreignRelations[] = $relation;
794
+		}
795
+	}
796
+
797
+	/**
798
+	 * Add a non proxy relation method name once.
799
+	 *
800
+	 * @param string $relation
801
+	 */
802
+	protected function addNonProxyRelation($relation)
803
+	{
804
+		if (!in_array($relation, $this->nonProxyRelationships)) {
805
+			$this->nonProxyRelationships[] = $relation;
806
+		}
807
+	}
808
+
809
+	/**
810
+	 * Add a local relation method name once.
811
+	 *
812
+	 * @param string $relation
813
+	 */
814
+	protected function addLocalRelation($relation)
815
+	{
816
+		if (!in_array($relation, $this->localRelations)) {
817
+			$this->localRelations[] = $relation;
818
+		}
819
+	}
820
+
821
+	/**
822
+	 * Add a many relation method name once.
823
+	 *
824
+	 * @param string $relation
825
+	 */
826
+	protected function addManyRelation($relation)
827
+	{
828
+		if (!in_array($relation, $this->manyRelations)) {
829
+			$this->manyRelations[] = $relation;
830
+		}
831
+	}
832
+
833
+	/**
834
+	 * Add a pivot relation method name once.
835
+	 *
836
+	 * @param string $relation
837
+	 */
838
+	protected function addPivotRelation($relation)
839
+	{
840
+		if (!in_array($relation, $this->pivotRelations)) {
841
+			$this->pivotRelations[] = $relation;
842
+		}
843
+	}
844
+
845
+	/**
846
+	 * Define a one-to-one relationship.
847
+	 *
848
+	 * @param mixed  $entity
849
+	 * @param string $related    entity class
850
+	 * @param string $foreignKey
851
+	 * @param string $localKey
852
+	 *
853
+	 * @throws MappingException
854
+	 *
855
+	 * @return \Analogue\ORM\Relationships\HasOne
856
+	 */
857
+	public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
858
+	{
859
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
860
+
861
+		$relatedMapper = Manager::getInstance()->mapper($related);
862
+
863
+		$relatedMap = $relatedMapper->getEntityMap();
864
+
865
+		$localKey = $localKey ?: $this->getKeyName();
866
+
867
+		// Add the relation to the definition in map
868
+		list(, $caller) = debug_backtrace(false);
869
+		$relation = $caller['function'];
870
+		$this->relatedClasses[$relation] = $related;
871
+
872
+		$this->addSingleRelation($relation);
873
+		$this->addForeignRelation($relation);
874
+		$this->addNonProxyRelation($relation);
875
+
876
+		// This relationship will always be eager loaded, as proxying it would
877
+		// mean having an object that doesn't actually exists.
878
+		if (!in_array($relation, $this->with)) {
879
+			$this->with[] = $relation;
880
+		}
881
+
882
+		return new HasOne($relatedMapper, $entity, $relatedMap->getTable().'.'.$foreignKey, $localKey);
883
+	}
884
+
885
+	/**
886
+	 * Define a polymorphic one-to-one relationship.
887
+	 *
888
+	 * @param mixed       $entity
889
+	 * @param string      $related
890
+	 * @param string      $name
891
+	 * @param string|null $type
892
+	 * @param string|null $id
893
+	 * @param string|null $localKey
894
+	 *
895
+	 * @throws MappingException
896
+	 *
897
+	 * @return \Analogue\ORM\Relationships\MorphOne
898
+	 */
899
+	public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
900
+	{
901
+		list($type, $id) = $this->getMorphs($name, $type, $id);
902
+
903
+		$localKey = $localKey ?: $this->getKeyName();
904
+
905
+		$relatedMapper = Manager::getInstance()->mapper($related);
906
+
907
+		$table = $relatedMapper->getEntityMap()->getTable();
908
+
909
+		// Add the relation to the definition in map
910
+		list(, $caller) = debug_backtrace(false);
911
+		$relation = $caller['function'];
912
+		$this->relatedClasses[$relation] = $related;
913
+
914
+		$this->addSingleRelation($relation);
915
+		$this->addForeignRelation($relation);
916
+		$this->addNonProxyRelation($relation);
917
+
918
+		// This relationship will always be eager loaded, as proxying it would
919
+		// mean having an object that doesn't actually exists.
920
+		if (!in_array($relation, $this->with)) {
921
+			$this->with[] = $relation;
922
+		}
923
+
924
+		return new MorphOne($relatedMapper, $entity, $table.'.'.$type, $table.'.'.$id, $localKey);
925
+	}
926
+
927
+	/**
928
+	 * Define an inverse one-to-one or many relationship.
929
+	 *
930
+	 * @param mixed       $entity
931
+	 * @param string      $related
932
+	 * @param string|null $foreignKey
933
+	 * @param string|null $otherKey
934
+	 * @param string|null $relation
935
+	 *
936
+	 * @throws MappingException
937
+	 *
938
+	 * @return \Analogue\ORM\Relationships\BelongsTo
939
+	 */
940
+	public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
941
+	{
942
+		// Add the relation to the definition in map
943
+		list(, $caller) = debug_backtrace(false);
944
+		$relation = $caller['function'];
945
+		$this->relatedClasses[$relation] = $related;
946
+
947
+		$this->addSingleRelation($relation);
948
+		$this->addLocalRelation($relation);
949
+
950
+		// If no foreign key was supplied, we can use a backtrace to guess the proper
951
+		// foreign key name by using the name of the relationship function, which
952
+		// when combined with an "_id" should conventionally match the columns.
953
+		if (is_null($foreignKey)) {
954
+			$foreignKey = snake_case($relation).'_id';
955
+		}
956
+
957
+		$this->localForeignKeys[$relation] = $foreignKey;
958
+
959
+		$relatedMapper = Manager::getInstance()->mapper($related);
960
+
961
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
962
+
963
+		return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
964
+	}
965
+
966
+	/**
967
+	 * Define a polymorphic, inverse one-to-one or many relationship.
968
+	 *
969
+	 * @param mixed       $entity
970
+	 * @param string|null $name
971
+	 * @param string|null $type
972
+	 * @param string|null $id
973
+	 *
974
+	 * @throws MappingException
975
+	 *
976
+	 * @return \Analogue\ORM\Relationships\MorphTo
977
+	 */
978
+	public function morphTo($entity, $name = null, $type = null, $id = null)
979
+	{
980
+		// If no name is provided, we will use the backtrace to get the function name
981
+		// since that is most likely the name of the polymorphic interface. We can
982
+		// use that to get both the class and foreign key that will be utilized.
983
+		if (is_null($name)) {
984
+			list(, $caller) = debug_backtrace(false);
985
+
986
+			$name = snake_case($caller['function']);
987
+		}
988
+		$this->addSingleRelations($name);
989
+		$this->addLocalRelation($name);
990
+		$this->addForeignRelations($name);
991
+		$this->relatedClass[$relation] = null;
992
+
993
+		list($type, $id) = $this->getMorphs($name, $type, $id);
994
+
995
+		// Store the foreign key in the entity map.
996
+		// We might want to store the (key, type) as we might need it
997
+		// to build a MorphTo proxy
998
+		$this->localForeignKeys[$name] = [
999
+			'id'   => $id,
1000
+			'type' => $type,
1001
+		];
1002
+
1003
+		$mapper = Manager::getInstance()->mapper(get_class($entity));
1004
+
1005
+		// If the type value is null it is probably safe to assume we're eager loading
1006
+		// the relationship. When that is the case we will pass in a dummy query as
1007
+		// there are multiple types in the morph and we can't use single queries.
1008
+		$factory = new Factory();
1009
+		$wrapper = $factory->make($entity);
1010
+
1011
+		if (is_null($class = $wrapper->getEntityAttribute($type))) {
1012
+			return new MorphTo(
1013
+				$mapper, $entity, $id, null, $type, $name
1014
+			);
1015
+		}
1016
+
1017
+		// If we are not eager loading the relationship we will essentially treat this
1018
+		// as a belongs-to style relationship since morph-to extends that class and
1019
+		// we will pass in the appropriate values so that it behaves as expected.
1020
+		else {
1021
+			$class = Manager::getInstance()->getInverseMorphMap($class);
1022
+			$relatedMapper = Manager::getInstance()->mapper($class);
1023
+
1024
+			$foreignKey = $relatedMapper->getEntityMap()->getKeyName();
1025
+
1026
+			return new MorphTo(
1027
+				$relatedMapper, $entity, $id, $foreignKey, $type, $name
1028
+			);
1029
+		}
1030
+	}
1031
+
1032
+	/**
1033
+	 * Define a one-to-many relationship.
1034
+	 *
1035
+	 * @param mixed       $entity
1036
+	 * @param string      $related
1037
+	 * @param string|null $foreignKey
1038
+	 * @param string|null $localKey
1039
+	 *
1040
+	 * @throws MappingException
1041
+	 *
1042
+	 * @return \Analogue\ORM\Relationships\HasMany
1043
+	 */
1044
+	public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
1045
+	{
1046
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1047
+
1048
+		$relatedMapper = Manager::getInstance()->mapper($related);
1049
+
1050
+		$table = $relatedMapper->getEntityMap()->getTable().'.'.$foreignKey;
1051
+
1052
+		$localKey = $localKey ?: $this->getKeyName();
1053
+
1054
+		// Add the relation to the definition in map
1055
+		list(, $caller) = debug_backtrace(false);
1056
+		$relation = $caller['function'];
1057
+		$this->relatedClasses[$relation] = $related;
1058
+
1059
+		$this->addManyRelation($relation);
1060
+		$this->addForeignRelation($relation);
1061
+
1062
+		return new HasMany($relatedMapper, $entity, $table, $localKey);
1063
+	}
1064
+
1065
+	/**
1066
+	 * Define a has-many-through relationship.
1067
+	 *
1068
+	 * @param mixed       $entity
1069
+	 * @param string      $related
1070
+	 * @param string      $through
1071
+	 * @param string|null $firstKey
1072
+	 * @param string|null $secondKey
1073
+	 *
1074
+	 * @throws MappingException
1075
+	 *
1076
+	 * @return \Analogue\ORM\Relationships\HasManyThrough
1077
+	 */
1078
+	public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
1079
+	{
1080
+		$relatedMapper = Manager::getInstance()->mapper($related);
1081
+
1082
+		$throughMapper = Manager::getInstance()->mapper($through);
1083
+
1084
+		$firstKey = $firstKey ?: $this->getForeignKey();
1085
+
1086
+		$throughMap = $throughMapper->getEntityMap();
1087
+
1088
+		$secondKey = $secondKey ?: $throughMap->getForeignKey();
1089
+
1090
+		// Add the relation to the definition in map
1091
+		list(, $caller) = debug_backtrace(false);
1092
+		$relation = $caller['function'];
1093
+		$this->relatedClasses[$relation] = $related;
1094
+
1095
+		$this->addManyRelation($relation);
1096
+		$this->addForeignRelation($relation);
1097
+
1098
+		return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
1099
+	}
1100
+
1101
+	/**
1102
+	 * Define a polymorphic one-to-many relationship.
1103
+	 *
1104
+	 * @param mixed       $entity
1105
+	 * @param string      $related
1106
+	 * @param string      $name
1107
+	 * @param string|null $type
1108
+	 * @param string|null $id
1109
+	 * @param string|null $localKey
1110
+	 *
1111
+	 * @return \Analogue\ORM\Relationships\MorphMany
1112
+	 */
1113
+	public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
1114
+	{
1115
+		// Here we will gather up the morph type and ID for the relationship so that we
1116
+		// can properly query the intermediate table of a relation. Finally, we will
1117
+		// get the table and create the relationship instances for the developers.
1118
+		list($type, $id) = $this->getMorphs($name, $type, $id);
1119
+
1120
+		$relatedMapper = Manager::getInstance()->mapper($related);
1121
+
1122
+		$table = $relatedMapper->getEntityMap()->getTable();
1123
+
1124
+		$localKey = $localKey ?: $this->getKeyName();
1125
+
1126
+		// Add the relation to the definition in map
1127
+		list(, $caller) = debug_backtrace(false);
1128
+		$relation = $caller['function'];
1129
+		$this->relatedClasses[$relation] = $related;
1130
+
1131
+		$this->addManyRelation($relation);
1132
+		$this->addForeignRelation($relation);
1133
+
1134
+		return new MorphMany($relatedMapper, $entity, $table.'.'.$type, $table.'.'.$id, $localKey);
1135
+	}
1136
+
1137
+	/**
1138
+	 * Define a many-to-many relationship.
1139
+	 *
1140
+	 * @param mixed       $entity
1141
+	 * @param string      $relatedClass
1142
+	 * @param string|null $table
1143
+	 * @param string|null $foreignKey
1144
+	 * @param string|null $otherKey
1145
+	 * @param string|null $relation
1146
+	 *
1147
+	 * @throws MappingException
1148
+	 *
1149
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
1150
+	 */
1151
+	public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1152
+	{
1153
+		// Add the relation to the definition in map
1154
+		list(, $caller) = debug_backtrace(false);
1155
+		$relation = $caller['function'];
1156
+		$this->relatedClasses[$relation] = $related;
1157
+
1158
+		$this->addManyRelation($relation);
1159
+		$this->addForeignRelation($relation);
1160
+		$this->addPivotRelation($relation);
1161
+
1162
+		// First, we'll need to determine the foreign key and "other key" for the
1163
+		// relationship. Once we have determined the keys we'll make the query
1164
+		// instances as well as the relationship instances we need for this.
1165
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1166
+
1167
+		$relatedMapper = Manager::getInstance()->mapper($related);
1168
+
1169
+		$relatedMap = $relatedMapper->getEntityMap();
1170
+
1171
+		$otherKey = $otherKey ?: $relatedMap->getForeignKey();
1172
+
1173
+		// If no table name was provided, we can guess it by concatenating the two
1174
+		// models using underscores in alphabetical order. The two model names
1175
+		// are transformed to snake case from their default CamelCase also.
1176
+		if (is_null($table)) {
1177
+			$table = $this->joiningTable($relatedMap);
1178
+		}
1179
+
1180
+		return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1181
+	}
1182
+
1183
+	/**
1184
+	 * Define a polymorphic many-to-many relationship.
1185
+	 *
1186
+	 * @param mixed       $entity
1187
+	 * @param string      $related
1188
+	 * @param string      $name
1189
+	 * @param string|null $table
1190
+	 * @param string|null $foreignKey
1191
+	 * @param string|null $otherKey
1192
+	 * @param bool        $inverse
1193
+	 *
1194
+	 * @throws MappingException
1195
+	 *
1196
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1197
+	 */
1198
+	public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1199
+	{
1200
+		// Add the relation to the definition in map
1201
+		list(, $caller) = debug_backtrace(false);
1202
+		$relation = $caller['function'];
1203
+		$this->relatedClasses[$relation] = $related;
1204
+
1205
+		$this->addManyRelation($relation);
1206
+		$this->addForeignRelation($relation);
1207
+		$this->addPivotRelation($relation);
1208
+
1209
+		// First, we will need to determine the foreign key and "other key" for the
1210
+		// relationship. Once we have determined the keys we will make the query
1211
+		// instances, as well as the relationship instances we need for these.
1212
+		$foreignKey = $foreignKey ?: $name.'_id';
1213
+
1214
+		$relatedMapper = Manager::getInstance()->mapper($related);
1215
+
1216
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1217
+
1218
+		$table = $table ?: str_plural($name);
1219
+
1220
+		return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1221
+	}
1222
+
1223
+	/**
1224
+	 * Define a polymorphic, inverse many-to-many relationship.
1225
+	 *
1226
+	 * @param mixed       $entity
1227
+	 * @param string      $related
1228
+	 * @param string      $name
1229
+	 * @param string|null $table
1230
+	 * @param string|null $foreignKey
1231
+	 * @param string|null $otherKey
1232
+	 *
1233
+	 * @throws MappingException
1234
+	 *
1235
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1236
+	 */
1237
+	public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1238
+	{
1239
+		// Add the relation to the definition in map
1240
+		list(, $caller) = debug_backtrace(false);
1241
+		$relation = $caller['function'];
1242
+		$this->relatedClasses[$relation] = $related;
1243
+
1244
+		$this->addManyRelation($relation);
1245
+		$this->addForeignRelation($relation);
1246
+
1247
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1248
+
1249
+		// For the inverse of the polymorphic many-to-many relations, we will change
1250
+		// the way we determine the foreign and other keys, as it is the opposite
1251
+		// of the morph-to-many method since we're figuring out these inverses.
1252
+		$otherKey = $otherKey ?: $name.'_id';
1253
+
1254
+		return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1255
+	}
1256
+
1257
+	/**
1258
+	 * Get the joining table name for a many-to-many relation.
1259
+	 *
1260
+	 * @param EntityMap $relatedMap
1261
+	 *
1262
+	 * @return string
1263
+	 */
1264
+	public function joiningTable($relatedMap)
1265
+	{
1266
+		// The joining table name, by convention, is simply the snake cased models
1267
+		// sorted alphabetically and concatenated with an underscore, so we can
1268
+		// just sort the models and join them together to get the table name.
1269
+		$base = $this->getTable();
1270
+
1271
+		$related = $relatedMap->getTable();
1272
+
1273
+		$tables = [$related, $base];
1274
+
1275
+		// Now that we have the model names in an array we can just sort them and
1276
+		// use the implode function to join them together with an underscores,
1277
+		// which is typically used by convention within the database system.
1278
+		sort($tables);
1279
+
1280
+		return strtolower(implode('_', $tables));
1281
+	}
1282
+
1283
+	/**
1284
+	 * Get the polymorphic relationship columns.
1285
+	 *
1286
+	 * @param string $name
1287
+	 * @param string $type
1288
+	 * @param string $id
1289
+	 *
1290
+	 * @return string[]
1291
+	 */
1292
+	protected function getMorphs($name, $type, $id)
1293
+	{
1294
+		$type = $type ?: $name.'_type';
1295
+
1296
+		$id = $id ?: $name.'_id';
1297
+
1298
+		return [$type, $id];
1299
+	}
1300
+
1301
+	/**
1302
+	 * Get the class name for polymorphic relations.
1303
+	 *
1304
+	 * @return string
1305
+	 */
1306
+	public function getMorphClass()
1307
+	{
1308
+		$morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1309
+
1310
+		return $this->morphClass ?: $morphClass;
1311
+	}
1312
+
1313
+	/**
1314
+	 * Create a new Entity Collection instance.
1315
+	 *
1316
+	 * @param array $entities
1317
+	 *
1318
+	 * @return \Analogue\ORM\EntityCollection
1319
+	 */
1320
+	public function newCollection(array $entities = [])
1321
+	{
1322
+		$collection = new EntityCollection($entities, $this);
1323
+
1324
+		return $collection->keyBy($this->getKeyName());
1325
+	}
1326
+
1327
+	/**
1328
+	 * Process EntityMap parsing at initialization time.
1329
+	 *
1330
+	 * @return void
1331
+	 */
1332
+	public function initialize()
1333
+	{
1334
+		$userMethods = $this->getCustomMethods();
1335
+
1336
+		// Parse EntityMap for method based relationship
1337
+		if (count($userMethods) > 0) {
1338
+			$this->relationships = $this->parseMethodsForRelationship($userMethods);
1339
+		}
1340
+
1341
+		// Parse EntityMap for dynamic relationships
1342
+		if (count($this->dynamicRelationships) > 0) {
1343
+			$this->relationships = $this->relationships + $this->getDynamicRelationships();
1344
+		}
1345
+	}
1346
+
1347
+	/**
1348
+	 * Parse every relationships on the EntityMap and sort
1349
+	 * them by type.
1350
+	 *
1351
+	 * @return void
1352
+	 */
1353
+	public function boot()
1354
+	{
1355
+		if (count($this->relationships > 0)) {
1356
+			$this->sortRelationshipsByType();
1357
+		}
1358
+	}
1359
+
1360
+	/**
1361
+	 * Get Methods that has been added in the child class.
1362
+	 *
1363
+	 * @return array
1364
+	 */
1365
+	protected function getCustomMethods()
1366
+	{
1367
+		$mapMethods = get_class_methods($this);
1368
+
1369
+		$parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1370
+
1371
+		return array_diff($mapMethods, $parentsMethods);
1372
+	}
1373
+
1374
+	/**
1375
+	 * Parse user's class methods for relationships.
1376
+	 *
1377
+	 * @param array $customMethods
1378
+	 *
1379
+	 * @return array
1380
+	 */
1381
+	protected function parseMethodsForRelationship(array $customMethods)
1382
+	{
1383
+		$relationships = [];
1384
+
1385
+		$class = new ReflectionClass(get_class($this));
1386
+
1387
+		// Get the mapped Entity class, as we will detect relationships
1388
+		// methods by testing that the first argument is type-hinted to
1389
+		// the same class as the mapped Entity.
1390
+		$entityClass = $this->getClass();
1391
+
1392
+		foreach ($customMethods as $methodName) {
1393
+			$method = $class->getMethod($methodName);
1394
+
1395
+			if ($method->getNumberOfParameters() > 0) {
1396
+				$params = $method->getParameters();
1397
+
1398
+				if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1399
+					$relationships[] = $methodName;
1400
+				}
1401
+			}
1402
+		}
1403
+
1404
+		return $relationships;
1405
+	}
1406
+
1407
+	/**
1408
+	 * Sort Relationships methods by type.
1409
+	 *
1410
+	 * TODO : replace this by direclty setting these value
1411
+	 * in the corresponding methods, so we won't need
1412
+	 * the correpondancy tabble
1413
+	 *
1414
+	 * @return void
1415
+	 */
1416
+	protected function sortRelationshipsByType()
1417
+	{
1418
+		$entityClass = $this->getClass();
1419
+
1420
+		// Instantiate a dummy entity which we will pass to relationship methods.
1421
+		$entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1422
+
1423
+		foreach ($this->relationships as $relation) {
1424
+			$this->$relation($entity);
1425
+		}
1426
+	}
1427
+
1428
+	/**
1429
+	 * Override this method for custom entity instantiation.
1430
+	 *
1431
+	 * @return null
1432
+	 */
1433
+	public function activator()
1434
+	{
1435
+	}
1436
+
1437
+	/**
1438
+	 * Magic call to dynamic relationships.
1439
+	 *
1440
+	 * @param string $method
1441
+	 * @param array  $parameters
1442
+	 *
1443
+	 * @throws Exception
1444
+	 *
1445
+	 * @return mixed
1446
+	 */
1447
+	public function __call($method, $parameters)
1448
+	{
1449
+		if (!array_key_exists($method, $this->dynamicRelationships)) {
1450
+			throw new Exception(get_class($this)." has no method $method");
1451
+		}
1452
+
1453
+		// Add $this to parameters so the closure can call relationship method on the map.
1454
+		$parameters[] = $this;
1455
+
1456
+		return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1457
+	}
1458 1458
 }
Please login to merge, or discard this patch.