Completed
Branch master (5737c6)
by Rémi
02:51
created
src/Relationships/MorphTo.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -163,7 +163,7 @@
 block discarded – undo
163 163
     {
164 164
         $foreign = $this->foreignKey;
165 165
 
166
-        return BaseCollection::make($this->dictionary[$type])->map(function ($entities) use ($foreign) {
166
+        return BaseCollection::make($this->dictionary[$type])->map(function($entities) use ($foreign) {
167 167
             return head($entities)->{$foreign};
168 168
 
169 169
         })->unique();
Please login to merge, or discard this patch.
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -8,226 +8,226 @@
 block discarded – undo
8 8
 
9 9
 class MorphTo extends BelongsTo
10 10
 {
11
-    /**
12
-     * The type of the polymorphic relation.
13
-     *
14
-     * @var string
15
-     */
16
-    protected $morphType;
17
-
18
-    /**
19
-     * The entities whose relations are being eager loaded.
20
-     *
21
-     * @var EntityCollection
22
-     */
23
-    protected $entities;
24
-
25
-    /**
26
-     * All of the result sets keyed by ID.
27
-     *
28
-     * @var array
29
-     */
30
-    protected $dictionary = [];
31
-
32
-    /**
33
-     * Indicates if soft-deleted model instances should be fetched.
34
-     *
35
-     * @var bool
36
-     */
37
-    protected $withTrashed = false;
38
-
39
-    /**
40
-     * Indicate if the parent entity hold the key for the relation.
41
-     *
42
-     * @var bool
43
-     */
44
-    protected static $ownForeignKey = true;
45
-
46
-    /**
47
-     * Create a new belongs to relationship instance.
48
-     *
49
-     * @param Mapper                 $mapper
50
-     * @param \Analogue\ORM\Mappable $parent
51
-     * @param string                 $foreignKey
52
-     * @param string                 $otherKey
53
-     * @param string                 $type
54
-     * @param string                 $relation
55
-     */
56
-    public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $type, $relation)
57
-    {
58
-        $this->morphType = $type;
59
-
60
-        parent::__construct($mapper, $parent, $foreignKey, $otherKey, $relation);
61
-    }
62
-
63
-    /**
64
-     * Set the constraints for an eager load of the relation.
65
-     *
66
-     * @param array $results
67
-     *
68
-     * @return void
69
-     */
70
-    public function addEagerConstraints(array $results)
71
-    {
72
-        $this->buildDictionary($results);
73
-    }
74
-
75
-    /**
76
-     * Build a dictionary with the entities.
77
-     *
78
-     * @param array $results
79
-     *
80
-     * @return void
81
-     */
82
-    protected function buildDictionary($results)
83
-    {
84
-        foreach ($results as $result) {
85
-            if ($result[$this->morphType]) {
86
-                $this->dictionary[$result[$this->morphType]][$result[$this->foreignKey]] = $result;
87
-            }
88
-        }
89
-    }
90
-
91
-    /**
92
-     * Match the eagerly loaded results to their parents.
93
-     *
94
-     * @param array  $results
95
-     * @param string $relation
96
-     *
97
-     * @return array
98
-     */
99
-    public function match(array $results, $relation)
100
-    {
101
-        return $entities;
102
-    }
103
-
104
-    /**
105
-     * Get the results of the relationship.
106
-     *
107
-     * @throws \Analogue\ORM\Exceptions\MappingException
108
-     *
109
-     * @return EntityCollection
110
-     */
111
-    public function getEager()
112
-    {
113
-        foreach (array_keys($this->dictionary) as $type) {
114
-            $this->matchToMorphParents($type, $this->getResultsByType($type));
115
-        }
116
-
117
-        return $this->entities;
118
-    }
119
-
120
-    /**
121
-     * Match the results for a given type to their parents.
122
-     *
123
-     * @param string           $type
124
-     * @param EntityCollection $results
125
-     *
126
-     * @return void
127
-     */
128
-    protected function matchToMorphParents($type, EntityCollection $results)
129
-    {
130
-        $mapper = $this->relatedMapper->getManager()->mapper($type);
131
-        $keyName = $mapper->getEntityMap()->getKeyName();
132
-
133
-        foreach ($results as $result) {
134
-            $key = $result[$keyName];
135
-
136
-            if (isset($this->dictionary[$type][$key])) {
137
-                foreach ($this->dictionary[$type][$key] as $result) {
138
-                    $result[$this->relation] = $result;
139
-                }
140
-            }
141
-        }
142
-    }
143
-
144
-    /**
145
-     * Get all of the relation results for a type.
146
-     *
147
-     * @param string $type
148
-     *
149
-     * @throws \Analogue\ORM\Exceptions\MappingException
150
-     *
151
-     * @return EntityCollection
152
-     */
153
-    protected function getResultsByType($type)
154
-    {
155
-        $mapper = $this->relatedMapper->getManager()->mapper($type);
156
-
157
-        $key = $mapper->getEntityMap()->getKeyName();
158
-
159
-        $query = $mapper->getQuery();
160
-
161
-        return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
162
-    }
163
-
164
-    /**
165
-     * Gather all of the foreign keys for a given type.
166
-     *
167
-     * @param string $type
168
-     *
169
-     * @return BaseCollection
170
-     */
171
-    protected function gatherKeysByType($type)
172
-    {
173
-        $foreign = $this->foreignKey;
174
-
175
-        return BaseCollection::make($this->dictionary[$type])->map(function ($entities) use ($foreign) {
176
-            return head($entities)->{$foreign};
177
-        })->unique();
178
-    }
179
-
180
-    /**
181
-     * Associate the model instance to the given parent.
182
-     *
183
-     * @param mixed $entity
184
-     *
185
-     * @return void
186
-     */
187
-    public function associate($entity)
188
-    {
189
-        // The Mapper will retrieve this association within the object model, we won't be using
190
-        // the foreign key attribute inside the parent Entity.
191
-        //
192
-        //$this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
193
-        //
194
-        // Instead, we'll just add the object to the Entity's attribute
195
-
196
-        $this->parent->setEntityAttribute($this->relation, $entity->getEntityObject());
197
-    }
198
-
199
-    /**
200
-     * Get the foreign key value pair for a related object.
201
-     *
202
-     * @var mixed
203
-     *
204
-     * @return array
205
-     */
206
-    public function getForeignKeyValuePair($related)
207
-    {
208
-        $foreignKey = $this->getForeignKey();
209
-
210
-        if ($related) {
211
-            $wrapper = $this->factory->make($related);
212
-
213
-            $relatedKey = $this->relatedMap->getKeyName();
214
-
215
-            return [
216
-                $foreignKey      => $wrapper->getEntityAttribute($relatedKey),
217
-                $this->morphType => $wrapper->getMap()->getMorphClass(),
218
-            ];
219
-        } else {
220
-            return [$foreignKey => null];
221
-        }
222
-    }
223
-
224
-    /**
225
-     * Get the dictionary used by the relationship.
226
-     *
227
-     * @return array
228
-     */
229
-    public function getDictionary()
230
-    {
231
-        return $this->dictionary;
232
-    }
11
+	/**
12
+	 * The type of the polymorphic relation.
13
+	 *
14
+	 * @var string
15
+	 */
16
+	protected $morphType;
17
+
18
+	/**
19
+	 * The entities whose relations are being eager loaded.
20
+	 *
21
+	 * @var EntityCollection
22
+	 */
23
+	protected $entities;
24
+
25
+	/**
26
+	 * All of the result sets keyed by ID.
27
+	 *
28
+	 * @var array
29
+	 */
30
+	protected $dictionary = [];
31
+
32
+	/**
33
+	 * Indicates if soft-deleted model instances should be fetched.
34
+	 *
35
+	 * @var bool
36
+	 */
37
+	protected $withTrashed = false;
38
+
39
+	/**
40
+	 * Indicate if the parent entity hold the key for the relation.
41
+	 *
42
+	 * @var bool
43
+	 */
44
+	protected static $ownForeignKey = true;
45
+
46
+	/**
47
+	 * Create a new belongs to relationship instance.
48
+	 *
49
+	 * @param Mapper                 $mapper
50
+	 * @param \Analogue\ORM\Mappable $parent
51
+	 * @param string                 $foreignKey
52
+	 * @param string                 $otherKey
53
+	 * @param string                 $type
54
+	 * @param string                 $relation
55
+	 */
56
+	public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $type, $relation)
57
+	{
58
+		$this->morphType = $type;
59
+
60
+		parent::__construct($mapper, $parent, $foreignKey, $otherKey, $relation);
61
+	}
62
+
63
+	/**
64
+	 * Set the constraints for an eager load of the relation.
65
+	 *
66
+	 * @param array $results
67
+	 *
68
+	 * @return void
69
+	 */
70
+	public function addEagerConstraints(array $results)
71
+	{
72
+		$this->buildDictionary($results);
73
+	}
74
+
75
+	/**
76
+	 * Build a dictionary with the entities.
77
+	 *
78
+	 * @param array $results
79
+	 *
80
+	 * @return void
81
+	 */
82
+	protected function buildDictionary($results)
83
+	{
84
+		foreach ($results as $result) {
85
+			if ($result[$this->morphType]) {
86
+				$this->dictionary[$result[$this->morphType]][$result[$this->foreignKey]] = $result;
87
+			}
88
+		}
89
+	}
90
+
91
+	/**
92
+	 * Match the eagerly loaded results to their parents.
93
+	 *
94
+	 * @param array  $results
95
+	 * @param string $relation
96
+	 *
97
+	 * @return array
98
+	 */
99
+	public function match(array $results, $relation)
100
+	{
101
+		return $entities;
102
+	}
103
+
104
+	/**
105
+	 * Get the results of the relationship.
106
+	 *
107
+	 * @throws \Analogue\ORM\Exceptions\MappingException
108
+	 *
109
+	 * @return EntityCollection
110
+	 */
111
+	public function getEager()
112
+	{
113
+		foreach (array_keys($this->dictionary) as $type) {
114
+			$this->matchToMorphParents($type, $this->getResultsByType($type));
115
+		}
116
+
117
+		return $this->entities;
118
+	}
119
+
120
+	/**
121
+	 * Match the results for a given type to their parents.
122
+	 *
123
+	 * @param string           $type
124
+	 * @param EntityCollection $results
125
+	 *
126
+	 * @return void
127
+	 */
128
+	protected function matchToMorphParents($type, EntityCollection $results)
129
+	{
130
+		$mapper = $this->relatedMapper->getManager()->mapper($type);
131
+		$keyName = $mapper->getEntityMap()->getKeyName();
132
+
133
+		foreach ($results as $result) {
134
+			$key = $result[$keyName];
135
+
136
+			if (isset($this->dictionary[$type][$key])) {
137
+				foreach ($this->dictionary[$type][$key] as $result) {
138
+					$result[$this->relation] = $result;
139
+				}
140
+			}
141
+		}
142
+	}
143
+
144
+	/**
145
+	 * Get all of the relation results for a type.
146
+	 *
147
+	 * @param string $type
148
+	 *
149
+	 * @throws \Analogue\ORM\Exceptions\MappingException
150
+	 *
151
+	 * @return EntityCollection
152
+	 */
153
+	protected function getResultsByType($type)
154
+	{
155
+		$mapper = $this->relatedMapper->getManager()->mapper($type);
156
+
157
+		$key = $mapper->getEntityMap()->getKeyName();
158
+
159
+		$query = $mapper->getQuery();
160
+
161
+		return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
162
+	}
163
+
164
+	/**
165
+	 * Gather all of the foreign keys for a given type.
166
+	 *
167
+	 * @param string $type
168
+	 *
169
+	 * @return BaseCollection
170
+	 */
171
+	protected function gatherKeysByType($type)
172
+	{
173
+		$foreign = $this->foreignKey;
174
+
175
+		return BaseCollection::make($this->dictionary[$type])->map(function ($entities) use ($foreign) {
176
+			return head($entities)->{$foreign};
177
+		})->unique();
178
+	}
179
+
180
+	/**
181
+	 * Associate the model instance to the given parent.
182
+	 *
183
+	 * @param mixed $entity
184
+	 *
185
+	 * @return void
186
+	 */
187
+	public function associate($entity)
188
+	{
189
+		// The Mapper will retrieve this association within the object model, we won't be using
190
+		// the foreign key attribute inside the parent Entity.
191
+		//
192
+		//$this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
193
+		//
194
+		// Instead, we'll just add the object to the Entity's attribute
195
+
196
+		$this->parent->setEntityAttribute($this->relation, $entity->getEntityObject());
197
+	}
198
+
199
+	/**
200
+	 * Get the foreign key value pair for a related object.
201
+	 *
202
+	 * @var mixed
203
+	 *
204
+	 * @return array
205
+	 */
206
+	public function getForeignKeyValuePair($related)
207
+	{
208
+		$foreignKey = $this->getForeignKey();
209
+
210
+		if ($related) {
211
+			$wrapper = $this->factory->make($related);
212
+
213
+			$relatedKey = $this->relatedMap->getKeyName();
214
+
215
+			return [
216
+				$foreignKey      => $wrapper->getEntityAttribute($relatedKey),
217
+				$this->morphType => $wrapper->getMap()->getMorphClass(),
218
+			];
219
+		} else {
220
+			return [$foreignKey => null];
221
+		}
222
+	}
223
+
224
+	/**
225
+	 * Get the dictionary used by the relationship.
226
+	 *
227
+	 * @return array
228
+	 */
229
+	public function getDictionary()
230
+	{
231
+		return $this->dictionary;
232
+	}
233 233
 }
Please login to merge, or discard this patch.
src/EntityMap.php 2 patches
Doc Comments   +1 added lines, -3 removed lines patch added patch discarded remove patch
@@ -931,7 +931,6 @@  discard block
 block discarded – undo
931 931
      * @param string      $related
932 932
      * @param string|null $foreignKey
933 933
      * @param string|null $otherKey
934
-     * @param string|null $relation
935 934
      *
936 935
      * @throws MappingException
937 936
      *
@@ -1138,11 +1137,10 @@  discard block
 block discarded – undo
1138 1137
      * Define a many-to-many relationship.
1139 1138
      *
1140 1139
      * @param mixed       $entity
1141
-     * @param string      $relatedClass
1140
+     * @param string      $related
1142 1141
      * @param string|null $table
1143 1142
      * @param string|null $foreignKey
1144 1143
      * @param string|null $otherKey
1145
-     * @param string|null $relation
1146 1144
      *
1147 1145
      * @throws MappingException
1148 1146
      *
Please login to merge, or discard this 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() : string
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() : string
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.
src/Plugins/SoftDeletes/SoftDeletingScope.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@
 block discarded – undo
33 33
     /**
34 34
      * Remove the scope from the given Analogue query builder.
35 35
      *
36
-     * @param mixed $query
36
+     * @param Query $query
37 37
      *
38 38
      * @return void
39 39
      */
Please login to merge, or discard this patch.
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -7,112 +7,112 @@
 block discarded – undo
7 7
 
8 8
 class SoftDeletingScope implements ScopeInterface
9 9
 {
10
-    /**
11
-     * All of the extensions to be added to the builder.
12
-     *
13
-     * @var array
14
-     */
15
-    protected $extensions = ['WithTrashed', 'OnlyTrashed'];
16
-
17
-    /**
18
-     * Apply the scope to a given Analogue query builder.
19
-     *
20
-     * @param \Analogue\ORM\System\Query $query
21
-     *
22
-     * @return void
23
-     */
24
-    public function apply(Query $query)
25
-    {
26
-        $entityMap = $query->getMapper()->getEntityMap();
27
-
28
-        $query->whereNull($entityMap->getQualifiedDeletedAtColumn());
29
-
30
-        $this->extend($query);
31
-    }
32
-
33
-    /**
34
-     * Remove the scope from the given Analogue query builder.
35
-     *
36
-     * @param mixed $query
37
-     *
38
-     * @return void
39
-     */
40
-    public function remove(Query $query)
41
-    {
42
-        $column = $query->getMapper()->getEntityMap()->getQualifiedDeletedAtColumn();
43
-
44
-        $query = $query->getQuery();
45
-
46
-        foreach ((array) $query->wheres as $key => $where) {
47
-            // If the where clause is a soft delete date constraint, we will remove it from
48
-            // the query and reset the keys on the wheres. This allows this developer to
49
-            // include deleted model in a relationship result set that is lazy loaded.
50
-            if ($this->isSoftDeleteConstraint($where, $column)) {
51
-                unset($query->wheres[$key]);
52
-
53
-                $query->wheres = array_values($query->wheres);
54
-            }
55
-        }
56
-    }
57
-
58
-    /**
59
-     * Extend the query builder with the needed functions.
60
-     *
61
-     * @param \Analogue\ORM\System\Query $query
62
-     *
63
-     * @return void
64
-     */
65
-    public function extend(Query $query)
66
-    {
67
-        foreach ($this->extensions as $extension) {
68
-            $this->{"add{$extension}"}($query);
69
-        }
70
-    }
71
-
72
-    /**
73
-     * Add the with-trashed extension to the builder.
74
-     *
75
-     * @param \Analogue\ORM\System\Query $query
76
-     *
77
-     * @return void
78
-     */
79
-    protected function addWithTrashed(Query $query)
80
-    {
81
-        $query->macro('withTrashed', function (Query $query) {
82
-            $this->remove($query);
83
-
84
-            return $query;
85
-        });
86
-    }
87
-
88
-    /**
89
-     * Add the only-trashed extension to the builder.
90
-     *
91
-     * @param \Analogue\ORM\System\Query $query
92
-     *
93
-     * @return void
94
-     */
95
-    protected function addOnlyTrashed(Query $query)
96
-    {
97
-        $query->macro('onlyTrashed', function (Query $query) {
98
-            $this->remove($query);
99
-
100
-            $query->getQuery()->whereNotNull($query->getMapper()->getEntityMap()->getQualifiedDeletedAtColumn());
101
-
102
-            return $query;
103
-        });
104
-    }
105
-
106
-    /**
107
-     * Determine if the given where clause is a soft delete constraint.
108
-     *
109
-     * @param array  $where
110
-     * @param string $column
111
-     *
112
-     * @return bool
113
-     */
114
-    protected function isSoftDeleteConstraint(array $where, $column)
115
-    {
116
-        return $where['type'] == 'Null' && $where['column'] == $column;
117
-    }
10
+	/**
11
+	 * All of the extensions to be added to the builder.
12
+	 *
13
+	 * @var array
14
+	 */
15
+	protected $extensions = ['WithTrashed', 'OnlyTrashed'];
16
+
17
+	/**
18
+	 * Apply the scope to a given Analogue query builder.
19
+	 *
20
+	 * @param \Analogue\ORM\System\Query $query
21
+	 *
22
+	 * @return void
23
+	 */
24
+	public function apply(Query $query)
25
+	{
26
+		$entityMap = $query->getMapper()->getEntityMap();
27
+
28
+		$query->whereNull($entityMap->getQualifiedDeletedAtColumn());
29
+
30
+		$this->extend($query);
31
+	}
32
+
33
+	/**
34
+	 * Remove the scope from the given Analogue query builder.
35
+	 *
36
+	 * @param mixed $query
37
+	 *
38
+	 * @return void
39
+	 */
40
+	public function remove(Query $query)
41
+	{
42
+		$column = $query->getMapper()->getEntityMap()->getQualifiedDeletedAtColumn();
43
+
44
+		$query = $query->getQuery();
45
+
46
+		foreach ((array) $query->wheres as $key => $where) {
47
+			// If the where clause is a soft delete date constraint, we will remove it from
48
+			// the query and reset the keys on the wheres. This allows this developer to
49
+			// include deleted model in a relationship result set that is lazy loaded.
50
+			if ($this->isSoftDeleteConstraint($where, $column)) {
51
+				unset($query->wheres[$key]);
52
+
53
+				$query->wheres = array_values($query->wheres);
54
+			}
55
+		}
56
+	}
57
+
58
+	/**
59
+	 * Extend the query builder with the needed functions.
60
+	 *
61
+	 * @param \Analogue\ORM\System\Query $query
62
+	 *
63
+	 * @return void
64
+	 */
65
+	public function extend(Query $query)
66
+	{
67
+		foreach ($this->extensions as $extension) {
68
+			$this->{"add{$extension}"}($query);
69
+		}
70
+	}
71
+
72
+	/**
73
+	 * Add the with-trashed extension to the builder.
74
+	 *
75
+	 * @param \Analogue\ORM\System\Query $query
76
+	 *
77
+	 * @return void
78
+	 */
79
+	protected function addWithTrashed(Query $query)
80
+	{
81
+		$query->macro('withTrashed', function (Query $query) {
82
+			$this->remove($query);
83
+
84
+			return $query;
85
+		});
86
+	}
87
+
88
+	/**
89
+	 * Add the only-trashed extension to the builder.
90
+	 *
91
+	 * @param \Analogue\ORM\System\Query $query
92
+	 *
93
+	 * @return void
94
+	 */
95
+	protected function addOnlyTrashed(Query $query)
96
+	{
97
+		$query->macro('onlyTrashed', function (Query $query) {
98
+			$this->remove($query);
99
+
100
+			$query->getQuery()->whereNotNull($query->getMapper()->getEntityMap()->getQualifiedDeletedAtColumn());
101
+
102
+			return $query;
103
+		});
104
+	}
105
+
106
+	/**
107
+	 * Determine if the given where clause is a soft delete constraint.
108
+	 *
109
+	 * @param array  $where
110
+	 * @param string $column
111
+	 *
112
+	 * @return bool
113
+	 */
114
+	protected function isSoftDeleteConstraint(array $where, $column)
115
+	{
116
+		return $where['type'] == 'Null' && $where['column'] == $column;
117
+	}
118 118
 }
Please login to merge, or discard this patch.
src/Relationships/HasMany.php 2 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -9,7 +9,7 @@  discard block
 block discarded – undo
9 9
      *
10 10
      * @param  $relation
11 11
      *
12
-     * @return mixed
12
+     * @return \Analogue\ORM\EntityCollection
13 13
      */
14 14
     public function getResults($relation)
15 15
     {
@@ -23,7 +23,7 @@  discard block
 block discarded – undo
23 23
     /**
24 24
      * Match the eagerly loaded results to their parents.
25 25
      *
26
-     * @param array  $$results
26
+     * @param array  $results
27 27
      * @param string $relation
28 28
      *
29 29
      * @return array
Please login to merge, or discard this patch.
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -4,32 +4,32 @@
 block discarded – undo
4 4
 
5 5
 class HasMany extends HasOneOrMany
6 6
 {
7
-    /**
8
-     * Lazy-Load the results of the relationship.
9
-     *
10
-     * @param  $relation
11
-     *
12
-     * @return mixed
13
-     */
14
-    public function getResults($relation)
15
-    {
16
-        $results = $this->query->get();
7
+	/**
8
+	 * Lazy-Load the results of the relationship.
9
+	 *
10
+	 * @param  $relation
11
+	 *
12
+	 * @return mixed
13
+	 */
14
+	public function getResults($relation)
15
+	{
16
+		$results = $this->query->get();
17 17
 
18
-        $this->cacheRelation($results, $relation);
18
+		$this->cacheRelation($results, $relation);
19 19
 
20
-        return $results;
21
-    }
20
+		return $results;
21
+	}
22 22
 
23
-    /**
24
-     * Match the eagerly loaded results to their parents.
25
-     *
26
-     * @param array  $$results
27
-     * @param string $relation
28
-     *
29
-     * @return array
30
-     */
31
-    public function match(array $results, $relation)
32
-    {
33
-        return $this->matchMany($results, $relation);
34
-    }
23
+	/**
24
+	 * Match the eagerly loaded results to their parents.
25
+	 *
26
+	 * @param array  $$results
27
+	 * @param string $relation
28
+	 *
29
+	 * @return array
30
+	 */
31
+	public function match(array $results, $relation)
32
+	{
33
+		return $this->matchMany($results, $relation);
34
+	}
35 35
 }
Please login to merge, or discard this patch.
src/Relationships/HasManyThrough.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -291,7 +291,6 @@
 block discarded – undo
291 291
      * Run synchronization content if needed by the
292 292
      * relation type.
293 293
      *
294
-     * @param array $actualContent
295 294
      *
296 295
      * @return void
297 296
      */
Please login to merge, or discard this patch.
Indentation   +290 added lines, -290 removed lines patch added patch discarded remove patch
@@ -9,294 +9,294 @@
 block discarded – undo
9 9
 
10 10
 class HasManyThrough extends Relationship
11 11
 {
12
-    /**
13
-     * The distance parent Entity instance.
14
-     *
15
-     * @var \Analogue\ORM\Entity
16
-     */
17
-    protected $farParent;
18
-
19
-    /**
20
-     * The far parent map instance.
21
-     *
22
-     * @var \Analogue\ORM\EntityMap
23
-     */
24
-    protected $farParentMap;
25
-
26
-    /**
27
-     * The near key on the relationship.
28
-     *
29
-     * @var string
30
-     */
31
-    protected $firstKey;
32
-
33
-    /**
34
-     * The far key on the relationship.
35
-     *
36
-     * @var string
37
-     */
38
-    protected $secondKey;
39
-
40
-    /**
41
-     * Create a new has many relationship instance.
42
-     *
43
-     * @param Mapper                  $mapper
44
-     * @param \Analogue\ORM\Mappable  $farParent
45
-     * @param \Analogue\ORM\EntityMap $parentMap
46
-     * @param string                  $firstKey
47
-     * @param string                  $secondKey
48
-     *
49
-     * @throws \Analogue\ORM\Exceptions\MappingException
50
-     */
51
-    public function __construct(Mapper $mapper, $farParent, $parentMap, $firstKey, $secondKey)
52
-    {
53
-        $this->firstKey = $firstKey;
54
-        $this->secondKey = $secondKey;
55
-        $this->farParent = $farParent;
56
-
57
-        $this->farParentMap = $mapper->getManager()->mapper($farParent)->getEntityMap();
58
-        $parentInstance = $mapper->getManager()->mapper($parentMap->getClass())->newInstance();
59
-
60
-        parent::__construct($mapper, $parentInstance);
61
-    }
62
-
63
-    /**
64
-     * Set the base constraints on the relation query.
65
-     *
66
-     * @return void
67
-     */
68
-    public function addConstraints()
69
-    {
70
-        $parentTable = $this->parentMap->getTable();
71
-
72
-        $this->setJoin();
73
-
74
-        if (static::$constraints) {
75
-            $farParentKeyName = $this->farParentMap->getKeyName();
76
-
77
-            $this->query->where(
78
-                $parentTable.'.'.$this->firstKey,
79
-                '=',
80
-                $this->farParent->getEntityAttribute($farParentKeyName)
81
-            );
82
-        }
83
-    }
84
-
85
-    /**
86
-     * Add the constraints for a relationship count query.
87
-     *
88
-     * @param Query $query
89
-     * @param Query $parent
90
-     *
91
-     * @return Query
92
-     */
93
-    public function getRelationCountQuery(Query $query, Query $parent)
94
-    {
95
-        $parentTable = $this->parentMap->getTable();
96
-
97
-        $this->setJoin($query);
98
-
99
-        $query->select(new Expression('count(*)'));
100
-
101
-        $key = $this->wrap($parentTable.'.'.$this->firstKey);
102
-
103
-        return $query->where($this->getHasCompareKey(), '=', new Expression($key));
104
-    }
105
-
106
-    /**
107
-     * Set the join clause on the query.
108
-     *
109
-     * @param null|Query $query
110
-     *
111
-     * @return void
112
-     */
113
-    protected function setJoin(Query $query = null)
114
-    {
115
-        $query = $query ?: $this->query;
116
-
117
-        $foreignKey = $this->relatedMap->getTable().'.'.$this->secondKey;
118
-
119
-        $query->join($this->parentMap->getTable(), $this->getQualifiedParentKeyName(), '=', $foreignKey);
120
-    }
121
-
122
-    /**
123
-     * Set the constraints for an eager load of the relation.
124
-     *
125
-     * @param array $results
126
-     *
127
-     * @return void
128
-     */
129
-    public function addEagerConstraints(array $results)
130
-    {
131
-        $table = $this->parentMap->getTable();
132
-
133
-        $this->query->whereIn($table.'.'.$this->firstKey, $this->getKeysFromResults($results));
134
-    }
135
-
136
-    /**
137
-     * Match eagerly loaded relationship to a result set.
138
-     *
139
-     * @param array  $results
140
-     * @param string $relation
141
-     *
142
-     * @return array
143
-     */
144
-    public function match(array $results, $relation)
145
-    {
146
-        $entities = $this->getEager();
147
-
148
-        $dictionary = $this->buildDictionary($entities);
149
-
150
-        $relatedKey = $this->relatedMap->getKeyName();
151
-
152
-        $cache = $this->parentMapper->getEntityCache();
153
-
154
-        $host = $this;
155
-
156
-        // Once we have the dictionary we can simply spin through the parent entities to
157
-        // link them up with their children using the keyed dictionary to make the
158
-        // matching very convenient and easy work. Then we'll just return them.
159
-        return array_map(function ($result) use ($relation, $relatedKey, $dictionary, $cache, $host) {
160
-            $key = $result[$relatedKey];
161
-
162
-            if (isset($dictionary[$key])) {
163
-                $value = $host->relatedMap->newCollection($dictionary[$key]);
164
-
165
-                $result[$relation] = $value;
166
-
167
-                $cache->cacheLoadedRelationResult($key, $relation, $value, $this);
168
-            }
169
-
170
-            return $result;
171
-        }, $results);
172
-    }
173
-
174
-    /**
175
-     * Build model dictionary keyed by the relation's foreign key.
176
-     *
177
-     * @param EntityCollection $results
178
-     *
179
-     * @return array
180
-     */
181
-    protected function buildDictionary(EntityCollection $results)
182
-    {
183
-        $dictionary = [];
184
-
185
-        $foreign = $this->firstKey;
186
-
187
-        // First we will create a dictionary of entities keyed by the foreign key of the
188
-        // relationship as this will allow us to quickly access all of the related
189
-        // entities without having to do nested looping which will be quite slow.
190
-        foreach ($results as $result) {
191
-            $dictionary[$result->{$foreign}][] = $result;
192
-        }
193
-
194
-        return $dictionary;
195
-    }
196
-
197
-    /**
198
-     * Get the results of the relationship.
199
-     *
200
-     * @param  $relation
201
-     *
202
-     * @return EntityCollection
203
-     */
204
-    public function getResults($relation)
205
-    {
206
-        $results = $this->query->get();
207
-
208
-        $this->cacheRelation($results, $relation);
209
-
210
-        return $results;
211
-    }
212
-
213
-    /**
214
-     * Execute the query as a "select" statement.
215
-     *
216
-     * @param array $columns
217
-     *
218
-     * @return EntityCollection
219
-     */
220
-    public function get($columns = ['*'])
221
-    {
222
-        // First we'll add the proper select columns onto the query so it is run with
223
-        // the proper columns. Then, we will get the results and hydrate out pivot
224
-        // entities with the result of those columns as a separate model relation.
225
-        $select = $this->getSelectColumns($columns);
226
-
227
-        $entities = $this->query->addSelect($select)->getEntities();
228
-
229
-        // If we actually found entities we will also eager load any relationships that
230
-        // have been specified as needing to be eager loaded. This will solve the
231
-        // n + 1 query problem for the developer and also increase performance.
232
-        if (count($entities) > 0) {
233
-            $entities = $this->query->eagerLoadRelations($entities);
234
-        }
235
-
236
-        return $this->relatedMap->newCollection($entities);
237
-    }
238
-
239
-    /**
240
-     * Set the select clause for the relation query.
241
-     *
242
-     * @param array $columns
243
-     *
244
-     * @return BelongsToMany
245
-     */
246
-    protected function getSelectColumns(array $columns = ['*'])
247
-    {
248
-        if ($columns == ['*']) {
249
-            $columns = [$this->relatedMap->getTable().'.*'];
250
-        }
251
-
252
-        return array_merge($columns, [$this->parentMap->getTable().'.'.$this->firstKey]);
253
-    }
254
-
255
-    /**
256
-     * Get a paginator for the "select" statement.
257
-     *
258
-     * @param int   $perPage
259
-     * @param array $columns
260
-     *
261
-     * @return \Illuminate\Pagination\LengthAwarePaginator
262
-     */
263
-    public function paginate($perPage = null, $columns = ['*'])
264
-    {
265
-        $this->query->addSelect($this->getSelectColumns($columns));
266
-
267
-        return $this->query->paginate($perPage, $columns);
268
-    }
269
-
270
-    /**
271
-     * Get the key name of the parent model.
272
-     *
273
-     * @return string
274
-     */
275
-    protected function getQualifiedParentKeyName()
276
-    {
277
-        return $this->parentMap->getQualifiedKeyName();
278
-    }
279
-
280
-    /**
281
-     * Get the key for comparing against the parent key in "has" query.
282
-     *
283
-     * @return string
284
-     */
285
-    public function getHasCompareKey()
286
-    {
287
-        return $this->farParentMap->getQualifiedKeyName();
288
-    }
289
-
290
-    /**
291
-     * Run synchronization content if needed by the
292
-     * relation type.
293
-     *
294
-     * @param array $actualContent
295
-     *
296
-     * @return void
297
-     */
298
-    public function sync(array $entities)
299
-    {
300
-        // N/A
301
-    }
12
+	/**
13
+	 * The distance parent Entity instance.
14
+	 *
15
+	 * @var \Analogue\ORM\Entity
16
+	 */
17
+	protected $farParent;
18
+
19
+	/**
20
+	 * The far parent map instance.
21
+	 *
22
+	 * @var \Analogue\ORM\EntityMap
23
+	 */
24
+	protected $farParentMap;
25
+
26
+	/**
27
+	 * The near key on the relationship.
28
+	 *
29
+	 * @var string
30
+	 */
31
+	protected $firstKey;
32
+
33
+	/**
34
+	 * The far key on the relationship.
35
+	 *
36
+	 * @var string
37
+	 */
38
+	protected $secondKey;
39
+
40
+	/**
41
+	 * Create a new has many relationship instance.
42
+	 *
43
+	 * @param Mapper                  $mapper
44
+	 * @param \Analogue\ORM\Mappable  $farParent
45
+	 * @param \Analogue\ORM\EntityMap $parentMap
46
+	 * @param string                  $firstKey
47
+	 * @param string                  $secondKey
48
+	 *
49
+	 * @throws \Analogue\ORM\Exceptions\MappingException
50
+	 */
51
+	public function __construct(Mapper $mapper, $farParent, $parentMap, $firstKey, $secondKey)
52
+	{
53
+		$this->firstKey = $firstKey;
54
+		$this->secondKey = $secondKey;
55
+		$this->farParent = $farParent;
56
+
57
+		$this->farParentMap = $mapper->getManager()->mapper($farParent)->getEntityMap();
58
+		$parentInstance = $mapper->getManager()->mapper($parentMap->getClass())->newInstance();
59
+
60
+		parent::__construct($mapper, $parentInstance);
61
+	}
62
+
63
+	/**
64
+	 * Set the base constraints on the relation query.
65
+	 *
66
+	 * @return void
67
+	 */
68
+	public function addConstraints()
69
+	{
70
+		$parentTable = $this->parentMap->getTable();
71
+
72
+		$this->setJoin();
73
+
74
+		if (static::$constraints) {
75
+			$farParentKeyName = $this->farParentMap->getKeyName();
76
+
77
+			$this->query->where(
78
+				$parentTable.'.'.$this->firstKey,
79
+				'=',
80
+				$this->farParent->getEntityAttribute($farParentKeyName)
81
+			);
82
+		}
83
+	}
84
+
85
+	/**
86
+	 * Add the constraints for a relationship count query.
87
+	 *
88
+	 * @param Query $query
89
+	 * @param Query $parent
90
+	 *
91
+	 * @return Query
92
+	 */
93
+	public function getRelationCountQuery(Query $query, Query $parent)
94
+	{
95
+		$parentTable = $this->parentMap->getTable();
96
+
97
+		$this->setJoin($query);
98
+
99
+		$query->select(new Expression('count(*)'));
100
+
101
+		$key = $this->wrap($parentTable.'.'.$this->firstKey);
102
+
103
+		return $query->where($this->getHasCompareKey(), '=', new Expression($key));
104
+	}
105
+
106
+	/**
107
+	 * Set the join clause on the query.
108
+	 *
109
+	 * @param null|Query $query
110
+	 *
111
+	 * @return void
112
+	 */
113
+	protected function setJoin(Query $query = null)
114
+	{
115
+		$query = $query ?: $this->query;
116
+
117
+		$foreignKey = $this->relatedMap->getTable().'.'.$this->secondKey;
118
+
119
+		$query->join($this->parentMap->getTable(), $this->getQualifiedParentKeyName(), '=', $foreignKey);
120
+	}
121
+
122
+	/**
123
+	 * Set the constraints for an eager load of the relation.
124
+	 *
125
+	 * @param array $results
126
+	 *
127
+	 * @return void
128
+	 */
129
+	public function addEagerConstraints(array $results)
130
+	{
131
+		$table = $this->parentMap->getTable();
132
+
133
+		$this->query->whereIn($table.'.'.$this->firstKey, $this->getKeysFromResults($results));
134
+	}
135
+
136
+	/**
137
+	 * Match eagerly loaded relationship to a result set.
138
+	 *
139
+	 * @param array  $results
140
+	 * @param string $relation
141
+	 *
142
+	 * @return array
143
+	 */
144
+	public function match(array $results, $relation)
145
+	{
146
+		$entities = $this->getEager();
147
+
148
+		$dictionary = $this->buildDictionary($entities);
149
+
150
+		$relatedKey = $this->relatedMap->getKeyName();
151
+
152
+		$cache = $this->parentMapper->getEntityCache();
153
+
154
+		$host = $this;
155
+
156
+		// Once we have the dictionary we can simply spin through the parent entities to
157
+		// link them up with their children using the keyed dictionary to make the
158
+		// matching very convenient and easy work. Then we'll just return them.
159
+		return array_map(function ($result) use ($relation, $relatedKey, $dictionary, $cache, $host) {
160
+			$key = $result[$relatedKey];
161
+
162
+			if (isset($dictionary[$key])) {
163
+				$value = $host->relatedMap->newCollection($dictionary[$key]);
164
+
165
+				$result[$relation] = $value;
166
+
167
+				$cache->cacheLoadedRelationResult($key, $relation, $value, $this);
168
+			}
169
+
170
+			return $result;
171
+		}, $results);
172
+	}
173
+
174
+	/**
175
+	 * Build model dictionary keyed by the relation's foreign key.
176
+	 *
177
+	 * @param EntityCollection $results
178
+	 *
179
+	 * @return array
180
+	 */
181
+	protected function buildDictionary(EntityCollection $results)
182
+	{
183
+		$dictionary = [];
184
+
185
+		$foreign = $this->firstKey;
186
+
187
+		// First we will create a dictionary of entities keyed by the foreign key of the
188
+		// relationship as this will allow us to quickly access all of the related
189
+		// entities without having to do nested looping which will be quite slow.
190
+		foreach ($results as $result) {
191
+			$dictionary[$result->{$foreign}][] = $result;
192
+		}
193
+
194
+		return $dictionary;
195
+	}
196
+
197
+	/**
198
+	 * Get the results of the relationship.
199
+	 *
200
+	 * @param  $relation
201
+	 *
202
+	 * @return EntityCollection
203
+	 */
204
+	public function getResults($relation)
205
+	{
206
+		$results = $this->query->get();
207
+
208
+		$this->cacheRelation($results, $relation);
209
+
210
+		return $results;
211
+	}
212
+
213
+	/**
214
+	 * Execute the query as a "select" statement.
215
+	 *
216
+	 * @param array $columns
217
+	 *
218
+	 * @return EntityCollection
219
+	 */
220
+	public function get($columns = ['*'])
221
+	{
222
+		// First we'll add the proper select columns onto the query so it is run with
223
+		// the proper columns. Then, we will get the results and hydrate out pivot
224
+		// entities with the result of those columns as a separate model relation.
225
+		$select = $this->getSelectColumns($columns);
226
+
227
+		$entities = $this->query->addSelect($select)->getEntities();
228
+
229
+		// If we actually found entities we will also eager load any relationships that
230
+		// have been specified as needing to be eager loaded. This will solve the
231
+		// n + 1 query problem for the developer and also increase performance.
232
+		if (count($entities) > 0) {
233
+			$entities = $this->query->eagerLoadRelations($entities);
234
+		}
235
+
236
+		return $this->relatedMap->newCollection($entities);
237
+	}
238
+
239
+	/**
240
+	 * Set the select clause for the relation query.
241
+	 *
242
+	 * @param array $columns
243
+	 *
244
+	 * @return BelongsToMany
245
+	 */
246
+	protected function getSelectColumns(array $columns = ['*'])
247
+	{
248
+		if ($columns == ['*']) {
249
+			$columns = [$this->relatedMap->getTable().'.*'];
250
+		}
251
+
252
+		return array_merge($columns, [$this->parentMap->getTable().'.'.$this->firstKey]);
253
+	}
254
+
255
+	/**
256
+	 * Get a paginator for the "select" statement.
257
+	 *
258
+	 * @param int   $perPage
259
+	 * @param array $columns
260
+	 *
261
+	 * @return \Illuminate\Pagination\LengthAwarePaginator
262
+	 */
263
+	public function paginate($perPage = null, $columns = ['*'])
264
+	{
265
+		$this->query->addSelect($this->getSelectColumns($columns));
266
+
267
+		return $this->query->paginate($perPage, $columns);
268
+	}
269
+
270
+	/**
271
+	 * Get the key name of the parent model.
272
+	 *
273
+	 * @return string
274
+	 */
275
+	protected function getQualifiedParentKeyName()
276
+	{
277
+		return $this->parentMap->getQualifiedKeyName();
278
+	}
279
+
280
+	/**
281
+	 * Get the key for comparing against the parent key in "has" query.
282
+	 *
283
+	 * @return string
284
+	 */
285
+	public function getHasCompareKey()
286
+	{
287
+		return $this->farParentMap->getQualifiedKeyName();
288
+	}
289
+
290
+	/**
291
+	 * Run synchronization content if needed by the
292
+	 * relation type.
293
+	 *
294
+	 * @param array $actualContent
295
+	 *
296
+	 * @return void
297
+	 */
298
+	public function sync(array $entities)
299
+	{
300
+		// N/A
301
+	}
302 302
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -156,7 +156,7 @@
 block discarded – undo
156 156
         // Once we have the dictionary we can simply spin through the parent entities to
157 157
         // link them up with their children using the keyed dictionary to make the
158 158
         // matching very convenient and easy work. Then we'll just return them.
159
-        return array_map(function ($result) use ($relation, $relatedKey, $dictionary, $cache, $host) {
159
+        return array_map(function($result) use ($relation, $relatedKey, $dictionary, $cache, $host) {
160 160
             $key = $result[$relatedKey];
161 161
 
162 162
             if (isset($dictionary[$key])) {
Please login to merge, or discard this patch.
src/System/Manager.php 2 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
      * Create a mapper for a given entity.
144 144
      *
145 145
      * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
146
-     * @param mixed                                            $entityMap
146
+     * @param null|EntityMap                                            $entityMap
147 147
      *
148 148
      * @throws MappingException
149 149
      * @throws \InvalidArgumentException
@@ -251,7 +251,7 @@  discard block
 block discarded – undo
251 251
     /**
252 252
      * Check if the entity is already registered.
253 253
      *
254
-     * @param string|Entity $entity
254
+     * @param string $entity
255 255
      *
256 256
      * @return bool
257 257
      */
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
     /**
334 334
      * Register an entity.
335 335
      *
336
-     * @param string|\Analogue\ORM\Mappable $entity    entity's class name
336
+     * @param string $entity    entity's class name
337 337
      * @param string|EntityMap              $entityMap map's class name
338 338
      *
339 339
      * @throws MappingException
Please login to merge, or discard this patch.
Indentation   +689 added lines, -689 removed lines patch added patch discarded remove patch
@@ -21,693 +21,693 @@
 block discarded – undo
21 21
  */
22 22
 class Manager
23 23
 {
24
-    /**
25
-     * Manager instance.
26
-     *
27
-     * @var Manager
28
-     */
29
-    protected static $instance;
30
-
31
-    /**
32
-     * Driver Manager.
33
-     *
34
-     * @var \Analogue\ORM\Drivers\Manager
35
-     */
36
-    protected $drivers;
37
-
38
-    /**
39
-     * Registered entity classes and corresponding map objects.
40
-     *
41
-     * @var array
42
-     */
43
-    protected $entityClasses = [];
44
-
45
-    /**
46
-     * Key value store of ValueObject Classes and corresponding map classes.
47
-     *
48
-     * @var array|ValueMap[]
49
-     */
50
-    protected $valueClasses = [];
51
-
52
-    /**
53
-     * Morph map.
54
-     */
55
-    protected $morphMap = [];
56
-
57
-    /**
58
-     * Loaded Mappers.
59
-     *
60
-     * @var array
61
-     */
62
-    protected $mappers = [];
63
-
64
-    /**
65
-     * Loaded Repositories.
66
-     *
67
-     * @var array
68
-     */
69
-    protected $repositories = [];
70
-
71
-    /**
72
-     * Event dispatcher instance.
73
-     *
74
-     * @var \Illuminate\Contracts\Events\Dispatcher
75
-     */
76
-    protected $eventDispatcher;
77
-
78
-    /**
79
-     * Available Analogue Events.
80
-     *
81
-     * @var array
82
-     */
83
-    protected $events = [
84
-        'initializing',
85
-        'initialized',
86
-        'store',
87
-        'stored',
88
-        'creating',
89
-        'created',
90
-        'updating',
91
-        'updated',
92
-        'deleting',
93
-        'deleted',
94
-    ];
95
-
96
-    /**
97
-     * If strictMode is set to true, Manager will throw
98
-     * an exception if no entityMap class are registered
99
-     * for a given entity class.
100
-     *
101
-     * @var bool
102
-     */
103
-    protected $strictMode = true;
104
-
105
-    /**
106
-     * We can add namespaces in this array where the manager
107
-     * will look for when auto registering entityMaps.
108
-     *
109
-     * @var array
110
-     */
111
-    protected $customMapNamespaces = [];
112
-
113
-    /**
114
-     * @param \Analogue\ORM\Drivers\Manager $driverManager
115
-     * @param Dispatcher                    $event
116
-     */
117
-    public function __construct(DriverManager $driverManager, Dispatcher $event)
118
-    {
119
-        $this->drivers = $driverManager;
120
-
121
-        $this->eventDispatcher = $event;
122
-
123
-        static::$instance = $this;
124
-    }
125
-
126
-    /**
127
-     * Create a mapper for a given entity (static alias).
128
-     *
129
-     * @param \Analogue\ORM\Mappable|string $entity
130
-     * @param null|EntityMap                $entityMap
131
-     *
132
-     * @throws MappingException
133
-     * @throws \InvalidArgumentException
134
-     *
135
-     * @return Mapper
136
-     */
137
-    public static function getMapper($entity, $entityMap = null)
138
-    {
139
-        return static::$instance->mapper($entity, $entityMap);
140
-    }
141
-
142
-    /**
143
-     * Create a mapper for a given entity.
144
-     *
145
-     * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
146
-     * @param mixed                                            $entityMap
147
-     *
148
-     * @throws MappingException
149
-     * @throws \InvalidArgumentException
150
-     *
151
-     * @return Mapper
152
-     */
153
-    public function mapper($entity, $entityMap = null)
154
-    {
155
-        if ($entity instanceof Wrapper) {
156
-            throw new MappingException('Tried to instantiate mapper on wrapped Entity');
157
-        }
158
-
159
-        $entity = $this->resolveEntityClass($entity);
160
-
161
-        $entity = $this->getInverseMorphMap($entity);
162
-
163
-        // Return existing mapper instance if exists.
164
-        if (array_key_exists($entity, $this->mappers)) {
165
-            return $this->mappers[$entity];
166
-        } else {
167
-            return $this->buildMapper($entity, $entityMap);
168
-        }
169
-    }
170
-
171
-    /**
172
-     * This method resolve entity class from mappable instances or iterators.
173
-     *
174
-     * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
175
-     *
176
-     * @throws \InvalidArgumentException
177
-     *
178
-     * @return string
179
-     */
180
-    protected function resolveEntityClass($entity)
181
-    {
182
-        // We first check if the entity is traversable and we'll resolve
183
-        // the entity based on the first item of the object.
184
-        if ($this->isTraversable($entity)) {
185
-            if (!count($entity)) {
186
-                throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
187
-            }
188
-
189
-            $firstEntityItem = ($entity instanceof \Iterator)
190
-                ? $entity->current()
191
-                : current($entity);
192
-
193
-            return $this->resolveEntityClass($firstEntityItem);
194
-        }
195
-
196
-        if (is_object($entity)) {
197
-            return get_class($entity);
198
-        }
199
-
200
-        if (is_string($entity)) {
201
-            return $entity;
202
-        }
203
-
204
-        throw new \InvalidArgumentException('Invalid entity type');
205
-    }
206
-
207
-    /**
208
-     * @param string $key
209
-     *
210
-     * @return string
211
-     */
212
-    public function getInverseMorphMap($key)
213
-    {
214
-        return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
215
-    }
216
-
217
-    /**
218
-     * Build a new Mapper instance for a given Entity.
219
-     *
220
-     * @param string $entity
221
-     * @param        $entityMap
222
-     *
223
-     * @throws MappingException
224
-     *
225
-     * @return Mapper
226
-     */
227
-    protected function buildMapper($entity, $entityMap)
228
-    {
229
-        // If an EntityMap hasn't been manually registered by the user
230
-        // register it at runtime.
231
-        if (!$this->isRegisteredEntity($entity)) {
232
-            $this->register($entity, $entityMap);
233
-        }
234
-
235
-        $entityMap = $this->entityClasses[$entity];
236
-
237
-        $factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
238
-
239
-        $mapper = $factory->make($entity, $entityMap);
240
-
241
-        $this->mappers[$entity] = $mapper;
242
-
243
-        // At this point we can safely call the boot() method on the entityMap as
244
-        // the mapper is now instantiated & registered within the manager.
245
-
246
-        $mapper->getEntityMap()->boot();
247
-
248
-        return $mapper;
249
-    }
250
-
251
-    /**
252
-     * Check if the entity is already registered.
253
-     *
254
-     * @param string|Entity $entity
255
-     *
256
-     * @return bool
257
-     */
258
-    public function isRegisteredEntity($entity)
259
-    {
260
-        if (!is_string($entity)) {
261
-            $entity = get_class($entity);
262
-        }
263
-
264
-        return array_key_exists($entity, $this->entityClasses);
265
-    }
266
-
267
-    /**
268
-     * Return an array containing registered entities & entityMap instances.
269
-     *
270
-     * @return array
271
-     */
272
-    public function getRegisteredEntities()
273
-    {
274
-        return $this->entityClasses;
275
-    }
276
-
277
-    /**
278
-     * Check if a value class is already registered.
279
-     *
280
-     * @param string|sdtClass $object
281
-     *
282
-     * @return bool
283
-     */
284
-    public function isRegisteredValueObject($object)
285
-    {
286
-        if (!is_string($object)) {
287
-            $object = get_class($object);
288
-        }
289
-
290
-        return array_key_exists($object, $this->valueClasses);
291
-    }
292
-
293
-    /**
294
-     * Return true if an object is an array or iterator.
295
-     *
296
-     * @param mixed $argument
297
-     *
298
-     * @return bool
299
-     */
300
-    public function isTraversable($argument)
301
-    {
302
-        return $argument instanceof \Traversable || is_array($argument);
303
-    }
304
-
305
-    /**
306
-     * Set strict mode for entityMap instantiation.
307
-     *
308
-     * @param bool $mode
309
-     */
310
-    public function setStrictMode($mode)
311
-    {
312
-        $this->strictMode = $mode;
313
-    }
314
-
315
-    /**
316
-     * Register a namespace in where Analogue
317
-     * will scan for EntityMaps & ValueMaps.
318
-     *
319
-     * @param string $namespace
320
-     *
321
-     * @return void
322
-     */
323
-    public function registerMapNamespace($namespace)
324
-    {
325
-        // Add a trailing antislash to namespace if not present
326
-        if (substr('testers', -1) != '\\') {
327
-            $namespace = $namespace.'\\';
328
-        }
329
-
330
-        $this->customMapNamespaces[] = $namespace;
331
-    }
332
-
333
-    /**
334
-     * Register an entity.
335
-     *
336
-     * @param string|\Analogue\ORM\Mappable $entity    entity's class name
337
-     * @param string|EntityMap              $entityMap map's class name
338
-     *
339
-     * @throws MappingException
340
-     *
341
-     * @return void
342
-     */
343
-    public function register($entity, $entityMap = null)
344
-    {
345
-        // If an object is provider, get the class name from it
346
-        if (!is_string($entity)) {
347
-            $entity = get_class($entity);
348
-        }
349
-
350
-        if ($this->isRegisteredEntity($entity)) {
351
-            throw new MappingException("Entity $entity is already registered.");
352
-        }
353
-
354
-        if (!class_exists($entity)) {
355
-            throw new MappingException("Class $entity does not exists");
356
-        }
357
-
358
-        if ($entityMap === null) {
359
-            $entityMap = $this->getEntityMapInstanceFor($entity);
360
-        }
361
-
362
-        if (is_string($entityMap)) {
363
-            $entityMap = new $entityMap();
364
-        }
365
-
366
-        if (!$entityMap instanceof EntityMap) {
367
-            throw new MappingException(get_class($entityMap).' must be an instance of EntityMap.');
368
-        }
369
-
370
-        $entityMap->setClass($entity);
371
-
372
-        $this->entityClasses[$entity] = $entityMap;
373
-    }
374
-
375
-    /**
376
-     * Get the entity map instance for a custom entity.
377
-     *
378
-     * @param string $entity
379
-     *
380
-     * @return \Analogue\ORM\EntityMap
381
-     */
382
-    protected function getEntityMapInstanceFor($entity)
383
-    {
384
-        if (class_exists($entity.'Map')) {
385
-            $map = $entity.'Map';
386
-            $map = new $map();
387
-
388
-            return $map;
389
-        }
390
-
391
-        if ($map = $this->getMapFromNamespaces($entity)) {
392
-            return $map;
393
-        }
394
-
395
-        if ($this->strictMode) {
396
-            throw new EntityMapNotFoundException("No Map registered for $entity");
397
-        }
398
-
399
-        $map = $this->getNewEntityMap();
400
-
401
-        return $map;
402
-    }
403
-
404
-    /**
405
-     * Scan through registered custom namespace
406
-     * for an Entity/ValueMap.
407
-     *
408
-     * @param string $class
409
-     *
410
-     * @return ValueMap|EntityMap|bool
411
-     */
412
-    protected function getMapFromNamespaces($class)
413
-    {
414
-        foreach ($this->customMapNamespaces as $namespace) {
415
-            if ($map = $this->findMapInNamespace($class, $namespace)) {
416
-                return $map;
417
-            }
418
-        }
419
-
420
-        return false;
421
-    }
422
-
423
-    /**
424
-     * Look in a custom namespace for an Entity/ValueMap.
425
-     *
426
-     * @param string $class
427
-     * @param string $namespace
428
-     *
429
-     * @return ValueMap|EntityMap|bool
430
-     */
431
-    protected function findMapInNamespace($class, $namespace)
432
-    {
433
-        $parts = explode('\\', $class);
434
-
435
-        $baseClass = $parts[count($parts) - 1];
436
-
437
-        $expectedClass = $namespace.$baseClass.'Map';
438
-
439
-        if (class_exists($expectedClass)) {
440
-            return new $expectedClass();
441
-        }
442
-
443
-        return false;
444
-    }
445
-
446
-    /**
447
-     * Dynamically create an entity map for a custom entity class.
448
-     *
449
-     * @return EntityMap
450
-     */
451
-    protected function getNewEntityMap()
452
-    {
453
-        return new EntityMap();
454
-    }
455
-
456
-    /**
457
-     * Return the Singleton instance of the manager.
458
-     *
459
-     * @return Manager
460
-     */
461
-    public static function getInstance()
462
-    {
463
-        return static::$instance;
464
-    }
465
-
466
-    /**
467
-     * Return the Driver Manager's instance.
468
-     *
469
-     * @return \Analogue\ORM\Drivers\Manager
470
-     */
471
-    public function getDriverManager()
472
-    {
473
-        return $this->drivers;
474
-    }
475
-
476
-    /**
477
-     * Get the Repository instance for the given Entity.
478
-     *
479
-     * @param \Analogue\ORM\Mappable|string $entity
480
-     *
481
-     * @throws \InvalidArgumentException
482
-     * @throws MappingException
483
-     *
484
-     * @return \Analogue\ORM\Repository
485
-     */
486
-    public function repository($entity)
487
-    {
488
-        if (!is_string($entity)) {
489
-            $entity = get_class($entity);
490
-        }
491
-
492
-        // First we check if the repository is not already created.
493
-        if (array_key_exists($entity, $this->repositories)) {
494
-            return $this->repositories[$entity];
495
-        }
496
-
497
-        $this->repositories[$entity] = new Repository($this->mapper($entity));
498
-
499
-        return $this->repositories[$entity];
500
-    }
501
-
502
-    /**
503
-     * Return true is the object is registered as value object.
504
-     *
505
-     * @param mixed $object
506
-     *
507
-     * @return bool
508
-     */
509
-    public function isValueObject($object)
510
-    {
511
-        if (!is_string($object)) {
512
-            $object = get_class($object);
513
-        }
514
-
515
-        return array_key_exists($object, $this->valueClasses);
516
-    }
517
-
518
-    /**
519
-     * Get the Value Map for a given Value Object Class.
520
-     *
521
-     * @param string $valueObject
522
-     *
523
-     * @throws MappingException
524
-     *
525
-     * @return \Analogue\ORM\ValueMap
526
-     */
527
-    public function getValueMap($valueObject)
528
-    {
529
-        if (!is_string($valueObject)) {
530
-            $valueObject = get_class($valueObject);
531
-        }
532
-
533
-        if (!array_key_exists($valueObject, $this->valueClasses)) {
534
-            $this->registerValueObject($valueObject);
535
-        }
536
-
537
-        /** @var ValueMap $valueMap */
538
-        $valueMap = new $this->valueClasses[$valueObject]();
539
-
540
-        $valueMap->setClass($valueObject);
541
-
542
-        return $valueMap;
543
-    }
544
-
545
-    /**
546
-     * Register a Value Object.
547
-     *
548
-     * @param string $valueObject
549
-     * @param string $valueMap
550
-     *
551
-     * @throws MappingException
552
-     *
553
-     * @return void
554
-     */
555
-    public function registerValueObject($valueObject, $valueMap = null)
556
-    {
557
-        if (!is_string($valueObject)) {
558
-            $valueObject = get_class($valueObject);
559
-        }
560
-
561
-        if ($valueMap === null) {
562
-
563
-            // First, we'll look into registered namespaces for Entity Maps,
564
-            // if any. Then we'll fallback to the same namespace of the object
565
-            if (!$valueMap = $this->getMapFromNamespaces($valueObject)) {
566
-                $valueMap = $valueObject.'Map';
567
-            } else {
568
-                $valueMap = get_class($valueMap);
569
-            }
570
-        }
571
-
572
-        if (!class_exists($valueMap)) {
573
-            throw new MappingException("$valueMap doesn't exists");
574
-        }
575
-
576
-        $this->valueClasses[$valueObject] = $valueMap;
577
-    }
578
-
579
-    /**
580
-     * Instantiate a new Value Object instance.
581
-     *
582
-     * @param string $valueObject
583
-     *
584
-     * @return \Analogue\ORM\ValueObject
585
-     */
586
-    public function getValueObjectInstance($valueObject)
587
-    {
588
-        $prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
589
-
590
-        return $prototype;
591
-    }
592
-
593
-    /**
594
-     * Register Analogue Plugin.
595
-     *
596
-     * @param string $plugin class
597
-     *
598
-     * @return void
599
-     */
600
-    public function registerPlugin($plugin)
601
-    {
602
-        /** @var AnaloguePluginInterface $plugin */
603
-        $plugin = new $plugin($this);
604
-
605
-        $this->events = array_merge($this->events, $plugin->getCustomEvents());
606
-
607
-        $plugin->register();
608
-    }
609
-
610
-    /**
611
-     * Register event listeners that will be fired regardless the type
612
-     * of the entity.
613
-     *
614
-     * @param string   $event
615
-     * @param \Closure $callback
616
-     *
617
-     * @throws \LogicException
618
-     *
619
-     * @return void
620
-     */
621
-    public function registerGlobalEvent($event, $callback)
622
-    {
623
-        if (!in_array($event, $this->events, false)) {
624
-            throw new \LogicException("Analogue : Event $event doesn't exist");
625
-        }
626
-
627
-        $this->eventDispatcher->listen("analogue.{$event}.*", $callback);
628
-    }
629
-
630
-    /**
631
-     * Shortcut to Mapper store.
632
-     *
633
-     * @param mixed $entity
634
-     *
635
-     * @throws MappingException
636
-     * @throws \InvalidArgumentException
637
-     *
638
-     * @return mixed
639
-     */
640
-    public function store($entity)
641
-    {
642
-        return $this->mapper($entity)->store($entity);
643
-    }
644
-
645
-    /**
646
-     * Shortcut to Mapper delete.
647
-     *
648
-     * @param mixed $entity
649
-     *
650
-     * @throws MappingException
651
-     * @throws \InvalidArgumentException
652
-     *
653
-     * @return \Illuminate\Support\Collection|null
654
-     */
655
-    public function delete($entity)
656
-    {
657
-        return $this->mapper($entity)->delete($entity);
658
-    }
659
-
660
-    /**
661
-     * Shortcut to Mapper query.
662
-     *
663
-     * @param mixed $entity
664
-     *
665
-     * @throws MappingException
666
-     * @throws \InvalidArgumentException
667
-     *
668
-     * @return Query
669
-     */
670
-    public function query($entity)
671
-    {
672
-        return $this->mapper($entity)->query();
673
-    }
674
-
675
-    /**
676
-     * Shortcut to Mapper Global Query.
677
-     *
678
-     * @param mixed $entity
679
-     *
680
-     * @throws MappingException
681
-     * @throws \InvalidArgumentException
682
-     *
683
-     * @return Query
684
-     */
685
-    public function globalQuery($entity)
686
-    {
687
-        return $this->mapper($entity)->globalQuery();
688
-    }
689
-
690
-    /**
691
-     * @param array $morphMap
692
-     *
693
-     * @return $this
694
-     */
695
-    public function morphMap(array $morphMap)
696
-    {
697
-        $this->morphMap = $morphMap;
698
-
699
-        return $this;
700
-    }
701
-
702
-    /**
703
-     * @param string $class
704
-     *
705
-     * @return mixed
706
-     */
707
-    public function getMorphMap($class)
708
-    {
709
-        $key = array_search($class, $this->morphMap, false);
710
-
711
-        return $key !== false ? $key : $class;
712
-    }
24
+	/**
25
+	 * Manager instance.
26
+	 *
27
+	 * @var Manager
28
+	 */
29
+	protected static $instance;
30
+
31
+	/**
32
+	 * Driver Manager.
33
+	 *
34
+	 * @var \Analogue\ORM\Drivers\Manager
35
+	 */
36
+	protected $drivers;
37
+
38
+	/**
39
+	 * Registered entity classes and corresponding map objects.
40
+	 *
41
+	 * @var array
42
+	 */
43
+	protected $entityClasses = [];
44
+
45
+	/**
46
+	 * Key value store of ValueObject Classes and corresponding map classes.
47
+	 *
48
+	 * @var array|ValueMap[]
49
+	 */
50
+	protected $valueClasses = [];
51
+
52
+	/**
53
+	 * Morph map.
54
+	 */
55
+	protected $morphMap = [];
56
+
57
+	/**
58
+	 * Loaded Mappers.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $mappers = [];
63
+
64
+	/**
65
+	 * Loaded Repositories.
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $repositories = [];
70
+
71
+	/**
72
+	 * Event dispatcher instance.
73
+	 *
74
+	 * @var \Illuminate\Contracts\Events\Dispatcher
75
+	 */
76
+	protected $eventDispatcher;
77
+
78
+	/**
79
+	 * Available Analogue Events.
80
+	 *
81
+	 * @var array
82
+	 */
83
+	protected $events = [
84
+		'initializing',
85
+		'initialized',
86
+		'store',
87
+		'stored',
88
+		'creating',
89
+		'created',
90
+		'updating',
91
+		'updated',
92
+		'deleting',
93
+		'deleted',
94
+	];
95
+
96
+	/**
97
+	 * If strictMode is set to true, Manager will throw
98
+	 * an exception if no entityMap class are registered
99
+	 * for a given entity class.
100
+	 *
101
+	 * @var bool
102
+	 */
103
+	protected $strictMode = true;
104
+
105
+	/**
106
+	 * We can add namespaces in this array where the manager
107
+	 * will look for when auto registering entityMaps.
108
+	 *
109
+	 * @var array
110
+	 */
111
+	protected $customMapNamespaces = [];
112
+
113
+	/**
114
+	 * @param \Analogue\ORM\Drivers\Manager $driverManager
115
+	 * @param Dispatcher                    $event
116
+	 */
117
+	public function __construct(DriverManager $driverManager, Dispatcher $event)
118
+	{
119
+		$this->drivers = $driverManager;
120
+
121
+		$this->eventDispatcher = $event;
122
+
123
+		static::$instance = $this;
124
+	}
125
+
126
+	/**
127
+	 * Create a mapper for a given entity (static alias).
128
+	 *
129
+	 * @param \Analogue\ORM\Mappable|string $entity
130
+	 * @param null|EntityMap                $entityMap
131
+	 *
132
+	 * @throws MappingException
133
+	 * @throws \InvalidArgumentException
134
+	 *
135
+	 * @return Mapper
136
+	 */
137
+	public static function getMapper($entity, $entityMap = null)
138
+	{
139
+		return static::$instance->mapper($entity, $entityMap);
140
+	}
141
+
142
+	/**
143
+	 * Create a mapper for a given entity.
144
+	 *
145
+	 * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
146
+	 * @param mixed                                            $entityMap
147
+	 *
148
+	 * @throws MappingException
149
+	 * @throws \InvalidArgumentException
150
+	 *
151
+	 * @return Mapper
152
+	 */
153
+	public function mapper($entity, $entityMap = null)
154
+	{
155
+		if ($entity instanceof Wrapper) {
156
+			throw new MappingException('Tried to instantiate mapper on wrapped Entity');
157
+		}
158
+
159
+		$entity = $this->resolveEntityClass($entity);
160
+
161
+		$entity = $this->getInverseMorphMap($entity);
162
+
163
+		// Return existing mapper instance if exists.
164
+		if (array_key_exists($entity, $this->mappers)) {
165
+			return $this->mappers[$entity];
166
+		} else {
167
+			return $this->buildMapper($entity, $entityMap);
168
+		}
169
+	}
170
+
171
+	/**
172
+	 * This method resolve entity class from mappable instances or iterators.
173
+	 *
174
+	 * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
175
+	 *
176
+	 * @throws \InvalidArgumentException
177
+	 *
178
+	 * @return string
179
+	 */
180
+	protected function resolveEntityClass($entity)
181
+	{
182
+		// We first check if the entity is traversable and we'll resolve
183
+		// the entity based on the first item of the object.
184
+		if ($this->isTraversable($entity)) {
185
+			if (!count($entity)) {
186
+				throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
187
+			}
188
+
189
+			$firstEntityItem = ($entity instanceof \Iterator)
190
+				? $entity->current()
191
+				: current($entity);
192
+
193
+			return $this->resolveEntityClass($firstEntityItem);
194
+		}
195
+
196
+		if (is_object($entity)) {
197
+			return get_class($entity);
198
+		}
199
+
200
+		if (is_string($entity)) {
201
+			return $entity;
202
+		}
203
+
204
+		throw new \InvalidArgumentException('Invalid entity type');
205
+	}
206
+
207
+	/**
208
+	 * @param string $key
209
+	 *
210
+	 * @return string
211
+	 */
212
+	public function getInverseMorphMap($key)
213
+	{
214
+		return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
215
+	}
216
+
217
+	/**
218
+	 * Build a new Mapper instance for a given Entity.
219
+	 *
220
+	 * @param string $entity
221
+	 * @param        $entityMap
222
+	 *
223
+	 * @throws MappingException
224
+	 *
225
+	 * @return Mapper
226
+	 */
227
+	protected function buildMapper($entity, $entityMap)
228
+	{
229
+		// If an EntityMap hasn't been manually registered by the user
230
+		// register it at runtime.
231
+		if (!$this->isRegisteredEntity($entity)) {
232
+			$this->register($entity, $entityMap);
233
+		}
234
+
235
+		$entityMap = $this->entityClasses[$entity];
236
+
237
+		$factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
238
+
239
+		$mapper = $factory->make($entity, $entityMap);
240
+
241
+		$this->mappers[$entity] = $mapper;
242
+
243
+		// At this point we can safely call the boot() method on the entityMap as
244
+		// the mapper is now instantiated & registered within the manager.
245
+
246
+		$mapper->getEntityMap()->boot();
247
+
248
+		return $mapper;
249
+	}
250
+
251
+	/**
252
+	 * Check if the entity is already registered.
253
+	 *
254
+	 * @param string|Entity $entity
255
+	 *
256
+	 * @return bool
257
+	 */
258
+	public function isRegisteredEntity($entity)
259
+	{
260
+		if (!is_string($entity)) {
261
+			$entity = get_class($entity);
262
+		}
263
+
264
+		return array_key_exists($entity, $this->entityClasses);
265
+	}
266
+
267
+	/**
268
+	 * Return an array containing registered entities & entityMap instances.
269
+	 *
270
+	 * @return array
271
+	 */
272
+	public function getRegisteredEntities()
273
+	{
274
+		return $this->entityClasses;
275
+	}
276
+
277
+	/**
278
+	 * Check if a value class is already registered.
279
+	 *
280
+	 * @param string|sdtClass $object
281
+	 *
282
+	 * @return bool
283
+	 */
284
+	public function isRegisteredValueObject($object)
285
+	{
286
+		if (!is_string($object)) {
287
+			$object = get_class($object);
288
+		}
289
+
290
+		return array_key_exists($object, $this->valueClasses);
291
+	}
292
+
293
+	/**
294
+	 * Return true if an object is an array or iterator.
295
+	 *
296
+	 * @param mixed $argument
297
+	 *
298
+	 * @return bool
299
+	 */
300
+	public function isTraversable($argument)
301
+	{
302
+		return $argument instanceof \Traversable || is_array($argument);
303
+	}
304
+
305
+	/**
306
+	 * Set strict mode for entityMap instantiation.
307
+	 *
308
+	 * @param bool $mode
309
+	 */
310
+	public function setStrictMode($mode)
311
+	{
312
+		$this->strictMode = $mode;
313
+	}
314
+
315
+	/**
316
+	 * Register a namespace in where Analogue
317
+	 * will scan for EntityMaps & ValueMaps.
318
+	 *
319
+	 * @param string $namespace
320
+	 *
321
+	 * @return void
322
+	 */
323
+	public function registerMapNamespace($namespace)
324
+	{
325
+		// Add a trailing antislash to namespace if not present
326
+		if (substr('testers', -1) != '\\') {
327
+			$namespace = $namespace.'\\';
328
+		}
329
+
330
+		$this->customMapNamespaces[] = $namespace;
331
+	}
332
+
333
+	/**
334
+	 * Register an entity.
335
+	 *
336
+	 * @param string|\Analogue\ORM\Mappable $entity    entity's class name
337
+	 * @param string|EntityMap              $entityMap map's class name
338
+	 *
339
+	 * @throws MappingException
340
+	 *
341
+	 * @return void
342
+	 */
343
+	public function register($entity, $entityMap = null)
344
+	{
345
+		// If an object is provider, get the class name from it
346
+		if (!is_string($entity)) {
347
+			$entity = get_class($entity);
348
+		}
349
+
350
+		if ($this->isRegisteredEntity($entity)) {
351
+			throw new MappingException("Entity $entity is already registered.");
352
+		}
353
+
354
+		if (!class_exists($entity)) {
355
+			throw new MappingException("Class $entity does not exists");
356
+		}
357
+
358
+		if ($entityMap === null) {
359
+			$entityMap = $this->getEntityMapInstanceFor($entity);
360
+		}
361
+
362
+		if (is_string($entityMap)) {
363
+			$entityMap = new $entityMap();
364
+		}
365
+
366
+		if (!$entityMap instanceof EntityMap) {
367
+			throw new MappingException(get_class($entityMap).' must be an instance of EntityMap.');
368
+		}
369
+
370
+		$entityMap->setClass($entity);
371
+
372
+		$this->entityClasses[$entity] = $entityMap;
373
+	}
374
+
375
+	/**
376
+	 * Get the entity map instance for a custom entity.
377
+	 *
378
+	 * @param string $entity
379
+	 *
380
+	 * @return \Analogue\ORM\EntityMap
381
+	 */
382
+	protected function getEntityMapInstanceFor($entity)
383
+	{
384
+		if (class_exists($entity.'Map')) {
385
+			$map = $entity.'Map';
386
+			$map = new $map();
387
+
388
+			return $map;
389
+		}
390
+
391
+		if ($map = $this->getMapFromNamespaces($entity)) {
392
+			return $map;
393
+		}
394
+
395
+		if ($this->strictMode) {
396
+			throw new EntityMapNotFoundException("No Map registered for $entity");
397
+		}
398
+
399
+		$map = $this->getNewEntityMap();
400
+
401
+		return $map;
402
+	}
403
+
404
+	/**
405
+	 * Scan through registered custom namespace
406
+	 * for an Entity/ValueMap.
407
+	 *
408
+	 * @param string $class
409
+	 *
410
+	 * @return ValueMap|EntityMap|bool
411
+	 */
412
+	protected function getMapFromNamespaces($class)
413
+	{
414
+		foreach ($this->customMapNamespaces as $namespace) {
415
+			if ($map = $this->findMapInNamespace($class, $namespace)) {
416
+				return $map;
417
+			}
418
+		}
419
+
420
+		return false;
421
+	}
422
+
423
+	/**
424
+	 * Look in a custom namespace for an Entity/ValueMap.
425
+	 *
426
+	 * @param string $class
427
+	 * @param string $namespace
428
+	 *
429
+	 * @return ValueMap|EntityMap|bool
430
+	 */
431
+	protected function findMapInNamespace($class, $namespace)
432
+	{
433
+		$parts = explode('\\', $class);
434
+
435
+		$baseClass = $parts[count($parts) - 1];
436
+
437
+		$expectedClass = $namespace.$baseClass.'Map';
438
+
439
+		if (class_exists($expectedClass)) {
440
+			return new $expectedClass();
441
+		}
442
+
443
+		return false;
444
+	}
445
+
446
+	/**
447
+	 * Dynamically create an entity map for a custom entity class.
448
+	 *
449
+	 * @return EntityMap
450
+	 */
451
+	protected function getNewEntityMap()
452
+	{
453
+		return new EntityMap();
454
+	}
455
+
456
+	/**
457
+	 * Return the Singleton instance of the manager.
458
+	 *
459
+	 * @return Manager
460
+	 */
461
+	public static function getInstance()
462
+	{
463
+		return static::$instance;
464
+	}
465
+
466
+	/**
467
+	 * Return the Driver Manager's instance.
468
+	 *
469
+	 * @return \Analogue\ORM\Drivers\Manager
470
+	 */
471
+	public function getDriverManager()
472
+	{
473
+		return $this->drivers;
474
+	}
475
+
476
+	/**
477
+	 * Get the Repository instance for the given Entity.
478
+	 *
479
+	 * @param \Analogue\ORM\Mappable|string $entity
480
+	 *
481
+	 * @throws \InvalidArgumentException
482
+	 * @throws MappingException
483
+	 *
484
+	 * @return \Analogue\ORM\Repository
485
+	 */
486
+	public function repository($entity)
487
+	{
488
+		if (!is_string($entity)) {
489
+			$entity = get_class($entity);
490
+		}
491
+
492
+		// First we check if the repository is not already created.
493
+		if (array_key_exists($entity, $this->repositories)) {
494
+			return $this->repositories[$entity];
495
+		}
496
+
497
+		$this->repositories[$entity] = new Repository($this->mapper($entity));
498
+
499
+		return $this->repositories[$entity];
500
+	}
501
+
502
+	/**
503
+	 * Return true is the object is registered as value object.
504
+	 *
505
+	 * @param mixed $object
506
+	 *
507
+	 * @return bool
508
+	 */
509
+	public function isValueObject($object)
510
+	{
511
+		if (!is_string($object)) {
512
+			$object = get_class($object);
513
+		}
514
+
515
+		return array_key_exists($object, $this->valueClasses);
516
+	}
517
+
518
+	/**
519
+	 * Get the Value Map for a given Value Object Class.
520
+	 *
521
+	 * @param string $valueObject
522
+	 *
523
+	 * @throws MappingException
524
+	 *
525
+	 * @return \Analogue\ORM\ValueMap
526
+	 */
527
+	public function getValueMap($valueObject)
528
+	{
529
+		if (!is_string($valueObject)) {
530
+			$valueObject = get_class($valueObject);
531
+		}
532
+
533
+		if (!array_key_exists($valueObject, $this->valueClasses)) {
534
+			$this->registerValueObject($valueObject);
535
+		}
536
+
537
+		/** @var ValueMap $valueMap */
538
+		$valueMap = new $this->valueClasses[$valueObject]();
539
+
540
+		$valueMap->setClass($valueObject);
541
+
542
+		return $valueMap;
543
+	}
544
+
545
+	/**
546
+	 * Register a Value Object.
547
+	 *
548
+	 * @param string $valueObject
549
+	 * @param string $valueMap
550
+	 *
551
+	 * @throws MappingException
552
+	 *
553
+	 * @return void
554
+	 */
555
+	public function registerValueObject($valueObject, $valueMap = null)
556
+	{
557
+		if (!is_string($valueObject)) {
558
+			$valueObject = get_class($valueObject);
559
+		}
560
+
561
+		if ($valueMap === null) {
562
+
563
+			// First, we'll look into registered namespaces for Entity Maps,
564
+			// if any. Then we'll fallback to the same namespace of the object
565
+			if (!$valueMap = $this->getMapFromNamespaces($valueObject)) {
566
+				$valueMap = $valueObject.'Map';
567
+			} else {
568
+				$valueMap = get_class($valueMap);
569
+			}
570
+		}
571
+
572
+		if (!class_exists($valueMap)) {
573
+			throw new MappingException("$valueMap doesn't exists");
574
+		}
575
+
576
+		$this->valueClasses[$valueObject] = $valueMap;
577
+	}
578
+
579
+	/**
580
+	 * Instantiate a new Value Object instance.
581
+	 *
582
+	 * @param string $valueObject
583
+	 *
584
+	 * @return \Analogue\ORM\ValueObject
585
+	 */
586
+	public function getValueObjectInstance($valueObject)
587
+	{
588
+		$prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
589
+
590
+		return $prototype;
591
+	}
592
+
593
+	/**
594
+	 * Register Analogue Plugin.
595
+	 *
596
+	 * @param string $plugin class
597
+	 *
598
+	 * @return void
599
+	 */
600
+	public function registerPlugin($plugin)
601
+	{
602
+		/** @var AnaloguePluginInterface $plugin */
603
+		$plugin = new $plugin($this);
604
+
605
+		$this->events = array_merge($this->events, $plugin->getCustomEvents());
606
+
607
+		$plugin->register();
608
+	}
609
+
610
+	/**
611
+	 * Register event listeners that will be fired regardless the type
612
+	 * of the entity.
613
+	 *
614
+	 * @param string   $event
615
+	 * @param \Closure $callback
616
+	 *
617
+	 * @throws \LogicException
618
+	 *
619
+	 * @return void
620
+	 */
621
+	public function registerGlobalEvent($event, $callback)
622
+	{
623
+		if (!in_array($event, $this->events, false)) {
624
+			throw new \LogicException("Analogue : Event $event doesn't exist");
625
+		}
626
+
627
+		$this->eventDispatcher->listen("analogue.{$event}.*", $callback);
628
+	}
629
+
630
+	/**
631
+	 * Shortcut to Mapper store.
632
+	 *
633
+	 * @param mixed $entity
634
+	 *
635
+	 * @throws MappingException
636
+	 * @throws \InvalidArgumentException
637
+	 *
638
+	 * @return mixed
639
+	 */
640
+	public function store($entity)
641
+	{
642
+		return $this->mapper($entity)->store($entity);
643
+	}
644
+
645
+	/**
646
+	 * Shortcut to Mapper delete.
647
+	 *
648
+	 * @param mixed $entity
649
+	 *
650
+	 * @throws MappingException
651
+	 * @throws \InvalidArgumentException
652
+	 *
653
+	 * @return \Illuminate\Support\Collection|null
654
+	 */
655
+	public function delete($entity)
656
+	{
657
+		return $this->mapper($entity)->delete($entity);
658
+	}
659
+
660
+	/**
661
+	 * Shortcut to Mapper query.
662
+	 *
663
+	 * @param mixed $entity
664
+	 *
665
+	 * @throws MappingException
666
+	 * @throws \InvalidArgumentException
667
+	 *
668
+	 * @return Query
669
+	 */
670
+	public function query($entity)
671
+	{
672
+		return $this->mapper($entity)->query();
673
+	}
674
+
675
+	/**
676
+	 * Shortcut to Mapper Global Query.
677
+	 *
678
+	 * @param mixed $entity
679
+	 *
680
+	 * @throws MappingException
681
+	 * @throws \InvalidArgumentException
682
+	 *
683
+	 * @return Query
684
+	 */
685
+	public function globalQuery($entity)
686
+	{
687
+		return $this->mapper($entity)->globalQuery();
688
+	}
689
+
690
+	/**
691
+	 * @param array $morphMap
692
+	 *
693
+	 * @return $this
694
+	 */
695
+	public function morphMap(array $morphMap)
696
+	{
697
+		$this->morphMap = $morphMap;
698
+
699
+		return $this;
700
+	}
701
+
702
+	/**
703
+	 * @param string $class
704
+	 *
705
+	 * @return mixed
706
+	 */
707
+	public function getMorphMap($class)
708
+	{
709
+		$key = array_search($class, $this->morphMap, false);
710
+
711
+		return $key !== false ? $key : $class;
712
+	}
713 713
 }
Please login to merge, or discard this patch.
src/System/ResultBuilder.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -38,7 +38,6 @@
 block discarded – undo
38 38
 
39 39
     /**
40 40
      * @param Mapper $defaultMapper
41
-     * @param array  $eagerLoads
42 41
      */
43 42
     public function __construct(Mapper $defaultMapper)
44 43
     {
Please login to merge, or discard this patch.
Indentation   +330 added lines, -330 removed lines patch added patch discarded remove patch
@@ -7,334 +7,334 @@
 block discarded – undo
7 7
 
8 8
 class ResultBuilder
9 9
 {
10
-    /**
11
-     * The default mapper used to build entities with.
12
-     *
13
-     * @var \Analogue\ORM\System\Mapper
14
-     */
15
-    protected $defaultMapper;
16
-
17
-    /**
18
-     * Relations that will be eager loaded on this query.
19
-     *
20
-     * @var array
21
-     */
22
-    protected $eagerLoads;
23
-
24
-    /**
25
-     * The Entity Map for the entity to build.
26
-     *
27
-     * @var \Analogue\ORM\EntityMap
28
-     */
29
-    protected $entityMap;
30
-
31
-    /**
32
-     * An array of builders used by this class to build necessary
33
-     * entities for each result type.
34
-     *
35
-     * @var array
36
-     */
37
-    protected $builders = [];
38
-
39
-    /**
40
-     * @param Mapper $defaultMapper
41
-     * @param array  $eagerLoads
42
-     */
43
-    public function __construct(Mapper $defaultMapper)
44
-    {
45
-        $this->defaultMapper = $defaultMapper;
46
-        $this->entityMap = $defaultMapper->getEntityMap();
47
-    }
48
-
49
-    /**
50
-     * Convert a result set into an array of entities.
51
-     *
52
-     * @param array $results
53
-     * @param array $eagerLoads name of the relation to be eager loaded on the Entities
54
-     *
55
-     * @return \Illuminate\Support\Collection
56
-     */
57
-    public function build(array $results, array $eagerLoads)
58
-    {
59
-        // First, we'll cast every single result to array
60
-        $results = array_map(function ($item) {
61
-            return (array) $item;
62
-        }, $results);
63
-
64
-        // Then, we'll cache every single results as raw attributes, before
65
-        // adding relationships, which will be cached when the relationship's
66
-        // query takes place.
67
-        $this->defaultMapper->getEntityCache()->add($results);
68
-
69
-        // Launch the queries related to eager loads, and match the
70
-        // current result set to these loaded relationships.
71
-        $results = $this->queryEagerLoadedRelationships($results, $eagerLoads);
72
-
73
-        // Note : Maybe we could use a PolymorphicResultBuilder, which would
74
-        // be shared by both STI and polymorphic relations, as they share the
75
-        // same process.
76
-
77
-        switch ($this->entityMap->getInheritanceType()) {
78
-            case 'single_table':
79
-                return $this->buildUsingSingleTableInheritance($results);
80
-                break;
81
-
82
-            default:
83
-                return $this->buildWithDefaultMapper($results);
84
-                break;
85
-        }
86
-    }
87
-
88
-    /**
89
-     * Launch queries on eager loaded relationships.
90
-     *
91
-     * @return array
92
-     */
93
-    protected function queryEagerLoadedRelationships(array $results, array $eagerLoads)
94
-    {
95
-        $this->eagerLoads = $this->parseRelations($eagerLoads);
96
-
97
-        return $this->eagerLoadRelations($results);
98
-    }
99
-
100
-    /**
101
-     * Parse a list of relations into individuals.
102
-     *
103
-     * @param array $relations
104
-     *
105
-     * @return array
106
-     */
107
-    protected function parseRelations(array $relations)
108
-    {
109
-        $results = [];
110
-
111
-        foreach ($relations as $name => $constraints) {
112
-            // If the "relation" value is actually a numeric key, we can assume that no
113
-            // constraints have been specified for the eager load and we'll just put
114
-            // an empty Closure with the loader so that we can treat all the same.
115
-            if (is_numeric($name)) {
116
-                $f = function () {
117
-                };
118
-
119
-                list($name, $constraints) = [$constraints, $f];
120
-            }
121
-
122
-            // We need to separate out any nested includes. Which allows the developers
123
-            // to load deep relationships using "dots" without stating each level of
124
-            // the relationship with its own key in the array of eager load names.
125
-            $results = $this->parseNested($name, $results);
126
-
127
-            $results[$name] = $constraints;
128
-        }
129
-
130
-        return $results;
131
-    }
132
-
133
-    /**
134
-     * Parse the nested relationships in a relation.
135
-     *
136
-     * @param string $name
137
-     * @param array  $results
138
-     *
139
-     * @return array
140
-     */
141
-    protected function parseNested($name, $results)
142
-    {
143
-        $progress = [];
144
-
145
-        // If the relation has already been set on the result array, we will not set it
146
-        // again, since that would override any constraints that were already placed
147
-        // on the relationships. We will only set the ones that are not specified.
148
-        foreach (explode('.', $name) as $segment) {
149
-            $progress[] = $segment;
150
-
151
-            if (!isset($results[$last = implode('.', $progress)])) {
152
-                $results[$last] = function () {
153
-                };
154
-            }
155
-        }
156
-
157
-        return $results;
158
-    }
159
-
160
-    /**
161
-     * Eager load the relationships on a result set.
162
-     *
163
-     * @param array $results
164
-     *
165
-     * @return array
166
-     */
167
-    public function eagerLoadRelations(array $results)
168
-    {
169
-        foreach ($this->eagerLoads as $name => $constraints) {
170
-
171
-            // For nested eager loads we'll skip loading them here and they will be set as an
172
-            // eager load on the query to retrieve the relation so that they will be eager
173
-            // loaded on that query, because that is where they get hydrated as models.
174
-            if (strpos($name, '.') === false) {
175
-                $results = $this->loadRelation($results, $name, $constraints);
176
-            }
177
-        }
178
-
179
-        return $results;
180
-    }
181
-
182
-    /**
183
-     * Eagerly load the relationship on a set of entities.
184
-     *
185
-     * @param array    $results
186
-     * @param string   $name
187
-     * @param \Closure $constraints
188
-     *
189
-     * @return array
190
-     */
191
-    protected function loadRelation(array $results, $name, Closure $constraints) : array
192
-    {
193
-        // First we will "back up" the existing where conditions on the query so we can
194
-        // add our eager constraints. Then we will merge the wheres that were on the
195
-        // query back to it in order that any where conditions might be specified.
196
-        $relation = $this->getRelation($name);
197
-
198
-        $relation->addEagerConstraints($results);
199
-
200
-        call_user_func($constraints, $relation);
201
-
202
-        // Once we have the results, we just match those back up to their parent models
203
-        // using the relationship instance. Then we just return the finished arrays
204
-        // of models which have been eagerly hydrated and are readied for return.
205
-
206
-        // Same, this step isn't necessary, as we take the inverse approach than Eloquent :
207
-        // filling the attributes before hydration, for more efficiency
208
-        //$relationshipResults = $relation->getEager();
209
-
210
-        return $relation->match($results, $name);
211
-    }
212
-
213
-    /**
214
-     * Get the relation instance for the given relation name.
215
-     *
216
-     * @param string $relation
217
-     *
218
-     * @return \Analogue\ORM\Relationships\Relationship
219
-     */
220
-    public function getRelation($relation)
221
-    {
222
-        // We want to run a relationship query without any constrains so that we will
223
-        // not have to remove these where clauses manually which gets really hacky
224
-        // and is error prone while we remove the developer's own where clauses.
225
-        $query = Relationship::noConstraints(function () use ($relation) {
226
-            return $this->entityMap->$relation($this->defaultMapper->newInstance());
227
-        });
228
-
229
-        $nested = $this->nestedRelations($relation);
230
-
231
-        // If there are nested relationships set on the query, we will put those onto
232
-        // the query instances so that they can be handled after this relationship
233
-        // is loaded. In this way they will all trickle down as they are loaded.
234
-        if (count($nested) > 0) {
235
-            $query->getQuery()->with($nested);
236
-        }
237
-
238
-        return $query;
239
-    }
240
-
241
-    /**
242
-     * Get the deeply nested relations for a given top-level relation.
243
-     *
244
-     * @param string $relation
245
-     *
246
-     * @return array
247
-     */
248
-    protected function nestedRelations($relation)
249
-    {
250
-        $nested = [];
251
-
252
-        // We are basically looking for any relationships that are nested deeper than
253
-        // the given top-level relationship. We will just check for any relations
254
-        // that start with the given top relations and adds them to our arrays.
255
-        foreach ($this->eagerLoads as $name => $constraints) {
256
-            if ($this->isNested($name, $relation)) {
257
-                $nested[substr($name, strlen($relation.'.'))] = $constraints;
258
-            }
259
-        }
260
-
261
-        return $nested;
262
-    }
263
-
264
-    /**
265
-     * Determine if the relationship is nested.
266
-     *
267
-     * @param string $name
268
-     * @param string $relation
269
-     *
270
-     * @return bool
271
-     */
272
-    protected function isNested($name, $relation)
273
-    {
274
-        $dots = str_contains($name, '.');
275
-
276
-        return $dots && starts_with($name, $relation.'.');
277
-    }
278
-
279
-    /**
280
-     * Build an entity from results, using the default mapper on this builder.
281
-     * This is the default build plan when no table inheritance is being used.
282
-     *
283
-     * @param array $results
284
-     *
285
-     * @return Collection
286
-     */
287
-    protected function buildWithDefaultMapper(array $results)
288
-    {
289
-        $builder = new EntityBuilder($this->defaultMapper, array_keys($this->eagerLoads));
290
-
291
-        return collect($results)->map(function ($item, $key) use ($builder) {
292
-            return $builder->build($item);
293
-        })->all();
294
-    }
295
-
296
-    /**
297
-     * Build an entity from results, using single table inheritance.
298
-     *
299
-     * @param array $results
300
-     *
301
-     * @return Collection
302
-     */
303
-    protected function buildUsingSingleTableInheritance(array $results)
304
-    {
305
-        return collect($results)->map(function ($item, $key) {
306
-            $builder = $this->builderForResult($item);
307
-
308
-            return $builder->build($item);
309
-        })->all();
310
-    }
311
-
312
-    /**
313
-     * Given a result array, return the entity builder needed to correctly
314
-     * build the result into an entity. If no getDiscriminatorColumnMap property
315
-     * has been defined on the EntityMap, we'll assume that the value stored in
316
-     * the $type column is the fully qualified class name of the entity and
317
-     * we'll use it instead.
318
-     *
319
-     * @param array $result
320
-     *
321
-     * @return EntityBuilder
322
-     */
323
-    protected function builderForResult(array $result)
324
-    {
325
-        $type = $result[$this->entityMap->getDiscriminatorColumn()];
326
-
327
-        $columnMap = $this->entityMap->getDiscriminatorColumnMap();
328
-
329
-        $class = isset($columnMap[$type]) ? $columnMap[$type] : $type;
330
-
331
-        if (!isset($this->builders[$type])) {
332
-            $this->builders[$type] = new EntityBuilder(
333
-                Manager::getInstance()->mapper($class),
334
-                array_keys($this->eagerLoads)
335
-            );
336
-        }
337
-
338
-        return $this->builders[$type];
339
-    }
10
+	/**
11
+	 * The default mapper used to build entities with.
12
+	 *
13
+	 * @var \Analogue\ORM\System\Mapper
14
+	 */
15
+	protected $defaultMapper;
16
+
17
+	/**
18
+	 * Relations that will be eager loaded on this query.
19
+	 *
20
+	 * @var array
21
+	 */
22
+	protected $eagerLoads;
23
+
24
+	/**
25
+	 * The Entity Map for the entity to build.
26
+	 *
27
+	 * @var \Analogue\ORM\EntityMap
28
+	 */
29
+	protected $entityMap;
30
+
31
+	/**
32
+	 * An array of builders used by this class to build necessary
33
+	 * entities for each result type.
34
+	 *
35
+	 * @var array
36
+	 */
37
+	protected $builders = [];
38
+
39
+	/**
40
+	 * @param Mapper $defaultMapper
41
+	 * @param array  $eagerLoads
42
+	 */
43
+	public function __construct(Mapper $defaultMapper)
44
+	{
45
+		$this->defaultMapper = $defaultMapper;
46
+		$this->entityMap = $defaultMapper->getEntityMap();
47
+	}
48
+
49
+	/**
50
+	 * Convert a result set into an array of entities.
51
+	 *
52
+	 * @param array $results
53
+	 * @param array $eagerLoads name of the relation to be eager loaded on the Entities
54
+	 *
55
+	 * @return \Illuminate\Support\Collection
56
+	 */
57
+	public function build(array $results, array $eagerLoads)
58
+	{
59
+		// First, we'll cast every single result to array
60
+		$results = array_map(function ($item) {
61
+			return (array) $item;
62
+		}, $results);
63
+
64
+		// Then, we'll cache every single results as raw attributes, before
65
+		// adding relationships, which will be cached when the relationship's
66
+		// query takes place.
67
+		$this->defaultMapper->getEntityCache()->add($results);
68
+
69
+		// Launch the queries related to eager loads, and match the
70
+		// current result set to these loaded relationships.
71
+		$results = $this->queryEagerLoadedRelationships($results, $eagerLoads);
72
+
73
+		// Note : Maybe we could use a PolymorphicResultBuilder, which would
74
+		// be shared by both STI and polymorphic relations, as they share the
75
+		// same process.
76
+
77
+		switch ($this->entityMap->getInheritanceType()) {
78
+			case 'single_table':
79
+				return $this->buildUsingSingleTableInheritance($results);
80
+				break;
81
+
82
+			default:
83
+				return $this->buildWithDefaultMapper($results);
84
+				break;
85
+		}
86
+	}
87
+
88
+	/**
89
+	 * Launch queries on eager loaded relationships.
90
+	 *
91
+	 * @return array
92
+	 */
93
+	protected function queryEagerLoadedRelationships(array $results, array $eagerLoads)
94
+	{
95
+		$this->eagerLoads = $this->parseRelations($eagerLoads);
96
+
97
+		return $this->eagerLoadRelations($results);
98
+	}
99
+
100
+	/**
101
+	 * Parse a list of relations into individuals.
102
+	 *
103
+	 * @param array $relations
104
+	 *
105
+	 * @return array
106
+	 */
107
+	protected function parseRelations(array $relations)
108
+	{
109
+		$results = [];
110
+
111
+		foreach ($relations as $name => $constraints) {
112
+			// If the "relation" value is actually a numeric key, we can assume that no
113
+			// constraints have been specified for the eager load and we'll just put
114
+			// an empty Closure with the loader so that we can treat all the same.
115
+			if (is_numeric($name)) {
116
+				$f = function () {
117
+				};
118
+
119
+				list($name, $constraints) = [$constraints, $f];
120
+			}
121
+
122
+			// We need to separate out any nested includes. Which allows the developers
123
+			// to load deep relationships using "dots" without stating each level of
124
+			// the relationship with its own key in the array of eager load names.
125
+			$results = $this->parseNested($name, $results);
126
+
127
+			$results[$name] = $constraints;
128
+		}
129
+
130
+		return $results;
131
+	}
132
+
133
+	/**
134
+	 * Parse the nested relationships in a relation.
135
+	 *
136
+	 * @param string $name
137
+	 * @param array  $results
138
+	 *
139
+	 * @return array
140
+	 */
141
+	protected function parseNested($name, $results)
142
+	{
143
+		$progress = [];
144
+
145
+		// If the relation has already been set on the result array, we will not set it
146
+		// again, since that would override any constraints that were already placed
147
+		// on the relationships. We will only set the ones that are not specified.
148
+		foreach (explode('.', $name) as $segment) {
149
+			$progress[] = $segment;
150
+
151
+			if (!isset($results[$last = implode('.', $progress)])) {
152
+				$results[$last] = function () {
153
+				};
154
+			}
155
+		}
156
+
157
+		return $results;
158
+	}
159
+
160
+	/**
161
+	 * Eager load the relationships on a result set.
162
+	 *
163
+	 * @param array $results
164
+	 *
165
+	 * @return array
166
+	 */
167
+	public function eagerLoadRelations(array $results)
168
+	{
169
+		foreach ($this->eagerLoads as $name => $constraints) {
170
+
171
+			// For nested eager loads we'll skip loading them here and they will be set as an
172
+			// eager load on the query to retrieve the relation so that they will be eager
173
+			// loaded on that query, because that is where they get hydrated as models.
174
+			if (strpos($name, '.') === false) {
175
+				$results = $this->loadRelation($results, $name, $constraints);
176
+			}
177
+		}
178
+
179
+		return $results;
180
+	}
181
+
182
+	/**
183
+	 * Eagerly load the relationship on a set of entities.
184
+	 *
185
+	 * @param array    $results
186
+	 * @param string   $name
187
+	 * @param \Closure $constraints
188
+	 *
189
+	 * @return array
190
+	 */
191
+	protected function loadRelation(array $results, $name, Closure $constraints) : array
192
+	{
193
+		// First we will "back up" the existing where conditions on the query so we can
194
+		// add our eager constraints. Then we will merge the wheres that were on the
195
+		// query back to it in order that any where conditions might be specified.
196
+		$relation = $this->getRelation($name);
197
+
198
+		$relation->addEagerConstraints($results);
199
+
200
+		call_user_func($constraints, $relation);
201
+
202
+		// Once we have the results, we just match those back up to their parent models
203
+		// using the relationship instance. Then we just return the finished arrays
204
+		// of models which have been eagerly hydrated and are readied for return.
205
+
206
+		// Same, this step isn't necessary, as we take the inverse approach than Eloquent :
207
+		// filling the attributes before hydration, for more efficiency
208
+		//$relationshipResults = $relation->getEager();
209
+
210
+		return $relation->match($results, $name);
211
+	}
212
+
213
+	/**
214
+	 * Get the relation instance for the given relation name.
215
+	 *
216
+	 * @param string $relation
217
+	 *
218
+	 * @return \Analogue\ORM\Relationships\Relationship
219
+	 */
220
+	public function getRelation($relation)
221
+	{
222
+		// We want to run a relationship query without any constrains so that we will
223
+		// not have to remove these where clauses manually which gets really hacky
224
+		// and is error prone while we remove the developer's own where clauses.
225
+		$query = Relationship::noConstraints(function () use ($relation) {
226
+			return $this->entityMap->$relation($this->defaultMapper->newInstance());
227
+		});
228
+
229
+		$nested = $this->nestedRelations($relation);
230
+
231
+		// If there are nested relationships set on the query, we will put those onto
232
+		// the query instances so that they can be handled after this relationship
233
+		// is loaded. In this way they will all trickle down as they are loaded.
234
+		if (count($nested) > 0) {
235
+			$query->getQuery()->with($nested);
236
+		}
237
+
238
+		return $query;
239
+	}
240
+
241
+	/**
242
+	 * Get the deeply nested relations for a given top-level relation.
243
+	 *
244
+	 * @param string $relation
245
+	 *
246
+	 * @return array
247
+	 */
248
+	protected function nestedRelations($relation)
249
+	{
250
+		$nested = [];
251
+
252
+		// We are basically looking for any relationships that are nested deeper than
253
+		// the given top-level relationship. We will just check for any relations
254
+		// that start with the given top relations and adds them to our arrays.
255
+		foreach ($this->eagerLoads as $name => $constraints) {
256
+			if ($this->isNested($name, $relation)) {
257
+				$nested[substr($name, strlen($relation.'.'))] = $constraints;
258
+			}
259
+		}
260
+
261
+		return $nested;
262
+	}
263
+
264
+	/**
265
+	 * Determine if the relationship is nested.
266
+	 *
267
+	 * @param string $name
268
+	 * @param string $relation
269
+	 *
270
+	 * @return bool
271
+	 */
272
+	protected function isNested($name, $relation)
273
+	{
274
+		$dots = str_contains($name, '.');
275
+
276
+		return $dots && starts_with($name, $relation.'.');
277
+	}
278
+
279
+	/**
280
+	 * Build an entity from results, using the default mapper on this builder.
281
+	 * This is the default build plan when no table inheritance is being used.
282
+	 *
283
+	 * @param array $results
284
+	 *
285
+	 * @return Collection
286
+	 */
287
+	protected function buildWithDefaultMapper(array $results)
288
+	{
289
+		$builder = new EntityBuilder($this->defaultMapper, array_keys($this->eagerLoads));
290
+
291
+		return collect($results)->map(function ($item, $key) use ($builder) {
292
+			return $builder->build($item);
293
+		})->all();
294
+	}
295
+
296
+	/**
297
+	 * Build an entity from results, using single table inheritance.
298
+	 *
299
+	 * @param array $results
300
+	 *
301
+	 * @return Collection
302
+	 */
303
+	protected function buildUsingSingleTableInheritance(array $results)
304
+	{
305
+		return collect($results)->map(function ($item, $key) {
306
+			$builder = $this->builderForResult($item);
307
+
308
+			return $builder->build($item);
309
+		})->all();
310
+	}
311
+
312
+	/**
313
+	 * Given a result array, return the entity builder needed to correctly
314
+	 * build the result into an entity. If no getDiscriminatorColumnMap property
315
+	 * has been defined on the EntityMap, we'll assume that the value stored in
316
+	 * the $type column is the fully qualified class name of the entity and
317
+	 * we'll use it instead.
318
+	 *
319
+	 * @param array $result
320
+	 *
321
+	 * @return EntityBuilder
322
+	 */
323
+	protected function builderForResult(array $result)
324
+	{
325
+		$type = $result[$this->entityMap->getDiscriminatorColumn()];
326
+
327
+		$columnMap = $this->entityMap->getDiscriminatorColumnMap();
328
+
329
+		$class = isset($columnMap[$type]) ? $columnMap[$type] : $type;
330
+
331
+		if (!isset($this->builders[$type])) {
332
+			$this->builders[$type] = new EntityBuilder(
333
+				Manager::getInstance()->mapper($class),
334
+				array_keys($this->eagerLoads)
335
+			);
336
+		}
337
+
338
+		return $this->builders[$type];
339
+	}
340 340
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -57,7 +57,7 @@  discard block
 block discarded – undo
57 57
     public function build(array $results, array $eagerLoads)
58 58
     {
59 59
         // First, we'll cast every single result to array
60
-        $results = array_map(function ($item) {
60
+        $results = array_map(function($item) {
61 61
             return (array) $item;
62 62
         }, $results);
63 63
 
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
             // constraints have been specified for the eager load and we'll just put
114 114
             // an empty Closure with the loader so that we can treat all the same.
115 115
             if (is_numeric($name)) {
116
-                $f = function () {
116
+                $f = function() {
117 117
                 };
118 118
 
119 119
                 list($name, $constraints) = [$constraints, $f];
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
             $progress[] = $segment;
150 150
 
151 151
             if (!isset($results[$last = implode('.', $progress)])) {
152
-                $results[$last] = function () {
152
+                $results[$last] = function() {
153 153
                 };
154 154
             }
155 155
         }
@@ -222,7 +222,7 @@  discard block
 block discarded – undo
222 222
         // We want to run a relationship query without any constrains so that we will
223 223
         // not have to remove these where clauses manually which gets really hacky
224 224
         // and is error prone while we remove the developer's own where clauses.
225
-        $query = Relationship::noConstraints(function () use ($relation) {
225
+        $query = Relationship::noConstraints(function() use ($relation) {
226 226
             return $this->entityMap->$relation($this->defaultMapper->newInstance());
227 227
         });
228 228
 
@@ -288,7 +288,7 @@  discard block
 block discarded – undo
288 288
     {
289 289
         $builder = new EntityBuilder($this->defaultMapper, array_keys($this->eagerLoads));
290 290
 
291
-        return collect($results)->map(function ($item, $key) use ($builder) {
291
+        return collect($results)->map(function($item, $key) use ($builder) {
292 292
             return $builder->build($item);
293 293
         })->all();
294 294
     }
@@ -302,7 +302,7 @@  discard block
 block discarded – undo
302 302
      */
303 303
     protected function buildUsingSingleTableInheritance(array $results)
304 304
     {
305
-        return collect($results)->map(function ($item, $key) {
305
+        return collect($results)->map(function($item, $key) {
306 306
             $builder = $this->builderForResult($item);
307 307
 
308 308
             return $builder->build($item);
Please login to merge, or discard this patch.
src/System/SingleTableInheritanceScope.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -83,7 +83,7 @@
 block discarded – undo
83 83
     /**
84 84
      * Remove the scope from the given Analogue query builder.
85 85
      *
86
-     * @param mixed $query
86
+     * @param Query $query
87 87
      *
88 88
      * @return void
89 89
      */
Please login to merge, or discard this patch.
Indentation   +106 added lines, -106 removed lines patch added patch discarded remove patch
@@ -6,110 +6,110 @@
 block discarded – undo
6 6
 
7 7
 class SingleTableInheritanceScope implements ScopeInterface
8 8
 {
9
-    /**
10
-     * Discriminator column name.
11
-     *
12
-     * @var string
13
-     */
14
-    protected $column;
15
-
16
-    /**
17
-     * Discriminator column allowed types.
18
-     *
19
-     * @var array
20
-     */
21
-    protected $types = [];
22
-
23
-    public function __construct(EntityMap $entityMap)
24
-    {
25
-        // Putting the heavy logic in here, so we won't have
26
-        // to go through it each time we reach for a query
27
-        // builder.
28
-
29
-        $this->column = $entityMap->getDiscriminatorColumn();
30
-
31
-        // First we need to retrieve the base class & it's normalized
32
-        // type string
33
-        $class = $entityMap->getClass();
34
-        $this->types[] = $this->getTypeStringForEntity($class, $entityMap);
35
-
36
-        // Then, we parse all registered entities for any potential
37
-        // child class.
38
-        $classes = Manager::getInstance()->getRegisteredEntities();
39
-
40
-        foreach ($classes as $otherClass => $entityMap) {
41
-            if (is_subclass_of($otherClass, $class)) {
42
-                $this->types[] = $this->getTypeStringForEntity($otherClass, $entityMap);
43
-            }
44
-        }
45
-    }
46
-
47
-    /**
48
-     * Get the normalized value to use for query on discriminator column.
49
-     *
50
-     * @param string    $class
51
-     * @param EntityMap $entityMap
52
-     *
53
-     * @return string
54
-     */
55
-    protected function getTypeStringForEntity($class, EntityMap $entityMap)
56
-    {
57
-        $class = $entityMap->getClass();
58
-
59
-        $type = array_keys(
60
-            $entityMap->getDiscriminatorColumnMap(),
61
-            $class
62
-        );
63
-
64
-        if (count($type) == 0) {
65
-            return $class;
66
-        }
67
-
68
-        return $type[0];
69
-    }
70
-
71
-    /**
72
-     * Apply the scope to a given Analogue query builder.
73
-     *
74
-     * @param \Analogue\ORM\System\Query $query
75
-     *
76
-     * @return void
77
-     */
78
-    public function apply(Query $query)
79
-    {
80
-        $query->whereIn($this->column, $this->types);
81
-    }
82
-
83
-    /**
84
-     * Remove the scope from the given Analogue query builder.
85
-     *
86
-     * @param mixed $query
87
-     *
88
-     * @return void
89
-     */
90
-    public function remove(Query $query)
91
-    {
92
-        $query = $query->getQuery();
93
-
94
-        foreach ((array) $query->wheres as $key => $where) {
95
-            if ($this->isSingleTableConstraint($where, $this->column)) {
96
-                unset($query->wheres[$key]);
97
-
98
-                $query->wheres = array_values($query->wheres);
99
-            }
100
-        }
101
-    }
102
-
103
-    /**
104
-     * Determine if the given where clause is a single table inheritance constraint.
105
-     *
106
-     * @param array  $where
107
-     * @param string $column
108
-     *
109
-     * @return bool
110
-     */
111
-    protected function isSingleTableConstraint(array $where, $column)
112
-    {
113
-        return $where['column'] == $column;
114
-    }
9
+	/**
10
+	 * Discriminator column name.
11
+	 *
12
+	 * @var string
13
+	 */
14
+	protected $column;
15
+
16
+	/**
17
+	 * Discriminator column allowed types.
18
+	 *
19
+	 * @var array
20
+	 */
21
+	protected $types = [];
22
+
23
+	public function __construct(EntityMap $entityMap)
24
+	{
25
+		// Putting the heavy logic in here, so we won't have
26
+		// to go through it each time we reach for a query
27
+		// builder.
28
+
29
+		$this->column = $entityMap->getDiscriminatorColumn();
30
+
31
+		// First we need to retrieve the base class & it's normalized
32
+		// type string
33
+		$class = $entityMap->getClass();
34
+		$this->types[] = $this->getTypeStringForEntity($class, $entityMap);
35
+
36
+		// Then, we parse all registered entities for any potential
37
+		// child class.
38
+		$classes = Manager::getInstance()->getRegisteredEntities();
39
+
40
+		foreach ($classes as $otherClass => $entityMap) {
41
+			if (is_subclass_of($otherClass, $class)) {
42
+				$this->types[] = $this->getTypeStringForEntity($otherClass, $entityMap);
43
+			}
44
+		}
45
+	}
46
+
47
+	/**
48
+	 * Get the normalized value to use for query on discriminator column.
49
+	 *
50
+	 * @param string    $class
51
+	 * @param EntityMap $entityMap
52
+	 *
53
+	 * @return string
54
+	 */
55
+	protected function getTypeStringForEntity($class, EntityMap $entityMap)
56
+	{
57
+		$class = $entityMap->getClass();
58
+
59
+		$type = array_keys(
60
+			$entityMap->getDiscriminatorColumnMap(),
61
+			$class
62
+		);
63
+
64
+		if (count($type) == 0) {
65
+			return $class;
66
+		}
67
+
68
+		return $type[0];
69
+	}
70
+
71
+	/**
72
+	 * Apply the scope to a given Analogue query builder.
73
+	 *
74
+	 * @param \Analogue\ORM\System\Query $query
75
+	 *
76
+	 * @return void
77
+	 */
78
+	public function apply(Query $query)
79
+	{
80
+		$query->whereIn($this->column, $this->types);
81
+	}
82
+
83
+	/**
84
+	 * Remove the scope from the given Analogue query builder.
85
+	 *
86
+	 * @param mixed $query
87
+	 *
88
+	 * @return void
89
+	 */
90
+	public function remove(Query $query)
91
+	{
92
+		$query = $query->getQuery();
93
+
94
+		foreach ((array) $query->wheres as $key => $where) {
95
+			if ($this->isSingleTableConstraint($where, $this->column)) {
96
+				unset($query->wheres[$key]);
97
+
98
+				$query->wheres = array_values($query->wheres);
99
+			}
100
+		}
101
+	}
102
+
103
+	/**
104
+	 * Determine if the given where clause is a single table inheritance constraint.
105
+	 *
106
+	 * @param array  $where
107
+	 * @param string $column
108
+	 *
109
+	 * @return bool
110
+	 */
111
+	protected function isSingleTableConstraint(array $where, $column)
112
+	{
113
+		return $where['column'] == $column;
114
+	}
115 115
 }
Please login to merge, or discard this patch.
src/System/Wrappers/ObjectWrapper.php 2 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -35,7 +35,6 @@
 block discarded – undo
35 35
     /**
36 36
      * Object Wrapper constructor.
37 37
      *
38
-     * @param mixed                  $object
39 38
      * @param Analogue\ORM\EntityMap $entityMap
40 39
      *
41 40
      * @return void
Please login to merge, or discard this patch.
Indentation   +221 added lines, -221 removed lines patch added patch discarded remove patch
@@ -10,225 +10,225 @@
 block discarded – undo
10 10
  */
11 11
 class ObjectWrapper extends Wrapper
12 12
 {
13
-    /**
14
-     * Internal Representation of analogue's entity attributes.
15
-     *
16
-     * @var array
17
-     */
18
-    protected $attributes = [];
19
-
20
-    /**
21
-     * Object properties that are not a part of the entity attributes,
22
-     * but which are needed to correctly hydrate the Object.
23
-     *
24
-     * @var array
25
-     */
26
-    protected $unmanagedProperties = [];
27
-
28
-    /**
29
-     * The hydrator for the wrapped object.
30
-     *
31
-     * @var HydratorInterface
32
-     */
33
-    protected $hydrator;
34
-
35
-    /**
36
-     * Object Wrapper constructor.
37
-     *
38
-     * @param mixed                  $object
39
-     * @param Analogue\ORM\EntityMap $entityMap
40
-     *
41
-     * @return void
42
-     */
43
-    public function __construct($entity, $entityMap, HydratorInterface $hydrator)
44
-    {
45
-        $this->hydrator = $hydrator;
46
-        parent::__construct($entity, $entityMap);
47
-        $this->attributes = $this->dehydrate($entity);
48
-    }
49
-
50
-    /**
51
-     * Returns the wrapped entity.
52
-     *
53
-     * @return mixed
54
-     */
55
-    public function getObject()
56
-    {
57
-        $this->hydrate();
58
-
59
-        return $this->entity;
60
-    }
61
-
62
-    /**
63
-     * Extract entity attributes / properties to an array of attributes.
64
-     *
65
-     * @param mixed $entity
66
-     *
67
-     * @return array
68
-     */
69
-    protected function dehydrate($entity) : array
70
-    {
71
-        $properties = $this->hydrator->extract($entity);
72
-
73
-        $this->unmanagedProperties = array_except($properties, $this->getManagedProperties());
74
-
75
-        return $this->attributesFromProperties($properties);
76
-    }
77
-
78
-    /**
79
-     * Hydrate object's properties/attribute from the internal array representation.
80
-     *
81
-     * @return mixed
82
-     */
83
-    public function hydrate()
84
-    {
85
-        $properties = $this->propertiesFromAttributes($this->attributes) + $this->unmanagedProperties;
86
-        $this->hydrator->hydrate($properties, $this->entity);
87
-    }
88
-
89
-    /**
90
-     * Return properties that will be extracted from the entity.
91
-     *
92
-     * @return array
93
-     */
94
-    protected function getManagedProperties() : array
95
-    {
96
-        $properties = $this->entityMap->getProperties();
97
-
98
-        $attributesName = $this->entityMap->getAttributesArrayName();
99
-
100
-        return $attributesName == null ? $properties : array_merge($properties, [$attributesName]);
101
-    }
102
-
103
-    /**
104
-     * Convert object's properties to analogue's internal attributes representation.
105
-     *
106
-     * @param array $properties
107
-     *
108
-     * @return array
109
-     */
110
-    protected function attributesFromProperties(array $properties) : array
111
-    {
112
-        // First, we'll only keep the entities that are part of the Entity's
113
-        // attributes
114
-        $managedProperties = $this->getManagedProperties();
115
-
116
-        $properties = array_only($properties, $managedProperties);
117
-
118
-        // If the entity does not uses the attributes array to store
119
-        // part of its attributes, we'll directly return the properties
120
-        if (!$this->entityMap->usesAttributesArray()) {
121
-            return $properties;
122
-        }
123
-
124
-        $arrayName = $this->entityMap->getAttributesArrayName();
125
-
126
-        if (!array_key_exists($arrayName, $properties)) {
127
-            throw new MappingException("Property $arrayName not set on object of type ".$this->getEntityClass());
128
-        }
129
-
130
-        if (!is_array($properties[$arrayName])) {
131
-            throw new MappingException("Property $arrayName should be an array.");
132
-        }
133
-
134
-        $attributes = $properties[$arrayName];
135
-
136
-        unset($properties[$arrayName]);
137
-
138
-        return $properties + $attributes;
139
-    }
140
-
141
-    /**
142
-     * Convert internal representation of attributes to an array of properties
143
-     * that can hydrate the actual object.
144
-     *
145
-     * @param array $attributes
146
-     *
147
-     * @return array
148
-     */
149
-    protected function propertiesFromAttributes(array $attributes) : array
150
-    {
151
-        $attributes = $this->attributes;
152
-
153
-        // Get all managed properties
154
-        $propertyNames = $this->entityMap->getProperties();
155
-
156
-        $propertyAttributes = array_only($attributes, $propertyNames);
157
-        $attributesArray = array_except($attributes, $propertyNames);
158
-
159
-        $attributesArrayName = $this->entityMap->getAttributesArrayName();
160
-
161
-        if ($attributesArrayName) {
162
-            $propertyAttributes[$attributesArrayName] = $attributesArray;
163
-        }
164
-
165
-        return $propertyAttributes;
166
-    }
167
-
168
-    /**
169
-     * Method used by the mapper to set the object
170
-     * attribute raw values (hydration).
171
-     *
172
-     * @param array $attributes
173
-     *
174
-     * @return void
175
-     */
176
-    public function setEntityAttributes(array $attributes)
177
-    {
178
-        $this->attributes = $attributes;
179
-    }
180
-
181
-    /**
182
-     * Method used by the mapper to get the
183
-     * raw object's values.
184
-     *
185
-     * @return array
186
-     */
187
-    public function getEntityAttributes() : array
188
-    {
189
-        return $this->attributes;
190
-    }
191
-
192
-    /**
193
-     * Method used by the mapper to set raw
194
-     * key-value pair.
195
-     *
196
-     * @param string $key
197
-     * @param string $value
198
-     *
199
-     * @return void
200
-     */
201
-    public function setEntityAttribute($key, $value)
202
-    {
203
-        $this->attributes[$key] = $value;
204
-    }
205
-
206
-    /**
207
-     * Method used by the mapper to get single
208
-     * key-value pair.
209
-     *
210
-     * @param string $key
211
-     *
212
-     * @return mixed|null
213
-     */
214
-    public function getEntityAttribute($key)
215
-    {
216
-        if ($this->hasAttribute($key)) {
217
-            return $this->attributes[$key];
218
-        } else {
219
-            return;
220
-        }
221
-    }
222
-
223
-    /**
224
-     * Test if a given attribute exists.
225
-     *
226
-     * @param string $key
227
-     *
228
-     * @return bool
229
-     */
230
-    public function hasAttribute($key) : bool
231
-    {
232
-        return array_key_exists($key, $this->attributes) ? true : false;
233
-    }
13
+	/**
14
+	 * Internal Representation of analogue's entity attributes.
15
+	 *
16
+	 * @var array
17
+	 */
18
+	protected $attributes = [];
19
+
20
+	/**
21
+	 * Object properties that are not a part of the entity attributes,
22
+	 * but which are needed to correctly hydrate the Object.
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected $unmanagedProperties = [];
27
+
28
+	/**
29
+	 * The hydrator for the wrapped object.
30
+	 *
31
+	 * @var HydratorInterface
32
+	 */
33
+	protected $hydrator;
34
+
35
+	/**
36
+	 * Object Wrapper constructor.
37
+	 *
38
+	 * @param mixed                  $object
39
+	 * @param Analogue\ORM\EntityMap $entityMap
40
+	 *
41
+	 * @return void
42
+	 */
43
+	public function __construct($entity, $entityMap, HydratorInterface $hydrator)
44
+	{
45
+		$this->hydrator = $hydrator;
46
+		parent::__construct($entity, $entityMap);
47
+		$this->attributes = $this->dehydrate($entity);
48
+	}
49
+
50
+	/**
51
+	 * Returns the wrapped entity.
52
+	 *
53
+	 * @return mixed
54
+	 */
55
+	public function getObject()
56
+	{
57
+		$this->hydrate();
58
+
59
+		return $this->entity;
60
+	}
61
+
62
+	/**
63
+	 * Extract entity attributes / properties to an array of attributes.
64
+	 *
65
+	 * @param mixed $entity
66
+	 *
67
+	 * @return array
68
+	 */
69
+	protected function dehydrate($entity) : array
70
+	{
71
+		$properties = $this->hydrator->extract($entity);
72
+
73
+		$this->unmanagedProperties = array_except($properties, $this->getManagedProperties());
74
+
75
+		return $this->attributesFromProperties($properties);
76
+	}
77
+
78
+	/**
79
+	 * Hydrate object's properties/attribute from the internal array representation.
80
+	 *
81
+	 * @return mixed
82
+	 */
83
+	public function hydrate()
84
+	{
85
+		$properties = $this->propertiesFromAttributes($this->attributes) + $this->unmanagedProperties;
86
+		$this->hydrator->hydrate($properties, $this->entity);
87
+	}
88
+
89
+	/**
90
+	 * Return properties that will be extracted from the entity.
91
+	 *
92
+	 * @return array
93
+	 */
94
+	protected function getManagedProperties() : array
95
+	{
96
+		$properties = $this->entityMap->getProperties();
97
+
98
+		$attributesName = $this->entityMap->getAttributesArrayName();
99
+
100
+		return $attributesName == null ? $properties : array_merge($properties, [$attributesName]);
101
+	}
102
+
103
+	/**
104
+	 * Convert object's properties to analogue's internal attributes representation.
105
+	 *
106
+	 * @param array $properties
107
+	 *
108
+	 * @return array
109
+	 */
110
+	protected function attributesFromProperties(array $properties) : array
111
+	{
112
+		// First, we'll only keep the entities that are part of the Entity's
113
+		// attributes
114
+		$managedProperties = $this->getManagedProperties();
115
+
116
+		$properties = array_only($properties, $managedProperties);
117
+
118
+		// If the entity does not uses the attributes array to store
119
+		// part of its attributes, we'll directly return the properties
120
+		if (!$this->entityMap->usesAttributesArray()) {
121
+			return $properties;
122
+		}
123
+
124
+		$arrayName = $this->entityMap->getAttributesArrayName();
125
+
126
+		if (!array_key_exists($arrayName, $properties)) {
127
+			throw new MappingException("Property $arrayName not set on object of type ".$this->getEntityClass());
128
+		}
129
+
130
+		if (!is_array($properties[$arrayName])) {
131
+			throw new MappingException("Property $arrayName should be an array.");
132
+		}
133
+
134
+		$attributes = $properties[$arrayName];
135
+
136
+		unset($properties[$arrayName]);
137
+
138
+		return $properties + $attributes;
139
+	}
140
+
141
+	/**
142
+	 * Convert internal representation of attributes to an array of properties
143
+	 * that can hydrate the actual object.
144
+	 *
145
+	 * @param array $attributes
146
+	 *
147
+	 * @return array
148
+	 */
149
+	protected function propertiesFromAttributes(array $attributes) : array
150
+	{
151
+		$attributes = $this->attributes;
152
+
153
+		// Get all managed properties
154
+		$propertyNames = $this->entityMap->getProperties();
155
+
156
+		$propertyAttributes = array_only($attributes, $propertyNames);
157
+		$attributesArray = array_except($attributes, $propertyNames);
158
+
159
+		$attributesArrayName = $this->entityMap->getAttributesArrayName();
160
+
161
+		if ($attributesArrayName) {
162
+			$propertyAttributes[$attributesArrayName] = $attributesArray;
163
+		}
164
+
165
+		return $propertyAttributes;
166
+	}
167
+
168
+	/**
169
+	 * Method used by the mapper to set the object
170
+	 * attribute raw values (hydration).
171
+	 *
172
+	 * @param array $attributes
173
+	 *
174
+	 * @return void
175
+	 */
176
+	public function setEntityAttributes(array $attributes)
177
+	{
178
+		$this->attributes = $attributes;
179
+	}
180
+
181
+	/**
182
+	 * Method used by the mapper to get the
183
+	 * raw object's values.
184
+	 *
185
+	 * @return array
186
+	 */
187
+	public function getEntityAttributes() : array
188
+	{
189
+		return $this->attributes;
190
+	}
191
+
192
+	/**
193
+	 * Method used by the mapper to set raw
194
+	 * key-value pair.
195
+	 *
196
+	 * @param string $key
197
+	 * @param string $value
198
+	 *
199
+	 * @return void
200
+	 */
201
+	public function setEntityAttribute($key, $value)
202
+	{
203
+		$this->attributes[$key] = $value;
204
+	}
205
+
206
+	/**
207
+	 * Method used by the mapper to get single
208
+	 * key-value pair.
209
+	 *
210
+	 * @param string $key
211
+	 *
212
+	 * @return mixed|null
213
+	 */
214
+	public function getEntityAttribute($key)
215
+	{
216
+		if ($this->hasAttribute($key)) {
217
+			return $this->attributes[$key];
218
+		} else {
219
+			return;
220
+		}
221
+	}
222
+
223
+	/**
224
+	 * Test if a given attribute exists.
225
+	 *
226
+	 * @param string $key
227
+	 *
228
+	 * @return bool
229
+	 */
230
+	public function hasAttribute($key) : bool
231
+	{
232
+		return array_key_exists($key, $this->attributes) ? true : false;
233
+	}
234 234
 }
Please login to merge, or discard this patch.