Completed
Branch master (a17b64)
by Rémi
15:50
created
src/MagicSetters.php 1 patch
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -7,28 +7,28 @@
 block discarded – undo
7 7
  */
8 8
 trait MagicSetters
9 9
 {
10
-    /**
11
-     * Dynamically set attributes on the entity.
12
-     *
13
-     * @param  string $key
14
-     * @param  mixed  $value
15
-     * @return void
16
-     */
17
-    public function __set($key, $value)
18
-    {
19
-        $this->attributes[$key] = $value;
20
-    }
10
+	/**
11
+	 * Dynamically set attributes on the entity.
12
+	 *
13
+	 * @param  string $key
14
+	 * @param  mixed  $value
15
+	 * @return void
16
+	 */
17
+	public function __set($key, $value)
18
+	{
19
+		$this->attributes[$key] = $value;
20
+	}
21 21
 
22
-    /**
23
-     * Unset an attribute on the entity.
24
-     *
25
-     * @param  string $key
26
-     * @return void
27
-     */
28
-    public function __unset($key)
29
-    {
30
-        unset($this->attributes[$key]);
31
-    }
22
+	/**
23
+	 * Unset an attribute on the entity.
24
+	 *
25
+	 * @param  string $key
26
+	 * @return void
27
+	 */
28
+	public function __unset($key)
29
+	{
30
+		unset($this->attributes[$key]);
31
+	}
32 32
 
33 33
 
34 34
 }
35 35
\ No newline at end of file
Please login to merge, or discard this patch.
src/Commands/Store.php 1 patch
Indentation   +221 added lines, -221 removed lines patch added patch discarded remove patch
@@ -14,238 +14,238 @@
 block discarded – undo
14 14
  */
15 15
 class Store extends Command
16 16
 {
17
-    /**
18
-     * Persist the entity in the database
19
-     *
20
-     * @throws \InvalidArgumentException
21
-     * @return false|mixed
22
-     */
23
-    public function execute()
24
-    {
25
-        $entity = $this->aggregate->getEntityObject();
26
-
27
-        $mapper = $this->aggregate->getMapper();
28
-
29
-        if ($mapper->fireEvent('storing', $entity) === false) {
30
-            return false;
31
-        }
32
-
33
-        $this->preStoreProcess();
34
-
35
-        /**
36
-         * We will test the entity for existence
37
-         * and run a creation if it doesn't exists
38
-         */
39
-        if (!$this->aggregate->exists()) {
40
-            if ($mapper->fireEvent('creating', $entity) === false) {
41
-                return false;
42
-            }
43
-
44
-            $this->insert();
45
-
46
-            $mapper->fireEvent('created', $entity, false);
47
-        }
48
-        else if ($this->aggregate->isDirty()) {
49
-            if ($mapper->fireEvent('updating', $entity) === false) {
50
-                return false;
51
-            }
52
-            $this->update();
53
-
54
-            $mapper->fireEvent('updated', $entity, false);
55
-        }
56
-
57
-        $this->postStoreProcess();
58
-
59
-        $mapper->fireEvent('stored', $entity, false);
60
-
61
-        return $entity;
62
-    }
63
-
64
-    /**
65
-     * Run all operations that have to occur before actually
66
-     * storing the entity
67
-     *
68
-     * @throws \InvalidArgumentException
69
-     * @return void
70
-     */
71
-    protected function preStoreProcess()
72
-    {
73
-        // Create any related object that doesn't exist in the database.
74
-        $localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
75
-
76
-        $this->createRelatedEntities($localRelationships);
77
-
78
-        // Now we can sync the related collections
79
-        $this->aggregate->syncRelationships($localRelationships);
80
-    }
81
-
82
-    /**
83
-     * Check for existence and create non-existing related entities
84
-     *
85
-     * @param  array
86
-     * @throws \InvalidArgumentException
87
-     * @return void
88
-     */
89
-    protected function createRelatedEntities($relations)
90
-    {
91
-        $entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
92
-
93
-        foreach ($entitiesToCreate as $aggregate) {
94
-            $this->createStoreCommand($aggregate)->execute();
95
-        }
17
+	/**
18
+	 * Persist the entity in the database
19
+	 *
20
+	 * @throws \InvalidArgumentException
21
+	 * @return false|mixed
22
+	 */
23
+	public function execute()
24
+	{
25
+		$entity = $this->aggregate->getEntityObject();
26
+
27
+		$mapper = $this->aggregate->getMapper();
28
+
29
+		if ($mapper->fireEvent('storing', $entity) === false) {
30
+			return false;
31
+		}
32
+
33
+		$this->preStoreProcess();
34
+
35
+		/**
36
+		 * We will test the entity for existence
37
+		 * and run a creation if it doesn't exists
38
+		 */
39
+		if (!$this->aggregate->exists()) {
40
+			if ($mapper->fireEvent('creating', $entity) === false) {
41
+				return false;
42
+			}
43
+
44
+			$this->insert();
45
+
46
+			$mapper->fireEvent('created', $entity, false);
47
+		}
48
+		else if ($this->aggregate->isDirty()) {
49
+			if ($mapper->fireEvent('updating', $entity) === false) {
50
+				return false;
51
+			}
52
+			$this->update();
53
+
54
+			$mapper->fireEvent('updated', $entity, false);
55
+		}
56
+
57
+		$this->postStoreProcess();
58
+
59
+		$mapper->fireEvent('stored', $entity, false);
60
+
61
+		return $entity;
62
+	}
63
+
64
+	/**
65
+	 * Run all operations that have to occur before actually
66
+	 * storing the entity
67
+	 *
68
+	 * @throws \InvalidArgumentException
69
+	 * @return void
70
+	 */
71
+	protected function preStoreProcess()
72
+	{
73
+		// Create any related object that doesn't exist in the database.
74
+		$localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
75
+
76
+		$this->createRelatedEntities($localRelationships);
77
+
78
+		// Now we can sync the related collections
79
+		$this->aggregate->syncRelationships($localRelationships);
80
+	}
81
+
82
+	/**
83
+	 * Check for existence and create non-existing related entities
84
+	 *
85
+	 * @param  array
86
+	 * @throws \InvalidArgumentException
87
+	 * @return void
88
+	 */
89
+	protected function createRelatedEntities($relations)
90
+	{
91
+		$entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
92
+
93
+		foreach ($entitiesToCreate as $aggregate) {
94
+			$this->createStoreCommand($aggregate)->execute();
95
+		}
96 96
         
97
-    }
98
-
99
-    /**
100
-     * Create a new store command
101
-     *
102
-     * @param  Aggregate $aggregate
103
-     * @return Store
104
-     */
105
-    protected function createStoreCommand(Aggregate $aggregate)
106
-    {
107
-        // We gotta retrieve the corresponding query adapter to use.
108
-        $mapper = $aggregate->getMapper();
109
-
110
-        return new Store($aggregate, $mapper->newQueryBuilder());
111
-    }
112
-
113
-    /**
114
-     * Run all operations that have to occur after the entity
115
-     * is stored.
116
-     *
117
-     * @throws \InvalidArgumentException
118
-     * @return void
119
-     */
120
-    protected function postStoreProcess()
121
-    {
122
-        $aggregate = $this->aggregate;
123
-
124
-        // Create any related object that doesn't exist in the database.
125
-        $foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
126
-
127
-        $this->createRelatedEntities($foreignRelationships);
128
-
129
-        // Update any pivot tables that has been modified.
130
-        $aggregate->updatePivotRecords();
131
-
132
-        // Update any dirty relationship. This include relationships that already exists, have
133
-        // dirty attributes / newly created related entities / dirty related entities.
134
-        $dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
97
+	}
98
+
99
+	/**
100
+	 * Create a new store command
101
+	 *
102
+	 * @param  Aggregate $aggregate
103
+	 * @return Store
104
+	 */
105
+	protected function createStoreCommand(Aggregate $aggregate)
106
+	{
107
+		// We gotta retrieve the corresponding query adapter to use.
108
+		$mapper = $aggregate->getMapper();
109
+
110
+		return new Store($aggregate, $mapper->newQueryBuilder());
111
+	}
112
+
113
+	/**
114
+	 * Run all operations that have to occur after the entity
115
+	 * is stored.
116
+	 *
117
+	 * @throws \InvalidArgumentException
118
+	 * @return void
119
+	 */
120
+	protected function postStoreProcess()
121
+	{
122
+		$aggregate = $this->aggregate;
123
+
124
+		// Create any related object that doesn't exist in the database.
125
+		$foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
126
+
127
+		$this->createRelatedEntities($foreignRelationships);
128
+
129
+		// Update any pivot tables that has been modified.
130
+		$aggregate->updatePivotRecords();
131
+
132
+		// Update any dirty relationship. This include relationships that already exists, have
133
+		// dirty attributes / newly created related entities / dirty related entities.
134
+		$dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
135 135
         
136
-        foreach ($dirtyRelatedAggregates as $related) {
137
-            $this->createStoreCommand($related)->execute();
138
-        }
139
-
140
-        // Now we can sync the related collections
141
-        if ($this->aggregate->exists()) {
142
-            $this->aggregate->syncRelationships($foreignRelationships);
143
-        }
136
+		foreach ($dirtyRelatedAggregates as $related) {
137
+			$this->createStoreCommand($related)->execute();
138
+		}
139
+
140
+		// Now we can sync the related collections
141
+		if ($this->aggregate->exists()) {
142
+			$this->aggregate->syncRelationships($foreignRelationships);
143
+		}
144 144
         
145
-        // TODO be move it to the wrapper class
146
-        // so it's the same code for the entity builder
147
-        $aggregate->setProxies();
148
-
149
-        // Update Entity Cache
150
-        $aggregate->getMapper()->getEntityCache()->refresh($aggregate);
151
-    }
152
-
153
-    /**
154
-     * Update Related Entities which attributes have
155
-     * been modified.
156
-     *
157
-     * @return void
158
-     */
159
-    protected function updateDirtyRelated()
160
-    {
161
-        $relations = $this->entityMap->getRelationships();
162
-        $attributes = $this->getAttributes();
163
-
164
-        foreach ($relations as $relation) {
165
-            if (!array_key_exists($relation, $attributes)) {
166
-                continue;
167
-            }
168
-
169
-            $value = $attributes[$relation];
170
-
171
-            if ($value == null) {
172
-                continue;
173
-            }
174
-
175
-            if ($value instanceof EntityProxy) {
176
-                continue;
177
-            }
178
-
179
-            if ($value instanceof CollectionProxy && $value->isLoaded()) {
180
-                $value = $value->getUnderlyingCollection();
181
-            }
182
-            if ($value instanceof CollectionProxy && !$value->isLoaded()) {
183
-                foreach ($value->getAddedItems() as $entity) {
184
-                    $this->updateEntityIfDirty($entity);
185
-                }
186
-                continue;
187
-            }
188
-
189
-            if ($value instanceof EntityCollection) {
190
-                foreach ($value as $entity) {
191
-                    if (!$this->createEntityIfNotExists($entity)) {
192
-                        $this->updateEntityIfDirty($entity);
193
-                    }
194
-                }
195
-                continue;
196
-            }
197
-            if ($value instanceof Mappable) {
198
-                $this->updateEntityIfDirty($value);
199
-                continue;
200
-            }
201
-        }
202
-    }
203
-
204
-    /**
205
-     * Execute an insert statement on the database
206
-     *
207
-     * @return void
208
-     */
209
-    protected function insert()
210
-    {
211
-        $aggregate = $this->aggregate;
212
-
213
-        $attributes = $aggregate->getRawAttributes();
145
+		// TODO be move it to the wrapper class
146
+		// so it's the same code for the entity builder
147
+		$aggregate->setProxies();
148
+
149
+		// Update Entity Cache
150
+		$aggregate->getMapper()->getEntityCache()->refresh($aggregate);
151
+	}
152
+
153
+	/**
154
+	 * Update Related Entities which attributes have
155
+	 * been modified.
156
+	 *
157
+	 * @return void
158
+	 */
159
+	protected function updateDirtyRelated()
160
+	{
161
+		$relations = $this->entityMap->getRelationships();
162
+		$attributes = $this->getAttributes();
163
+
164
+		foreach ($relations as $relation) {
165
+			if (!array_key_exists($relation, $attributes)) {
166
+				continue;
167
+			}
168
+
169
+			$value = $attributes[$relation];
170
+
171
+			if ($value == null) {
172
+				continue;
173
+			}
174
+
175
+			if ($value instanceof EntityProxy) {
176
+				continue;
177
+			}
178
+
179
+			if ($value instanceof CollectionProxy && $value->isLoaded()) {
180
+				$value = $value->getUnderlyingCollection();
181
+			}
182
+			if ($value instanceof CollectionProxy && !$value->isLoaded()) {
183
+				foreach ($value->getAddedItems() as $entity) {
184
+					$this->updateEntityIfDirty($entity);
185
+				}
186
+				continue;
187
+			}
188
+
189
+			if ($value instanceof EntityCollection) {
190
+				foreach ($value as $entity) {
191
+					if (!$this->createEntityIfNotExists($entity)) {
192
+						$this->updateEntityIfDirty($entity);
193
+					}
194
+				}
195
+				continue;
196
+			}
197
+			if ($value instanceof Mappable) {
198
+				$this->updateEntityIfDirty($value);
199
+				continue;
200
+			}
201
+		}
202
+	}
203
+
204
+	/**
205
+	 * Execute an insert statement on the database
206
+	 *
207
+	 * @return void
208
+	 */
209
+	protected function insert()
210
+	{
211
+		$aggregate = $this->aggregate;
212
+
213
+		$attributes = $aggregate->getRawAttributes();
214 214
         
215
-        $keyName = $aggregate->getEntityMap()->getKeyName();
215
+		$keyName = $aggregate->getEntityMap()->getKeyName();
216 216
 
217
-        // Check if the primary key is defined in the attributes
218
-        if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
219
-            $this->query->insert($attributes);
220
-        } else {
221
-            $sequence = $aggregate->getEntityMap()->getSequence();
217
+		// Check if the primary key is defined in the attributes
218
+		if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
219
+			$this->query->insert($attributes);
220
+		} else {
221
+			$sequence = $aggregate->getEntityMap()->getSequence();
222 222
 
223
-            $id = $this->query->insertGetId($attributes, $sequence);
223
+			$id = $this->query->insertGetId($attributes, $sequence);
224 224
 
225
-            $aggregate->setEntityAttribute($keyName, $id);
226
-        }
225
+			$aggregate->setEntityAttribute($keyName, $id);
226
+		}
227 227
 
228
-    }
228
+	}
229 229
 
230
-    /**
231
-     * Run an update statement on the entity
232
-     *
233
-     * @throws \InvalidArgumentException
234
-     *
235
-     * @return void
236
-     */
237
-    protected function update()
238
-    {
239
-        $query = $this->query;
230
+	/**
231
+	 * Run an update statement on the entity
232
+	 *
233
+	 * @throws \InvalidArgumentException
234
+	 *
235
+	 * @return void
236
+	 */
237
+	protected function update()
238
+	{
239
+		$query = $this->query;
240 240
 
241
-        $keyName = $this->aggregate->getEntityKey();
241
+		$keyName = $this->aggregate->getEntityKey();
242 242
 
243
-        $query = $query->where($keyName, '=', $this->aggregate->getEntityId());
243
+		$query = $query->where($keyName, '=', $this->aggregate->getEntityId());
244 244
 
245
-        $dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
245
+		$dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
246 246
 
247
-        if (count($dirtyAttributes) > 0) {
248
-            $query->update($dirtyAttributes);
249
-        }
250
-    }
247
+		if (count($dirtyAttributes) > 0) {
248
+			$query->update($dirtyAttributes);
249
+		}
250
+	}
251 251
 }
Please login to merge, or discard this patch.
src/MagicGetters.php 1 patch
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -12,48 +12,48 @@
 block discarded – undo
12 12
 	protected $attributes = [];
13 13
 
14 14
 	/**
15
-     * Dynamically retrieve attributes on the entity.
16
-     *
17
-     * @param  string $key
18
-     * @return mixed
19
-     */
20
-    public function __get($key)
21
-    {
22
-    	// When using mixed mapping, we will check
23
-    	// for a class property corresponding to 
24
-    	// the attribute's key first. 
25
-    	// 
26
-    	// Note : this may raise issues as we may grant
27
-    	// access to unwanted properties, like class dependencies. 
28
-    	// 
29
-    	// -> Solution would be to access the entityMap's $attributes, but we
30
-    	// have to do this in a very efficient way.
31
-    	// 
32
-    	// Manager::getEntityMap(get_class($this))->hasProperty()
33
-    	// 
34
-    	// We could do the casting to array / json the same way, and it would 
15
+	 * Dynamically retrieve attributes on the entity.
16
+	 *
17
+	 * @param  string $key
18
+	 * @return mixed
19
+	 */
20
+	public function __get($key)
21
+	{
22
+		// When using mixed mapping, we will check
23
+		// for a class property corresponding to 
24
+		// the attribute's key first. 
25
+		// 
26
+		// Note : this may raise issues as we may grant
27
+		// access to unwanted properties, like class dependencies. 
28
+		// 
29
+		// -> Solution would be to access the entityMap's $attributes, but we
30
+		// have to do this in a very efficient way.
31
+		// 
32
+		// Manager::getEntityMap(get_class($this))->hasProperty()
33
+		// 
34
+		// We could do the casting to array / json the same way, and it would 
35 35
     	
36 36
 
37
-    	if(property_exists($this, $key)) {
38
-    		return $this->$key;
39
-    	}
37
+		if(property_exists($this, $key)) {
38
+			return $this->$key;
39
+		}
40 40
 
41
-    	if(array_key_exists($key, $this->attributes))
42
-    	{
43
-        	return $this->attributes[$key];
44
-        }
41
+		if(array_key_exists($key, $this->attributes))
42
+		{
43
+			return $this->attributes[$key];
44
+		}
45 45
 
46
-        return null;
47
-    }
46
+		return null;
47
+	}
48 48
 
49
-    /**
50
-     * Determine if an attribute exists on the entity.
51
-     *
52
-     * @param  string $key
53
-     * @return bool
54
-     */
55
-    public function __isset($key)
56
-    {
57
-        return array_key_exists($key, $this->attributes) || property_exists($this, $key);
58
-    }
49
+	/**
50
+	 * Determine if an attribute exists on the entity.
51
+	 *
52
+	 * @param  string $key
53
+	 * @return bool
54
+	 */
55
+	public function __isset($key)
56
+	{
57
+		return array_key_exists($key, $this->attributes) || property_exists($this, $key);
58
+	}
59 59
 }
60 60
\ No newline at end of file
Please login to merge, or discard this patch.
src/EntityMap.php 1 patch
Indentation   +1279 added lines, -1279 removed lines patch added patch discarded remove patch
@@ -23,1286 +23,1286 @@
 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
-     * 
58
-     * @var string
59
-     */
60
-    protected $arrayName = 'attributes';
61
-
62
-    /**
63
-     * Array containing the list of database columns to be mapped
64
-     * in the attributes array of the entity. 
65
-     *
66
-     * @var array
67
-     */
68
-    protected $attributes = [];
69
-
70
-    /**
71
-     * Array containing the list of database columns to be mapped
72
-     * to the entity's class properties. 
73
-     *
74
-     * @var array
75
-     */
76
-    protected $properties = [];
77
-
78
-    /**
79
-     * The Custom Domain Class to use with this mapping
80
-     *
81
-     * @var string|null
82
-     */
83
-    protected $class = null;
84
-
85
-    /**
86
-     * Embedded Value Objects
87
-     * 
88
-     * @var array
89
-     */
90
-    protected $embeddables = [];
91
-
92
-    /**
93
-     * Determine the relationships method used on the entity.
94
-     * If not set, mapper will autodetect them
95
-     *
96
-     * @var array
97
-     */
98
-    private $relationships = [];
99
-
100
-    /**
101
-     * Relationships that should be treated as collection.
102
-     *
103
-     * @var array
104
-     */
105
-    private $manyRelations = [];
106
-
107
-    /**
108
-     * Relationships that should be treated as single entity.
109
-     *
110
-     * @var array
111
-     */
112
-    private $singleRelations = [];
113
-
114
-    /**
115
-     * Relationships for which the key is stored in the Entity itself
116
-     *
117
-     * @var array
118
-     */
119
-    private $localRelations = [];
120
-
121
-    /** 
122
-     * List of local keys associated to local relation methods
123
-     * 
124
-     * @var array
125
-     */
126
-    private $localForeignKeys = [];
127
-
128
-    /**
129
-     * Relationships for which the key is stored in the Related Entity
130
-     *
131
-     * @var array
132
-     */
133
-    private $foreignRelations = [];
134
-
135
-    /**
136
-     * Relationships which use a pivot record.
137
-     *
138
-     * @var array
139
-     */
140
-    private $pivotRelations = [];
141
-
142
-    /**
143
-     * Dynamic relationships
144
-     *
145
-     * @var array
146
-     */
147
-    private $dynamicRelationships = [];
148
-
149
-    /**
150
-     * Targetted class for the relationship method. value is set to `null` for
151
-     * polymorphic relations. 
152
-     * 
153
-     * @var array
154
-     */
155
-    private $relatedClasses = [];
156
-
157
-    /** 
158
-     * Some relation methods like embedded objects, or HasOne and MorphOne,
159
-     * will never have a proxy loaded on them. 
160
-     * 
161
-     * @var  array
162
-     */
163
-    private $nonProxyRelationships = [];
164
-
165
-    /**
166
-     * The number of models to return for pagination.
167
-     *
168
-     * @var int
169
-     */
170
-    protected $perPage = 15;
171
-
172
-    /**
173
-     * The relations to eager load on every query.
174
-     *
175
-     * @var array
176
-     */
177
-    protected $with = [];
178
-
179
-    /**
180
-     * The class name to be used in polymorphic relations.
181
-     *
182
-     * @var string
183
-     */
184
-    protected $morphClass;
185
-
186
-    /**
187
-     * Sequence name, to be used with postgreSql
188
-     * defaults to %table_name%_id_seq
189
-     *
190
-     * @var string|null
191
-     */
192
-    protected $sequence = null;
193
-
194
-    /**
195
-     * Indicates if the entity should be timestamped.
196
-     *
197
-     * @var bool
198
-     */
199
-    public $timestamps = false;
200
-
201
-    /**
202
-     * The name of the "created at" column.
203
-     *
204
-     * @var string
205
-     */
206
-    protected $createdAtColumn = 'created_at';
207
-
208
-    /**
209
-     * The name of the "updated at" column.
210
-     *
211
-     * @var string
212
-     */
213
-    protected $updatedAtColumn = 'updated_at';
214
-
215
-    /**
216
-     * Indicates if the entity uses softdeletes
217
-     *
218
-     * @var boolean
219
-     */
220
-    public $softDeletes = false;
221
-
222
-    /**
223
-     * The name of the "deleted at" column.
224
-     *
225
-     * @var string
226
-     */
227
-    protected $deletedAtColumn = 'deleted_at';
228
-
229
-    /**
230
-     * The date format to use with the current database connection
231
-     *
232
-     * @var string
233
-     */
234
-    protected $dateFormat;
235
-
236
-    /**
237
-     * Set this property to true if the entity should be instantiated
238
-     * using the IoC Container
239
-     * 
240
-     * @var boolean
241
-     */
242
-    protected $dependencyInjection = false;
243
-
244
-    /**
245
-     * Set the usage of inheritance, possible values are :
246
-     * "single_table"
247
-     * null
248
-     * 
249
-     * @var string | null
250
-     */
251
-    protected $inheritanceType = null;
252
-
253
-    /**
254
-     * Discriminator column name
255
-     * 
256
-     * @var string
257
-     */
258
-    protected $discriminatorColumn = "type";
259
-
260
-    /**
261
-     * Allow using a string to define which entity type should be instantiated.
262
-     * If not set, analogue will uses entity's FQDN
263
-     * 
264
-     * @var array
265
-     */
266
-    protected $discriminatorColumnMap = [];
267
-
268
-    /**
269
-     * Return Domain class attributes, useful when mapping to a Plain PHP Object
270
-     *
271
-     * @return array
272
-     */
273
-    public function getAttributes()
274
-    {
275
-        return $this->attributes;
276
-    }
277
-
278
-    /**
279
-     * Set the domain class attributes
280
-     *
281
-     * @param array $attributeNames
282
-     */
283
-    public function setAttributes(array $attributeNames)
284
-    {
285
-        $this->attributes = $attributeNames;
286
-    }
287
-
288
-    /**
289
-     * Get all the attribute names for the class, including relationships, embeddables and primary key.
290
-     *
291
-     * @return array
292
-     */
293
-    public function getCompiledAttributes()
294
-    {
295
-        $key = $this->getKeyName();
296
-
297
-        $embeddables = array_keys($this->getEmbeddables());
298
-
299
-        $relationships = $this->getRelationships();
300
-
301
-        $attributes = $this->getAttributes();
302
-
303
-        return array_merge([$key], $embeddables, $relationships, $attributes);
304
-    }
305
-
306
-    /**
307
-     * Set the date format to use with the current database connection
308
-     *
309
-     * @param string $format
310
-     */
311
-    public function setDateFormat($format)
312
-    {
313
-        $this->dateFormat = $format;
314
-    }
315
-
316
-    /**
317
-     * Get the date format to use with the current database connection
318
-     *
319
-     *  @return string
320
-     */
321
-    public function getDateFormat()
322
-    {
323
-        return $this->dateFormat;
324
-    }
325
-
326
-    /**
327
-     * Set the Driver for this mapping
328
-     *
329
-     * @param string $driver
330
-     */
331
-    public function setDriver($driver)
332
-    {
333
-        $this->driver = $driver;
334
-    }
335
-
336
-    /**
337
-     * Get the Driver for this mapping.
338
-     *
339
-     * @return string
340
-     */
341
-    public function getDriver()
342
-    {
343
-        return $this->driver;
344
-    }
345
-
346
-    /**
347
-     * Set the db connection to use on the table
348
-     *
349
-     * @param $connection
350
-     */
351
-    public function setConnection($connection)
352
-    {
353
-        $this->connection = $connection;
354
-    }
355
-
356
-    /**
357
-     * Get the Database connection the Entity is stored on.
358
-     *
359
-     * @return string
360
-     */
361
-    public function getConnection()
362
-    {
363
-        return $this->connection;
364
-    }
365
-
366
-    /**
367
-     * Get the table associated with the entity.
368
-     *
369
-     * @return string
370
-     */
371
-    public function getTable()
372
-    {
373
-        if (!is_null($this->table)) {
374
-            return $this->table;
375
-        }
376
-
377
-        return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
378
-    }
379
-
380
-    /**
381
-     * Set the database table name
382
-     *
383
-     * @param  string $table
384
-     */
385
-    public function setTable($table)
386
-    {
387
-        $this->table = $table;
388
-    }
389
-
390
-    /**
391
-     * Get the pgSql sequence name
392
-     *
393
-     * @return string
394
-     */
395
-    public function getSequence()
396
-    {
397
-        if (!is_null($this->sequence)) {
398
-            return $this->sequence;
399
-        } else {
400
-            return $this->getTable() . '_id_seq';
401
-        }
402
-    }
403
-
404
-    /**
405
-     * Get the custom entity class
406
-     *
407
-     * @return string namespaced class name
408
-     */
409
-    public function getClass()
410
-    {
411
-        return isset($this->class) ? $this->class : null;
412
-    }
413
-
414
-    /**
415
-     * Set the custom entity class
416
-     *
417
-     * @param string $class namespaced class name
418
-     */
419
-    public function setClass($class)
420
-    {
421
-        $this->class = $class;
422
-    }
423
-
424
-    /**
425
-     * Get the embedded Value Objects
426
-     *
427
-     * @return array
428
-     */
429
-    public function getEmbeddables()
430
-    {
431
-        return $this->embeddables;
432
-    }
433
-
434
-    /**
435
-     * Set the embedded Value Objects
436
-     *
437
-     * @param array $embeddables
438
-     */
439
-    public function setEmbeddables(array $embeddables)
440
-    {
441
-        $this->embeddables = $embeddables;
442
-    }
443
-
444
-    /**
445
-     * Get the relationships to map on a custom domain
446
-     * class.
447
-     *
448
-     * @return array
449
-     */
450
-    public function getRelationships()
451
-    {
452
-        return $this->relationships;
453
-    }
454
-
455
-    /**  
456
-     * Get the relationships that will not have a proxy
457
-     * set on them
458
-     * 
459
-     * @return array
460
-     */
461
-    public function getRelationshipsWithoutProxy()
462
-    {
463
-        return $this->nonProxyRelationships;
464
-    }
465
-
466
-    /**
467
-     * Relationships of the Entity type
468
-     *
469
-     * @return array
470
-     */
471
-    public function getSingleRelationships()
472
-    {
473
-        return $this->singleRelations;
474
-    }
475
-
476
-    /**
477
-     * Relationships of type Collection
478
-     *
479
-     * @return array
480
-     */
481
-    public function getManyRelationships()
482
-    {
483
-        return $this->manyRelations;
484
-    }
485
-
486
-    /**
487
-     * Relationships with foreign key in the mapped entity record.
488
-     *
489
-     * @return array
490
-     */
491
-    public function getLocalRelationships()
492
-    {
493
-        return $this->localRelations;
494
-    }
495
-
496
-    /**  
497
-     * Return the local keys associated to the relationship
498
-     * 
499
-     * @param  string $relation
500
-     * @return string | array | null
501
-     */
502
-    public function getLocalKeys($relation)
503
-    {
504
-        return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
505
-    }
506
-
507
-    /**
508
-     * Relationships with foreign key in the related Entity record
509
-     *
510
-     * @return array
511
-     */
512
-    public function getForeignRelationships()
513
-    {
514
-        return $this->foreignRelations;
515
-    }
516
-
517
-    /**
518
-     * Relationships which keys are stored in a pivot record
519
-     *
520
-     * @return array
521
-     */
522
-    public function getPivotRelationships()
523
-    {
524
-        return $this->pivotRelations;
525
-    }
526
-
527
-    /**  
528
-     * Get the targetted type for a relationship. Return null if polymorphic
529
-     * 
530
-     * @param  string  $relation
531
-     * @return string | null
532
-     */
533
-    public function getTargettedClass($relation)
534
-    {
535
-        return $this->relatedClasses[$relation];
536
-    }
537
-
538
-    /**
539
-     * Add a Dynamic Relationship method at runtime. This has to be done
540
-     * by hooking the 'initializing' event, before entityMap is initialized.
541
-     *
542
-     * @param string  $name         Relation name
543
-     * @param \Closure $relationship
544
-     *
545
-     * @return void
546
-     */
547
-    public function addRelationshipMethod($name, \Closure $relationship)
548
-    {
549
-        $this->dynamicRelationships[$name] = $relationship;
550
-    }
551
-
552
-    /**
553
-     * Get the dynamic relationship method names.
554
-     *
555
-     * @return array
556
-     */
557
-    public function getDynamicRelationships()
558
-    {
559
-        return array_keys($this->dynamicRelationships);
560
-    }
561
-
562
-    /**
563
-     * Get the relationships that have to be eager loaded
564
-     * on each request.
565
-     *
566
-     * @return array
567
-     */
568
-    public function getEagerloadedRelationships()
569
-    {
570
-        return $this->with;
571
-    }
572
-
573
-    /**
574
-     * Get the primary key for the entity.
575
-     *
576
-     * @return string
577
-     */
578
-    public function getKeyName()
579
-    {
580
-        return $this->primaryKey;
581
-    }
582
-
583
-    /**
584
-     * Set the primary key for the entity.
585
-     *
586
-     * @param $key
587
-     * @return void
588
-     */
589
-    public function setKeyName($key)
590
-    {
591
-        $this->primaryKey = $key;
592
-    }
593
-
594
-    /**
595
-     * Get the table qualified key name.
596
-     *
597
-     * @return string
598
-     */
599
-    public function getQualifiedKeyName()
600
-    {
601
-        return $this->getTable() . '.' . $this->getKeyName();
602
-    }
603
-
604
-    /**
605
-     * Get the number of models to return per page.
606
-     *
607
-     * @return int
608
-     */
609
-    public function getPerPage()
610
-    {
611
-        return $this->perPage;
612
-    }
613
-
614
-    /**
615
-     * Set the number of models to return per page.
616
-     *
617
-     * @param  int $perPage
618
-     * @return void
619
-     */
620
-    public function setPerPage($perPage)
621
-    {
622
-        $this->perPage = $perPage;
623
-    }
624
-
625
-    /**
626
-     * Determine if the entity uses get.
627
-     *
628
-     * @return bool
629
-     */
630
-    public function usesTimestamps()
631
-    {
632
-        return $this->timestamps;
633
-    }
634
-
635
-    /**
636
-     * Determine if the entity uses soft deletes
637
-     *
638
-     * @return bool
639
-     */
640
-    public function usesSoftDeletes()
641
-    {
642
-        return $this->softDeletes;
643
-    }
644
-
645
-    /**
646
-     * Get the 'created_at' column name
647
-     *
648
-     * @return string
649
-     */
650
-    public function getCreatedAtColumn()
651
-    {
652
-        return $this->createdAtColumn;
653
-    }
654
-
655
-    /**
656
-     * Get the 'updated_at' column name
657
-     *
658
-     * @return string
659
-     */
660
-    public function getUpdatedAtColumn()
661
-    {
662
-        return $this->updatedAtColumn;
663
-    }
664
-
665
-    /**
666
-     * Get the deleted_at column
667
-     *
668
-     * @return string
669
-     */
670
-    public function getQualifiedDeletedAtColumn()
671
-    {
672
-        return $this->deletedAtColumn;
673
-    }
674
-
675
-    /**
676
-     * Get the default foreign key name for the model.
677
-     *
678
-     * @return string
679
-     */
680
-    public function getForeignKey()
681
-    {
682
-        return snake_case(class_basename($this->getClass())) . '_id';
683
-    }
684
-
685
-    /**
686
-     * Return the inheritance type used by the entity.
687
-     *
688
-     * @return string|null
689
-     */
690
-    public function getInheritanceType()
691
-    {
692
-        return $this->inheritanceType;
693
-    }
694
-
695
-    /**
696
-     * Return the discriminator column name on the entity that's
697
-     * used for table inheritance.
698
-     *
699
-     * @return string
700
-     */
701
-    public function getDiscriminatorColumn()
702
-    {
703
-        return $this->discriminatorColumn;
704
-    }
705
-
706
-    /**
707
-     * Return the mapping of discriminator column values to
708
-     * entity class names that are used for table inheritance.
709
-     *
710
-     * @return array
711
-     */
712
-    public function getDiscriminatorColumnMap()
713
-    {
714
-        return $this->discriminatorColumnMap;
715
-    }
716
-
717
-    /**
718
-     * Return true if the entity should be instanciated using
719
-     * the IoC Container
720
-     * 
721
-     * @return boolean
722
-     */
723
-    public function useDependencyInjection()
724
-    {
725
-        return $this->dependencyInjection;
726
-    }
727
-
728
-    /**
729
-     * Define a one-to-one relationship.
730
-     *
731
-     * @param  mixed  $entity
732
-     * @param  string $related entity class
733
-     * @param  string $foreignKey
734
-     * @param  string $localKey
735
-     * @throws MappingException
736
-     * @return \Analogue\ORM\Relationships\HasOne
737
-     */
738
-    public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
739
-    {
740
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
741
-
742
-        $relatedMapper = Manager::getInstance()->mapper($related);
743
-
744
-        $relatedMap = $relatedMapper->getEntityMap();
745
-
746
-        $localKey = $localKey ?: $this->getKeyName();
747
-
748
-        // Add the relation to the definition in map
749
-        list(, $caller) = debug_backtrace(false);
750
-        $relation = $caller['function'];
751
-        $this->relatedClasses[$relation] = $related;
752
-        $this->singleRelations[] = $relation;
753
-        $this->foreignRelations[] = $relation;
754
-        $this->nonProxyRelationships[] = $relation;
755
-
756
-        // This relationship will always be eager loaded, as proxying it would
757
-        // mean having an object that doesn't actually exists.
758
-        if(! in_array($relation, $this->with)) {
759
-            $this->with[] = $relation;
760
-        }
761
-
762
-        return new HasOne($relatedMapper, $entity, $relatedMap->getTable() . '.' . $foreignKey, $localKey);
763
-    }
764
-
765
-    /**
766
-     * Define a polymorphic one-to-one relationship.
767
-     *
768
-     * @param  mixed       $entity
769
-     * @param  string      $related
770
-     * @param  string      $name
771
-     * @param  string|null $type
772
-     * @param  string|null $id
773
-     * @param  string|null $localKey
774
-     * @throws MappingException
775
-     * @return \Analogue\ORM\Relationships\MorphOne
776
-     */
777
-    public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
778
-    {
779
-        list($type, $id) = $this->getMorphs($name, $type, $id);
780
-
781
-        $localKey = $localKey ?: $this->getKeyName();
782
-
783
-        $relatedMapper = Manager::getInstance()->mapper($related);
784
-
785
-        $table = $relatedMapper->getEntityMap()->getTable();
786
-
787
-        // Add the relation to the definition in map
788
-        list(, $caller) = debug_backtrace(false);
789
-        $relation = $caller['function'];
790
-        $this->relatedClasses[$relation] = $related;
791
-        $this->singleRelations[] = $relation;
792
-        $this->foreignRelations[] = $relation;
793
-        $this->nonProxyRelationships[] = $relation;
794
-
795
-        // This relationship will always be eager loaded, as proxying it would
796
-        // mean having an object that doesn't actually exists.
797
-        if(! in_array($relation, $this->with)) {
798
-            $this->with[] = $relation;
799
-        }
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
+	 * 
58
+	 * @var string
59
+	 */
60
+	protected $arrayName = 'attributes';
61
+
62
+	/**
63
+	 * Array containing the list of database columns to be mapped
64
+	 * in the attributes array of the entity. 
65
+	 *
66
+	 * @var array
67
+	 */
68
+	protected $attributes = [];
69
+
70
+	/**
71
+	 * Array containing the list of database columns to be mapped
72
+	 * to the entity's class properties. 
73
+	 *
74
+	 * @var array
75
+	 */
76
+	protected $properties = [];
77
+
78
+	/**
79
+	 * The Custom Domain Class to use with this mapping
80
+	 *
81
+	 * @var string|null
82
+	 */
83
+	protected $class = null;
84
+
85
+	/**
86
+	 * Embedded Value Objects
87
+	 * 
88
+	 * @var array
89
+	 */
90
+	protected $embeddables = [];
91
+
92
+	/**
93
+	 * Determine the relationships method used on the entity.
94
+	 * If not set, mapper will autodetect them
95
+	 *
96
+	 * @var array
97
+	 */
98
+	private $relationships = [];
99
+
100
+	/**
101
+	 * Relationships that should be treated as collection.
102
+	 *
103
+	 * @var array
104
+	 */
105
+	private $manyRelations = [];
106
+
107
+	/**
108
+	 * Relationships that should be treated as single entity.
109
+	 *
110
+	 * @var array
111
+	 */
112
+	private $singleRelations = [];
113
+
114
+	/**
115
+	 * Relationships for which the key is stored in the Entity itself
116
+	 *
117
+	 * @var array
118
+	 */
119
+	private $localRelations = [];
120
+
121
+	/** 
122
+	 * List of local keys associated to local relation methods
123
+	 * 
124
+	 * @var array
125
+	 */
126
+	private $localForeignKeys = [];
127
+
128
+	/**
129
+	 * Relationships for which the key is stored in the Related Entity
130
+	 *
131
+	 * @var array
132
+	 */
133
+	private $foreignRelations = [];
134
+
135
+	/**
136
+	 * Relationships which use a pivot record.
137
+	 *
138
+	 * @var array
139
+	 */
140
+	private $pivotRelations = [];
141
+
142
+	/**
143
+	 * Dynamic relationships
144
+	 *
145
+	 * @var array
146
+	 */
147
+	private $dynamicRelationships = [];
148
+
149
+	/**
150
+	 * Targetted class for the relationship method. value is set to `null` for
151
+	 * polymorphic relations. 
152
+	 * 
153
+	 * @var array
154
+	 */
155
+	private $relatedClasses = [];
156
+
157
+	/** 
158
+	 * Some relation methods like embedded objects, or HasOne and MorphOne,
159
+	 * will never have a proxy loaded on them. 
160
+	 * 
161
+	 * @var  array
162
+	 */
163
+	private $nonProxyRelationships = [];
164
+
165
+	/**
166
+	 * The number of models to return for pagination.
167
+	 *
168
+	 * @var int
169
+	 */
170
+	protected $perPage = 15;
171
+
172
+	/**
173
+	 * The relations to eager load on every query.
174
+	 *
175
+	 * @var array
176
+	 */
177
+	protected $with = [];
178
+
179
+	/**
180
+	 * The class name to be used in polymorphic relations.
181
+	 *
182
+	 * @var string
183
+	 */
184
+	protected $morphClass;
185
+
186
+	/**
187
+	 * Sequence name, to be used with postgreSql
188
+	 * defaults to %table_name%_id_seq
189
+	 *
190
+	 * @var string|null
191
+	 */
192
+	protected $sequence = null;
193
+
194
+	/**
195
+	 * Indicates if the entity should be timestamped.
196
+	 *
197
+	 * @var bool
198
+	 */
199
+	public $timestamps = false;
200
+
201
+	/**
202
+	 * The name of the "created at" column.
203
+	 *
204
+	 * @var string
205
+	 */
206
+	protected $createdAtColumn = 'created_at';
207
+
208
+	/**
209
+	 * The name of the "updated at" column.
210
+	 *
211
+	 * @var string
212
+	 */
213
+	protected $updatedAtColumn = 'updated_at';
214
+
215
+	/**
216
+	 * Indicates if the entity uses softdeletes
217
+	 *
218
+	 * @var boolean
219
+	 */
220
+	public $softDeletes = false;
221
+
222
+	/**
223
+	 * The name of the "deleted at" column.
224
+	 *
225
+	 * @var string
226
+	 */
227
+	protected $deletedAtColumn = 'deleted_at';
228
+
229
+	/**
230
+	 * The date format to use with the current database connection
231
+	 *
232
+	 * @var string
233
+	 */
234
+	protected $dateFormat;
235
+
236
+	/**
237
+	 * Set this property to true if the entity should be instantiated
238
+	 * using the IoC Container
239
+	 * 
240
+	 * @var boolean
241
+	 */
242
+	protected $dependencyInjection = false;
243
+
244
+	/**
245
+	 * Set the usage of inheritance, possible values are :
246
+	 * "single_table"
247
+	 * null
248
+	 * 
249
+	 * @var string | null
250
+	 */
251
+	protected $inheritanceType = null;
252
+
253
+	/**
254
+	 * Discriminator column name
255
+	 * 
256
+	 * @var string
257
+	 */
258
+	protected $discriminatorColumn = "type";
259
+
260
+	/**
261
+	 * Allow using a string to define which entity type should be instantiated.
262
+	 * If not set, analogue will uses entity's FQDN
263
+	 * 
264
+	 * @var array
265
+	 */
266
+	protected $discriminatorColumnMap = [];
267
+
268
+	/**
269
+	 * Return Domain class attributes, useful when mapping to a Plain PHP Object
270
+	 *
271
+	 * @return array
272
+	 */
273
+	public function getAttributes()
274
+	{
275
+		return $this->attributes;
276
+	}
277
+
278
+	/**
279
+	 * Set the domain class attributes
280
+	 *
281
+	 * @param array $attributeNames
282
+	 */
283
+	public function setAttributes(array $attributeNames)
284
+	{
285
+		$this->attributes = $attributeNames;
286
+	}
287
+
288
+	/**
289
+	 * Get all the attribute names for the class, including relationships, embeddables and primary key.
290
+	 *
291
+	 * @return array
292
+	 */
293
+	public function getCompiledAttributes()
294
+	{
295
+		$key = $this->getKeyName();
296
+
297
+		$embeddables = array_keys($this->getEmbeddables());
298
+
299
+		$relationships = $this->getRelationships();
300
+
301
+		$attributes = $this->getAttributes();
302
+
303
+		return array_merge([$key], $embeddables, $relationships, $attributes);
304
+	}
305
+
306
+	/**
307
+	 * Set the date format to use with the current database connection
308
+	 *
309
+	 * @param string $format
310
+	 */
311
+	public function setDateFormat($format)
312
+	{
313
+		$this->dateFormat = $format;
314
+	}
315
+
316
+	/**
317
+	 * Get the date format to use with the current database connection
318
+	 *
319
+	 *  @return string
320
+	 */
321
+	public function getDateFormat()
322
+	{
323
+		return $this->dateFormat;
324
+	}
325
+
326
+	/**
327
+	 * Set the Driver for this mapping
328
+	 *
329
+	 * @param string $driver
330
+	 */
331
+	public function setDriver($driver)
332
+	{
333
+		$this->driver = $driver;
334
+	}
335
+
336
+	/**
337
+	 * Get the Driver for this mapping.
338
+	 *
339
+	 * @return string
340
+	 */
341
+	public function getDriver()
342
+	{
343
+		return $this->driver;
344
+	}
345
+
346
+	/**
347
+	 * Set the db connection to use on the table
348
+	 *
349
+	 * @param $connection
350
+	 */
351
+	public function setConnection($connection)
352
+	{
353
+		$this->connection = $connection;
354
+	}
355
+
356
+	/**
357
+	 * Get the Database connection the Entity is stored on.
358
+	 *
359
+	 * @return string
360
+	 */
361
+	public function getConnection()
362
+	{
363
+		return $this->connection;
364
+	}
365
+
366
+	/**
367
+	 * Get the table associated with the entity.
368
+	 *
369
+	 * @return string
370
+	 */
371
+	public function getTable()
372
+	{
373
+		if (!is_null($this->table)) {
374
+			return $this->table;
375
+		}
376
+
377
+		return str_replace('\\', '', snake_case(str_plural(class_basename($this->getClass()))));
378
+	}
379
+
380
+	/**
381
+	 * Set the database table name
382
+	 *
383
+	 * @param  string $table
384
+	 */
385
+	public function setTable($table)
386
+	{
387
+		$this->table = $table;
388
+	}
389
+
390
+	/**
391
+	 * Get the pgSql sequence name
392
+	 *
393
+	 * @return string
394
+	 */
395
+	public function getSequence()
396
+	{
397
+		if (!is_null($this->sequence)) {
398
+			return $this->sequence;
399
+		} else {
400
+			return $this->getTable() . '_id_seq';
401
+		}
402
+	}
403
+
404
+	/**
405
+	 * Get the custom entity class
406
+	 *
407
+	 * @return string namespaced class name
408
+	 */
409
+	public function getClass()
410
+	{
411
+		return isset($this->class) ? $this->class : null;
412
+	}
413
+
414
+	/**
415
+	 * Set the custom entity class
416
+	 *
417
+	 * @param string $class namespaced class name
418
+	 */
419
+	public function setClass($class)
420
+	{
421
+		$this->class = $class;
422
+	}
423
+
424
+	/**
425
+	 * Get the embedded Value Objects
426
+	 *
427
+	 * @return array
428
+	 */
429
+	public function getEmbeddables()
430
+	{
431
+		return $this->embeddables;
432
+	}
433
+
434
+	/**
435
+	 * Set the embedded Value Objects
436
+	 *
437
+	 * @param array $embeddables
438
+	 */
439
+	public function setEmbeddables(array $embeddables)
440
+	{
441
+		$this->embeddables = $embeddables;
442
+	}
443
+
444
+	/**
445
+	 * Get the relationships to map on a custom domain
446
+	 * class.
447
+	 *
448
+	 * @return array
449
+	 */
450
+	public function getRelationships()
451
+	{
452
+		return $this->relationships;
453
+	}
454
+
455
+	/**  
456
+	 * Get the relationships that will not have a proxy
457
+	 * set on them
458
+	 * 
459
+	 * @return array
460
+	 */
461
+	public function getRelationshipsWithoutProxy()
462
+	{
463
+		return $this->nonProxyRelationships;
464
+	}
465
+
466
+	/**
467
+	 * Relationships of the Entity type
468
+	 *
469
+	 * @return array
470
+	 */
471
+	public function getSingleRelationships()
472
+	{
473
+		return $this->singleRelations;
474
+	}
475
+
476
+	/**
477
+	 * Relationships of type Collection
478
+	 *
479
+	 * @return array
480
+	 */
481
+	public function getManyRelationships()
482
+	{
483
+		return $this->manyRelations;
484
+	}
485
+
486
+	/**
487
+	 * Relationships with foreign key in the mapped entity record.
488
+	 *
489
+	 * @return array
490
+	 */
491
+	public function getLocalRelationships()
492
+	{
493
+		return $this->localRelations;
494
+	}
495
+
496
+	/**  
497
+	 * Return the local keys associated to the relationship
498
+	 * 
499
+	 * @param  string $relation
500
+	 * @return string | array | null
501
+	 */
502
+	public function getLocalKeys($relation)
503
+	{
504
+		return isset($this->localForeignKeys[$relation]) ? $this->localForeignKeys[$relation] : null;
505
+	}
506
+
507
+	/**
508
+	 * Relationships with foreign key in the related Entity record
509
+	 *
510
+	 * @return array
511
+	 */
512
+	public function getForeignRelationships()
513
+	{
514
+		return $this->foreignRelations;
515
+	}
516
+
517
+	/**
518
+	 * Relationships which keys are stored in a pivot record
519
+	 *
520
+	 * @return array
521
+	 */
522
+	public function getPivotRelationships()
523
+	{
524
+		return $this->pivotRelations;
525
+	}
526
+
527
+	/**  
528
+	 * Get the targetted type for a relationship. Return null if polymorphic
529
+	 * 
530
+	 * @param  string  $relation
531
+	 * @return string | null
532
+	 */
533
+	public function getTargettedClass($relation)
534
+	{
535
+		return $this->relatedClasses[$relation];
536
+	}
537
+
538
+	/**
539
+	 * Add a Dynamic Relationship method at runtime. This has to be done
540
+	 * by hooking the 'initializing' event, before entityMap is initialized.
541
+	 *
542
+	 * @param string  $name         Relation name
543
+	 * @param \Closure $relationship
544
+	 *
545
+	 * @return void
546
+	 */
547
+	public function addRelationshipMethod($name, \Closure $relationship)
548
+	{
549
+		$this->dynamicRelationships[$name] = $relationship;
550
+	}
551
+
552
+	/**
553
+	 * Get the dynamic relationship method names.
554
+	 *
555
+	 * @return array
556
+	 */
557
+	public function getDynamicRelationships()
558
+	{
559
+		return array_keys($this->dynamicRelationships);
560
+	}
561
+
562
+	/**
563
+	 * Get the relationships that have to be eager loaded
564
+	 * on each request.
565
+	 *
566
+	 * @return array
567
+	 */
568
+	public function getEagerloadedRelationships()
569
+	{
570
+		return $this->with;
571
+	}
572
+
573
+	/**
574
+	 * Get the primary key for the entity.
575
+	 *
576
+	 * @return string
577
+	 */
578
+	public function getKeyName()
579
+	{
580
+		return $this->primaryKey;
581
+	}
582
+
583
+	/**
584
+	 * Set the primary key for the entity.
585
+	 *
586
+	 * @param $key
587
+	 * @return void
588
+	 */
589
+	public function setKeyName($key)
590
+	{
591
+		$this->primaryKey = $key;
592
+	}
593
+
594
+	/**
595
+	 * Get the table qualified key name.
596
+	 *
597
+	 * @return string
598
+	 */
599
+	public function getQualifiedKeyName()
600
+	{
601
+		return $this->getTable() . '.' . $this->getKeyName();
602
+	}
603
+
604
+	/**
605
+	 * Get the number of models to return per page.
606
+	 *
607
+	 * @return int
608
+	 */
609
+	public function getPerPage()
610
+	{
611
+		return $this->perPage;
612
+	}
613
+
614
+	/**
615
+	 * Set the number of models to return per page.
616
+	 *
617
+	 * @param  int $perPage
618
+	 * @return void
619
+	 */
620
+	public function setPerPage($perPage)
621
+	{
622
+		$this->perPage = $perPage;
623
+	}
624
+
625
+	/**
626
+	 * Determine if the entity uses get.
627
+	 *
628
+	 * @return bool
629
+	 */
630
+	public function usesTimestamps()
631
+	{
632
+		return $this->timestamps;
633
+	}
634
+
635
+	/**
636
+	 * Determine if the entity uses soft deletes
637
+	 *
638
+	 * @return bool
639
+	 */
640
+	public function usesSoftDeletes()
641
+	{
642
+		return $this->softDeletes;
643
+	}
644
+
645
+	/**
646
+	 * Get the 'created_at' column name
647
+	 *
648
+	 * @return string
649
+	 */
650
+	public function getCreatedAtColumn()
651
+	{
652
+		return $this->createdAtColumn;
653
+	}
654
+
655
+	/**
656
+	 * Get the 'updated_at' column name
657
+	 *
658
+	 * @return string
659
+	 */
660
+	public function getUpdatedAtColumn()
661
+	{
662
+		return $this->updatedAtColumn;
663
+	}
664
+
665
+	/**
666
+	 * Get the deleted_at column
667
+	 *
668
+	 * @return string
669
+	 */
670
+	public function getQualifiedDeletedAtColumn()
671
+	{
672
+		return $this->deletedAtColumn;
673
+	}
674
+
675
+	/**
676
+	 * Get the default foreign key name for the model.
677
+	 *
678
+	 * @return string
679
+	 */
680
+	public function getForeignKey()
681
+	{
682
+		return snake_case(class_basename($this->getClass())) . '_id';
683
+	}
684
+
685
+	/**
686
+	 * Return the inheritance type used by the entity.
687
+	 *
688
+	 * @return string|null
689
+	 */
690
+	public function getInheritanceType()
691
+	{
692
+		return $this->inheritanceType;
693
+	}
694
+
695
+	/**
696
+	 * Return the discriminator column name on the entity that's
697
+	 * used for table inheritance.
698
+	 *
699
+	 * @return string
700
+	 */
701
+	public function getDiscriminatorColumn()
702
+	{
703
+		return $this->discriminatorColumn;
704
+	}
705
+
706
+	/**
707
+	 * Return the mapping of discriminator column values to
708
+	 * entity class names that are used for table inheritance.
709
+	 *
710
+	 * @return array
711
+	 */
712
+	public function getDiscriminatorColumnMap()
713
+	{
714
+		return $this->discriminatorColumnMap;
715
+	}
716
+
717
+	/**
718
+	 * Return true if the entity should be instanciated using
719
+	 * the IoC Container
720
+	 * 
721
+	 * @return boolean
722
+	 */
723
+	public function useDependencyInjection()
724
+	{
725
+		return $this->dependencyInjection;
726
+	}
727
+
728
+	/**
729
+	 * Define a one-to-one relationship.
730
+	 *
731
+	 * @param  mixed  $entity
732
+	 * @param  string $related entity class
733
+	 * @param  string $foreignKey
734
+	 * @param  string $localKey
735
+	 * @throws MappingException
736
+	 * @return \Analogue\ORM\Relationships\HasOne
737
+	 */
738
+	public function hasOne($entity, $related, $foreignKey = null, $localKey = null)
739
+	{
740
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
741
+
742
+		$relatedMapper = Manager::getInstance()->mapper($related);
743
+
744
+		$relatedMap = $relatedMapper->getEntityMap();
745
+
746
+		$localKey = $localKey ?: $this->getKeyName();
747
+
748
+		// Add the relation to the definition in map
749
+		list(, $caller) = debug_backtrace(false);
750
+		$relation = $caller['function'];
751
+		$this->relatedClasses[$relation] = $related;
752
+		$this->singleRelations[] = $relation;
753
+		$this->foreignRelations[] = $relation;
754
+		$this->nonProxyRelationships[] = $relation;
755
+
756
+		// This relationship will always be eager loaded, as proxying it would
757
+		// mean having an object that doesn't actually exists.
758
+		if(! in_array($relation, $this->with)) {
759
+			$this->with[] = $relation;
760
+		}
761
+
762
+		return new HasOne($relatedMapper, $entity, $relatedMap->getTable() . '.' . $foreignKey, $localKey);
763
+	}
764
+
765
+	/**
766
+	 * Define a polymorphic one-to-one relationship.
767
+	 *
768
+	 * @param  mixed       $entity
769
+	 * @param  string      $related
770
+	 * @param  string      $name
771
+	 * @param  string|null $type
772
+	 * @param  string|null $id
773
+	 * @param  string|null $localKey
774
+	 * @throws MappingException
775
+	 * @return \Analogue\ORM\Relationships\MorphOne
776
+	 */
777
+	public function morphOne($entity, $related, $name, $type = null, $id = null, $localKey = null)
778
+	{
779
+		list($type, $id) = $this->getMorphs($name, $type, $id);
780
+
781
+		$localKey = $localKey ?: $this->getKeyName();
782
+
783
+		$relatedMapper = Manager::getInstance()->mapper($related);
784
+
785
+		$table = $relatedMapper->getEntityMap()->getTable();
786
+
787
+		// Add the relation to the definition in map
788
+		list(, $caller) = debug_backtrace(false);
789
+		$relation = $caller['function'];
790
+		$this->relatedClasses[$relation] = $related;
791
+		$this->singleRelations[] = $relation;
792
+		$this->foreignRelations[] = $relation;
793
+		$this->nonProxyRelationships[] = $relation;
794
+
795
+		// This relationship will always be eager loaded, as proxying it would
796
+		// mean having an object that doesn't actually exists.
797
+		if(! in_array($relation, $this->with)) {
798
+			$this->with[] = $relation;
799
+		}
800 800
         
801
-        return new MorphOne($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
802
-    }
803
-
804
-    /**
805
-     * Define an inverse one-to-one or many relationship.
806
-     *
807
-     * @param  mixed       $entity
808
-     * @param  string      $related
809
-     * @param  string|null $foreignKey
810
-     * @param  string|null $otherKey
811
-     * @param  string|null $relation
812
-     * @throws MappingException
813
-     * @return \Analogue\ORM\Relationships\BelongsTo
814
-     */
815
-    public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
816
-    {
817
-        // Add the relation to the definition in map
818
-        list(, $caller) = debug_backtrace(false);
819
-        $relation = $caller['function'];
820
-        $this->relatedClasses[$relation] = $related;
821
-        $this->singleRelations[] = $relation;
822
-        $this->localRelations[] = $relation;
823
-
824
-        // If no foreign key was supplied, we can use a backtrace to guess the proper
825
-        // foreign key name by using the name of the relationship function, which
826
-        // when combined with an "_id" should conventionally match the columns.
827
-        if (is_null($foreignKey)) {
828
-            $foreignKey = snake_case($relation) . '_id';
829
-        }
830
-
831
-        $this->localForeignKeys[$relation] = $foreignKey;
832
-
833
-        $relatedMapper = Manager::getInstance()->mapper($related);
834
-
835
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
836
-
837
-        return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
838
-    }
839
-
840
-    /**
841
-     * Define a polymorphic, inverse one-to-one or many relationship.
842
-     *
843
-     * @param  mixed       $entity
844
-     * @param  string|null $name
845
-     * @param  string|null $type
846
-     * @param  string|null $id
847
-     * @throws MappingException
848
-     * @return \Analogue\ORM\Relationships\MorphTo
849
-     */
850
-    public function morphTo($entity, $name = null, $type = null, $id = null)
851
-    {
852
-        // If no name is provided, we will use the backtrace to get the function name
853
-        // since that is most likely the name of the polymorphic interface. We can
854
-        // use that to get both the class and foreign key that will be utilized.
855
-        if (is_null($name)) {
856
-            list(, $caller) = debug_backtrace(false);
857
-
858
-            $name = snake_case($caller['function']);
859
-        }
860
-        $this->singleRelations[] = $name;
861
-        $this->localRelations[] = $relation;
862
-        $this->foreignRelations[] = $relation;
863
-        $this->relatedClass[$relation] = null;
864
-
865
-        list($type, $id) = $this->getMorphs($name, $type, $id);
866
-
867
-        // Store the foreign key in the entity map. 
868
-        // We might want to store the (key, type) as we might need it
869
-        // to build a MorphTo proxy
870
-        $this->localForeignKeys[$name] = [
871
-            "id" => $id,
872
-            "type" => $type
873
-        ];
801
+		return new MorphOne($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
802
+	}
803
+
804
+	/**
805
+	 * Define an inverse one-to-one or many relationship.
806
+	 *
807
+	 * @param  mixed       $entity
808
+	 * @param  string      $related
809
+	 * @param  string|null $foreignKey
810
+	 * @param  string|null $otherKey
811
+	 * @param  string|null $relation
812
+	 * @throws MappingException
813
+	 * @return \Analogue\ORM\Relationships\BelongsTo
814
+	 */
815
+	public function belongsTo($entity, $related, $foreignKey = null, $otherKey = null)
816
+	{
817
+		// Add the relation to the definition in map
818
+		list(, $caller) = debug_backtrace(false);
819
+		$relation = $caller['function'];
820
+		$this->relatedClasses[$relation] = $related;
821
+		$this->singleRelations[] = $relation;
822
+		$this->localRelations[] = $relation;
823
+
824
+		// If no foreign key was supplied, we can use a backtrace to guess the proper
825
+		// foreign key name by using the name of the relationship function, which
826
+		// when combined with an "_id" should conventionally match the columns.
827
+		if (is_null($foreignKey)) {
828
+			$foreignKey = snake_case($relation) . '_id';
829
+		}
830
+
831
+		$this->localForeignKeys[$relation] = $foreignKey;
832
+
833
+		$relatedMapper = Manager::getInstance()->mapper($related);
834
+
835
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getKeyName();
836
+
837
+		return new BelongsTo($relatedMapper, $entity, $foreignKey, $otherKey, $relation);
838
+	}
839
+
840
+	/**
841
+	 * Define a polymorphic, inverse one-to-one or many relationship.
842
+	 *
843
+	 * @param  mixed       $entity
844
+	 * @param  string|null $name
845
+	 * @param  string|null $type
846
+	 * @param  string|null $id
847
+	 * @throws MappingException
848
+	 * @return \Analogue\ORM\Relationships\MorphTo
849
+	 */
850
+	public function morphTo($entity, $name = null, $type = null, $id = null)
851
+	{
852
+		// If no name is provided, we will use the backtrace to get the function name
853
+		// since that is most likely the name of the polymorphic interface. We can
854
+		// use that to get both the class and foreign key that will be utilized.
855
+		if (is_null($name)) {
856
+			list(, $caller) = debug_backtrace(false);
857
+
858
+			$name = snake_case($caller['function']);
859
+		}
860
+		$this->singleRelations[] = $name;
861
+		$this->localRelations[] = $relation;
862
+		$this->foreignRelations[] = $relation;
863
+		$this->relatedClass[$relation] = null;
864
+
865
+		list($type, $id) = $this->getMorphs($name, $type, $id);
866
+
867
+		// Store the foreign key in the entity map. 
868
+		// We might want to store the (key, type) as we might need it
869
+		// to build a MorphTo proxy
870
+		$this->localForeignKeys[$name] = [
871
+			"id" => $id,
872
+			"type" => $type
873
+		];
874 874
         
875 875
 
876
-        $mapper = Manager::getInstance()->mapper(get_class($entity));
877
-
878
-        // If the type value is null it is probably safe to assume we're eager loading
879
-        // the relationship. When that is the case we will pass in a dummy query as
880
-        // there are multiple types in the morph and we can't use single queries.
881
-        $factory = new Factory;
882
-        $wrapper = $factory->make($entity);
883
-
884
-        if (is_null($class = $wrapper->getEntityAttribute($type))) {
885
-            return new MorphTo(
886
-                $mapper, $entity, $id, null, $type, $name
887
-            );
888
-        }
889
-
890
-        // If we are not eager loading the relationship we will essentially treat this
891
-        // as a belongs-to style relationship since morph-to extends that class and
892
-        // we will pass in the appropriate values so that it behaves as expected.
893
-        else {
894
-            $class = Manager::getInstance()->getInverseMorphMap($class);
895
-            $relatedMapper = Manager::getInstance()->mapper($class);
896
-
897
-            $foreignKey = $relatedMapper->getEntityMap()->getKeyName();
898
-
899
-            return new MorphTo(
900
-                $relatedMapper, $entity, $id, $foreignKey, $type, $name
901
-            );
902
-        }
903
-    }
904
-
905
-    /**
906
-     * Define a one-to-many relationship.
907
-     *
908
-     * @param  mixed       $entity
909
-     * @param  string      $related
910
-     * @param  string|null $foreignKey
911
-     * @param  string|null $localKey
912
-     * @throws MappingException
913
-     * @return \Analogue\ORM\Relationships\HasMany
914
-     */
915
-    public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
916
-    {
917
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
918
-
919
-        $relatedMapper = Manager::getInstance()->mapper($related);
920
-
921
-        $table = $relatedMapper->getEntityMap()->getTable() . '.' . $foreignKey;
922
-
923
-        $localKey = $localKey ?: $this->getKeyName();
924
-
925
-        // Add the relation to the definition in map
926
-        list(, $caller) = debug_backtrace(false);
927
-        $relation = $caller['function'];
928
-        $this->relatedClasses[$relation] = $related;
929
-        $this->manyRelations[] = $relation;
930
-        $this->foreignRelations[] = $relation;
931
-
932
-        return new HasMany($relatedMapper, $entity, $table, $localKey);
933
-    }
934
-
935
-    /**
936
-     * Define a has-many-through relationship.
937
-     *
938
-     * @param  mixed       $entity
939
-     * @param  string      $related
940
-     * @param  string      $through
941
-     * @param  string|null $firstKey
942
-     * @param  string|null $secondKey
943
-     * @throws MappingException
944
-     * @return \Analogue\ORM\Relationships\HasManyThrough
945
-     */
946
-    public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
947
-    {
948
-        $relatedMapper = Manager::getInstance()->mapper($related);
949
-
950
-        $throughMapper = Manager::getInstance()->mapper($through);
951
-
952
-
953
-        $firstKey = $firstKey ?: $this->getForeignKey();
954
-
955
-        $throughMap = $throughMapper->getEntityMap();
956
-
957
-        $secondKey = $secondKey ?: $throughMap->getForeignKey();
958
-
959
-        // Add the relation to the definition in map
960
-        list(, $caller) = debug_backtrace(false);
961
-        $relation = $caller['function'];
962
-        $this->relatedClasses[$relation] = $related;
963
-        $this->manyRelations[] = $relation;
964
-        $this->foreignRelations[] = $relation;
965
-
966
-        return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
967
-    }
968
-
969
-    /**
970
-     * Define a polymorphic one-to-many relationship.
971
-     *
972
-     * @param  mixed       $entity
973
-     * @param  string      $related
974
-     * @param  string      $name
975
-     * @param  string|null $type
976
-     * @param  string|null $id
977
-     * @param  string|null $localKey
978
-     * @return \Analogue\ORM\Relationships\MorphMany
979
-     */
980
-    public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
981
-    {
982
-        // Here we will gather up the morph type and ID for the relationship so that we
983
-        // can properly query the intermediate table of a relation. Finally, we will
984
-        // get the table and create the relationship instances for the developers.
985
-        list($type, $id) = $this->getMorphs($name, $type, $id);
986
-
987
-        $relatedMapper = Manager::getInstance()->mapper($related);
988
-
989
-        $table = $relatedMapper->getEntityMap()->getTable();
990
-
991
-        $localKey = $localKey ?: $this->getKeyName();
992
-
993
-        // Add the relation to the definition in map
994
-        list(, $caller) = debug_backtrace(false);
995
-        $relation = $caller['function'];
996
-        $this->relatedClasses[$relation] = $related;
997
-        $this->manyRelations[] = $relation;
998
-        $this->foreignRelations[] = $relation;
999
-
1000
-        return new MorphMany($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
1001
-    }
1002
-
1003
-    /**
1004
-     * Define a many-to-many relationship.
1005
-     *
1006
-     * @param  mixed       $entity
1007
-     * @param  string      $relatedClass
1008
-     * @param  string|null $table
1009
-     * @param  string|null $foreignKey
1010
-     * @param  string|null $otherKey
1011
-     * @param  string|null $relation
1012
-     * @throws MappingException
1013
-     * @return \Analogue\ORM\Relationships\BelongsToMany
1014
-     */
1015
-    public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1016
-    {
1017
-        // Add the relation to the definition in map
1018
-        list(, $caller) = debug_backtrace(false);
1019
-        $relation = $caller['function'];
1020
-        $this->relatedClasses[$relation] = $related;
1021
-        $this->manyRelations[] = $relation;
1022
-        $this->foreignRelations[] = $relation;
1023
-        $this->pivotRelations[] = $relation;
1024
-
1025
-        // First, we'll need to determine the foreign key and "other key" for the
1026
-        // relationship. Once we have determined the keys we'll make the query
1027
-        // instances as well as the relationship instances we need for this.
1028
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1029
-
1030
-        $relatedMapper = Manager::getInstance()->mapper($related);
1031
-
1032
-        $relatedMap = $relatedMapper->getEntityMap();
1033
-
1034
-        $otherKey = $otherKey ?: $relatedMap->getForeignKey();
1035
-
1036
-        // If no table name was provided, we can guess it by concatenating the two
1037
-        // models using underscores in alphabetical order. The two model names
1038
-        // are transformed to snake case from their default CamelCase also.
1039
-        if (is_null($table)) {
1040
-            $table = $this->joiningTable($relatedMap);
1041
-        }
1042
-
1043
-        return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1044
-    }
1045
-
1046
-    /**
1047
-     * Define a polymorphic many-to-many relationship.
1048
-     *
1049
-     * @param  mixed       $entity
1050
-     * @param  string      $related
1051
-     * @param  string      $name
1052
-     * @param  string|null $table
1053
-     * @param  string|null $foreignKey
1054
-     * @param  string|null $otherKey
1055
-     * @param  bool        $inverse
1056
-     * @throws MappingException
1057
-     * @return \Analogue\ORM\Relationships\MorphToMany
1058
-     */
1059
-    public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1060
-    {
1061
-        // Add the relation to the definition in map
1062
-        list(, $caller) = debug_backtrace(false);
1063
-        $relation = $caller['function'];
1064
-        $this->relatedClasses[$relation] = $related;
1065
-        $this->manyRelations[] = $relation;
1066
-        $this->foreignRelations[] = $relation;
1067
-        $this->pivotRelations[] = $relation;
1068
-
1069
-        // First, we will need to determine the foreign key and "other key" for the
1070
-        // relationship. Once we have determined the keys we will make the query
1071
-        // instances, as well as the relationship instances we need for these.
1072
-        $foreignKey = $foreignKey ?: $name . '_id';
1073
-
1074
-        $relatedMapper = Manager::getInstance()->mapper($related);
1075
-
1076
-        $otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1077
-
1078
-        $table = $table ?: str_plural($name);
1079
-
1080
-        return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1081
-    }
1082
-
1083
-    /**
1084
-     * Define a polymorphic, inverse many-to-many relationship.
1085
-     *
1086
-     * @param  mixed       $entity
1087
-     * @param  string      $related
1088
-     * @param  string      $name
1089
-     * @param  string|null $table
1090
-     * @param  string|null $foreignKey
1091
-     * @param  string|null $otherKey
1092
-     * @throws MappingException
1093
-     * @return \Analogue\ORM\Relationships\MorphToMany
1094
-     */
1095
-    public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1096
-    {
1097
-        // Add the relation to the definition in map
1098
-        list(, $caller) = debug_backtrace(false);
1099
-        $relation = $caller['function'];
1100
-        $this->relatedClasses[$relation] = $related;
1101
-        $this->manyRelations[] = $relation;
1102
-        $this->foreignRelations[] = $relation;
1103
-
1104
-        $foreignKey = $foreignKey ?: $this->getForeignKey();
1105
-
1106
-        // For the inverse of the polymorphic many-to-many relations, we will change
1107
-        // the way we determine the foreign and other keys, as it is the opposite
1108
-        // of the morph-to-many method since we're figuring out these inverses.
1109
-        $otherKey = $otherKey ?: $name . '_id';
1110
-
1111
-        return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1112
-    }
1113
-
1114
-    /**
1115
-     * Get the joining table name for a many-to-many relation.
1116
-     *
1117
-     * @param  EntityMap $relatedMap
1118
-     * @return string
1119
-     */
1120
-    public function joiningTable($relatedMap)
1121
-    {
1122
-        // The joining table name, by convention, is simply the snake cased models
1123
-        // sorted alphabetically and concatenated with an underscore, so we can
1124
-        // just sort the models and join them together to get the table name.
1125
-        $base = $this->getTable();
1126
-
1127
-        $related = $relatedMap->getTable();
1128
-
1129
-        $tables = [$related, $base];
1130
-
1131
-        // Now that we have the model names in an array we can just sort them and
1132
-        // use the implode function to join them together with an underscores,
1133
-        // which is typically used by convention within the database system.
1134
-        sort($tables);
1135
-
1136
-        return strtolower(implode('_', $tables));
1137
-    }
1138
-
1139
-    /**
1140
-     * Get the polymorphic relationship columns.
1141
-     *
1142
-     * @param  string $name
1143
-     * @param  string $type
1144
-     * @param  string $id
1145
-     * @return string[]
1146
-     */
1147
-    protected function getMorphs($name, $type, $id)
1148
-    {
1149
-        $type = $type ?: $name . '_type';
1150
-
1151
-        $id = $id ?: $name . '_id';
1152
-
1153
-        return [$type, $id];
1154
-    }
1155
-
1156
-    /**
1157
-     * Get the class name for polymorphic relations.
1158
-     *
1159
-     * @return string
1160
-     */
1161
-    public function getMorphClass()
1162
-    {
1163
-        $morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1164
-        return $this->morphClass ?: $morphClass;
1165
-    }
1166
-
1167
-    /**
1168
-     * Create a new Entity Collection instance.
1169
-     *
1170
-     * @param  array $entities
1171
-     * @return \Analogue\ORM\EntityCollection
1172
-     */
1173
-    public function newCollection(array $entities = [])
1174
-    {
1175
-        $collection = new EntityCollection($entities, $this);
1176
-        return $collection->keyBy($this->getKeyName());
1177
-    }
1178
-
1179
-    /**
1180
-     * Process EntityMap parsing at initialization time
1181
-     *
1182
-     * @return void
1183
-     */
1184
-    public function initialize()
1185
-    {
1186
-        $userMethods = $this->getCustomMethods();
1187
-
1188
-        // Parse EntityMap for method based relationship
1189
-        if (count($userMethods) > 0) {
1190
-            $this->relationships = $this->parseMethodsForRelationship($userMethods);
1191
-        }
1192
-
1193
-        // Parse EntityMap for dynamic relationships
1194
-        if (count($this->dynamicRelationships) > 0) {
1195
-            $this->relationships = $this->relationships + $this->getDynamicRelationships();
1196
-        }
1197
-    }
1198
-
1199
-    /**
1200
-     * Parse every relationships on the EntityMap and sort
1201
-     * them by type.
1202
-     *
1203
-     * @return void
1204
-     */
1205
-    public function boot()
1206
-    {
1207
-        if (count($this->relationships > 0)) {
1208
-            $this->sortRelationshipsByType();
1209
-        }
1210
-    }
1211
-
1212
-    /**
1213
-     * Get Methods that has been added in the child class.
1214
-     *
1215
-     * @return array
1216
-     */
1217
-    protected function getCustomMethods()
1218
-    {
1219
-        $mapMethods = get_class_methods($this);
1220
-
1221
-        $parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1222
-
1223
-        return array_diff($mapMethods, $parentsMethods);
1224
-    }
1225
-
1226
-    /**
1227
-     * Parse user's class methods for relationships
1228
-     *
1229
-     * @param  array $customMethods
1230
-     * @return array
1231
-     */
1232
-    protected function parseMethodsForRelationship(array $customMethods)
1233
-    {
1234
-        $relationships = [];
1235
-
1236
-        $class = new ReflectionClass(get_class($this));
1237
-
1238
-        // Get the mapped Entity class, as we will detect relationships
1239
-        // methods by testing that the first argument is type-hinted to
1240
-        // the same class as the mapped Entity.
1241
-        $entityClass = $this->getClass();
1242
-
1243
-        foreach ($customMethods as $methodName) {
1244
-            $method = $class->getMethod($methodName);
1245
-
1246
-            if ($method->getNumberOfParameters() > 0) {
1247
-                $params = $method->getParameters();
1248
-
1249
-                if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1250
-                    $relationships[] = $methodName;
1251
-                }
1252
-            }
1253
-        }
1254
-
1255
-        return $relationships;
1256
-    }
1257
-
1258
-    /**
1259
-     * Sort Relationships methods by type
1260
-     * 
1261
-     * TODO : replace this by direclty setting these value
1262
-     * in the corresponding methods, so we won't need
1263
-     * the correpondancy tabble
1264
-     * 
1265
-     * @return void
1266
-     */
1267
-    protected function sortRelationshipsByType()
1268
-    {
1269
-        $entityClass = $this->getClass();
1270
-
1271
-        // Instantiate a dummy entity which we will pass to relationship methods.
1272
-        $entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1273
-
1274
-        foreach ($this->relationships as $relation) {
1275
-            $this->$relation($entity);
1276
-        }
1277
-    }
1278
-
1279
-    /**
1280
-     * Override this method for custom entity instantiation
1281
-     *
1282
-     * @return null
1283
-     */
1284
-    public function activator()
1285
-    {
1286
-        return null;
1287
-    }
1288
-
1289
-    /**
1290
-     * Call dynamic relationship, if it exists
1291
-     *
1292
-     * @param  string $method
1293
-     * @param  array  $parameters
1294
-     * @throws Exception
1295
-     * @return mixed
1296
-     */
1297
-    public function __call($method, $parameters)
1298
-    {
1299
-        if (!array_key_exists($method, $this->dynamicRelationships)) {
1300
-            throw new Exception(get_class($this) . " has no method $method");
1301
-        }
1302
-
1303
-        // Add $this to parameters so the closure can call relationship method on the map.
1304
-        $parameters[] = $this;
1305
-
1306
-        return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1307
-    }
876
+		$mapper = Manager::getInstance()->mapper(get_class($entity));
877
+
878
+		// If the type value is null it is probably safe to assume we're eager loading
879
+		// the relationship. When that is the case we will pass in a dummy query as
880
+		// there are multiple types in the morph and we can't use single queries.
881
+		$factory = new Factory;
882
+		$wrapper = $factory->make($entity);
883
+
884
+		if (is_null($class = $wrapper->getEntityAttribute($type))) {
885
+			return new MorphTo(
886
+				$mapper, $entity, $id, null, $type, $name
887
+			);
888
+		}
889
+
890
+		// If we are not eager loading the relationship we will essentially treat this
891
+		// as a belongs-to style relationship since morph-to extends that class and
892
+		// we will pass in the appropriate values so that it behaves as expected.
893
+		else {
894
+			$class = Manager::getInstance()->getInverseMorphMap($class);
895
+			$relatedMapper = Manager::getInstance()->mapper($class);
896
+
897
+			$foreignKey = $relatedMapper->getEntityMap()->getKeyName();
898
+
899
+			return new MorphTo(
900
+				$relatedMapper, $entity, $id, $foreignKey, $type, $name
901
+			);
902
+		}
903
+	}
904
+
905
+	/**
906
+	 * Define a one-to-many relationship.
907
+	 *
908
+	 * @param  mixed       $entity
909
+	 * @param  string      $related
910
+	 * @param  string|null $foreignKey
911
+	 * @param  string|null $localKey
912
+	 * @throws MappingException
913
+	 * @return \Analogue\ORM\Relationships\HasMany
914
+	 */
915
+	public function hasMany($entity, $related, $foreignKey = null, $localKey = null)
916
+	{
917
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
918
+
919
+		$relatedMapper = Manager::getInstance()->mapper($related);
920
+
921
+		$table = $relatedMapper->getEntityMap()->getTable() . '.' . $foreignKey;
922
+
923
+		$localKey = $localKey ?: $this->getKeyName();
924
+
925
+		// Add the relation to the definition in map
926
+		list(, $caller) = debug_backtrace(false);
927
+		$relation = $caller['function'];
928
+		$this->relatedClasses[$relation] = $related;
929
+		$this->manyRelations[] = $relation;
930
+		$this->foreignRelations[] = $relation;
931
+
932
+		return new HasMany($relatedMapper, $entity, $table, $localKey);
933
+	}
934
+
935
+	/**
936
+	 * Define a has-many-through relationship.
937
+	 *
938
+	 * @param  mixed       $entity
939
+	 * @param  string      $related
940
+	 * @param  string      $through
941
+	 * @param  string|null $firstKey
942
+	 * @param  string|null $secondKey
943
+	 * @throws MappingException
944
+	 * @return \Analogue\ORM\Relationships\HasManyThrough
945
+	 */
946
+	public function hasManyThrough($entity, $related, $through, $firstKey = null, $secondKey = null)
947
+	{
948
+		$relatedMapper = Manager::getInstance()->mapper($related);
949
+
950
+		$throughMapper = Manager::getInstance()->mapper($through);
951
+
952
+
953
+		$firstKey = $firstKey ?: $this->getForeignKey();
954
+
955
+		$throughMap = $throughMapper->getEntityMap();
956
+
957
+		$secondKey = $secondKey ?: $throughMap->getForeignKey();
958
+
959
+		// Add the relation to the definition in map
960
+		list(, $caller) = debug_backtrace(false);
961
+		$relation = $caller['function'];
962
+		$this->relatedClasses[$relation] = $related;
963
+		$this->manyRelations[] = $relation;
964
+		$this->foreignRelations[] = $relation;
965
+
966
+		return new HasManyThrough($relatedMapper, $entity, $throughMap, $firstKey, $secondKey);
967
+	}
968
+
969
+	/**
970
+	 * Define a polymorphic one-to-many relationship.
971
+	 *
972
+	 * @param  mixed       $entity
973
+	 * @param  string      $related
974
+	 * @param  string      $name
975
+	 * @param  string|null $type
976
+	 * @param  string|null $id
977
+	 * @param  string|null $localKey
978
+	 * @return \Analogue\ORM\Relationships\MorphMany
979
+	 */
980
+	public function morphMany($entity, $related, $name, $type = null, $id = null, $localKey = null)
981
+	{
982
+		// Here we will gather up the morph type and ID for the relationship so that we
983
+		// can properly query the intermediate table of a relation. Finally, we will
984
+		// get the table and create the relationship instances for the developers.
985
+		list($type, $id) = $this->getMorphs($name, $type, $id);
986
+
987
+		$relatedMapper = Manager::getInstance()->mapper($related);
988
+
989
+		$table = $relatedMapper->getEntityMap()->getTable();
990
+
991
+		$localKey = $localKey ?: $this->getKeyName();
992
+
993
+		// Add the relation to the definition in map
994
+		list(, $caller) = debug_backtrace(false);
995
+		$relation = $caller['function'];
996
+		$this->relatedClasses[$relation] = $related;
997
+		$this->manyRelations[] = $relation;
998
+		$this->foreignRelations[] = $relation;
999
+
1000
+		return new MorphMany($relatedMapper, $entity, $table . '.' . $type, $table . '.' . $id, $localKey);
1001
+	}
1002
+
1003
+	/**
1004
+	 * Define a many-to-many relationship.
1005
+	 *
1006
+	 * @param  mixed       $entity
1007
+	 * @param  string      $relatedClass
1008
+	 * @param  string|null $table
1009
+	 * @param  string|null $foreignKey
1010
+	 * @param  string|null $otherKey
1011
+	 * @param  string|null $relation
1012
+	 * @throws MappingException
1013
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
1014
+	 */
1015
+	public function belongsToMany($entity, $related, $table = null, $foreignKey = null, $otherKey = null)
1016
+	{
1017
+		// Add the relation to the definition in map
1018
+		list(, $caller) = debug_backtrace(false);
1019
+		$relation = $caller['function'];
1020
+		$this->relatedClasses[$relation] = $related;
1021
+		$this->manyRelations[] = $relation;
1022
+		$this->foreignRelations[] = $relation;
1023
+		$this->pivotRelations[] = $relation;
1024
+
1025
+		// First, we'll need to determine the foreign key and "other key" for the
1026
+		// relationship. Once we have determined the keys we'll make the query
1027
+		// instances as well as the relationship instances we need for this.
1028
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1029
+
1030
+		$relatedMapper = Manager::getInstance()->mapper($related);
1031
+
1032
+		$relatedMap = $relatedMapper->getEntityMap();
1033
+
1034
+		$otherKey = $otherKey ?: $relatedMap->getForeignKey();
1035
+
1036
+		// If no table name was provided, we can guess it by concatenating the two
1037
+		// models using underscores in alphabetical order. The two model names
1038
+		// are transformed to snake case from their default CamelCase also.
1039
+		if (is_null($table)) {
1040
+			$table = $this->joiningTable($relatedMap);
1041
+		}
1042
+
1043
+		return new BelongsToMany($relatedMapper, $entity, $table, $foreignKey, $otherKey, $relation);
1044
+	}
1045
+
1046
+	/**
1047
+	 * Define a polymorphic many-to-many relationship.
1048
+	 *
1049
+	 * @param  mixed       $entity
1050
+	 * @param  string      $related
1051
+	 * @param  string      $name
1052
+	 * @param  string|null $table
1053
+	 * @param  string|null $foreignKey
1054
+	 * @param  string|null $otherKey
1055
+	 * @param  bool        $inverse
1056
+	 * @throws MappingException
1057
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1058
+	 */
1059
+	public function morphToMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null, $inverse = false)
1060
+	{
1061
+		// Add the relation to the definition in map
1062
+		list(, $caller) = debug_backtrace(false);
1063
+		$relation = $caller['function'];
1064
+		$this->relatedClasses[$relation] = $related;
1065
+		$this->manyRelations[] = $relation;
1066
+		$this->foreignRelations[] = $relation;
1067
+		$this->pivotRelations[] = $relation;
1068
+
1069
+		// First, we will need to determine the foreign key and "other key" for the
1070
+		// relationship. Once we have determined the keys we will make the query
1071
+		// instances, as well as the relationship instances we need for these.
1072
+		$foreignKey = $foreignKey ?: $name . '_id';
1073
+
1074
+		$relatedMapper = Manager::getInstance()->mapper($related);
1075
+
1076
+		$otherKey = $otherKey ?: $relatedMapper->getEntityMap()->getForeignKey();
1077
+
1078
+		$table = $table ?: str_plural($name);
1079
+
1080
+		return new MorphToMany($relatedMapper, $entity, $name, $table, $foreignKey, $otherKey, $caller, $inverse);
1081
+	}
1082
+
1083
+	/**
1084
+	 * Define a polymorphic, inverse many-to-many relationship.
1085
+	 *
1086
+	 * @param  mixed       $entity
1087
+	 * @param  string      $related
1088
+	 * @param  string      $name
1089
+	 * @param  string|null $table
1090
+	 * @param  string|null $foreignKey
1091
+	 * @param  string|null $otherKey
1092
+	 * @throws MappingException
1093
+	 * @return \Analogue\ORM\Relationships\MorphToMany
1094
+	 */
1095
+	public function morphedByMany($entity, $related, $name, $table = null, $foreignKey = null, $otherKey = null)
1096
+	{
1097
+		// Add the relation to the definition in map
1098
+		list(, $caller) = debug_backtrace(false);
1099
+		$relation = $caller['function'];
1100
+		$this->relatedClasses[$relation] = $related;
1101
+		$this->manyRelations[] = $relation;
1102
+		$this->foreignRelations[] = $relation;
1103
+
1104
+		$foreignKey = $foreignKey ?: $this->getForeignKey();
1105
+
1106
+		// For the inverse of the polymorphic many-to-many relations, we will change
1107
+		// the way we determine the foreign and other keys, as it is the opposite
1108
+		// of the morph-to-many method since we're figuring out these inverses.
1109
+		$otherKey = $otherKey ?: $name . '_id';
1110
+
1111
+		return $this->morphToMany($entity, $related, $name, $table, $foreignKey, $otherKey, true);
1112
+	}
1113
+
1114
+	/**
1115
+	 * Get the joining table name for a many-to-many relation.
1116
+	 *
1117
+	 * @param  EntityMap $relatedMap
1118
+	 * @return string
1119
+	 */
1120
+	public function joiningTable($relatedMap)
1121
+	{
1122
+		// The joining table name, by convention, is simply the snake cased models
1123
+		// sorted alphabetically and concatenated with an underscore, so we can
1124
+		// just sort the models and join them together to get the table name.
1125
+		$base = $this->getTable();
1126
+
1127
+		$related = $relatedMap->getTable();
1128
+
1129
+		$tables = [$related, $base];
1130
+
1131
+		// Now that we have the model names in an array we can just sort them and
1132
+		// use the implode function to join them together with an underscores,
1133
+		// which is typically used by convention within the database system.
1134
+		sort($tables);
1135
+
1136
+		return strtolower(implode('_', $tables));
1137
+	}
1138
+
1139
+	/**
1140
+	 * Get the polymorphic relationship columns.
1141
+	 *
1142
+	 * @param  string $name
1143
+	 * @param  string $type
1144
+	 * @param  string $id
1145
+	 * @return string[]
1146
+	 */
1147
+	protected function getMorphs($name, $type, $id)
1148
+	{
1149
+		$type = $type ?: $name . '_type';
1150
+
1151
+		$id = $id ?: $name . '_id';
1152
+
1153
+		return [$type, $id];
1154
+	}
1155
+
1156
+	/**
1157
+	 * Get the class name for polymorphic relations.
1158
+	 *
1159
+	 * @return string
1160
+	 */
1161
+	public function getMorphClass()
1162
+	{
1163
+		$morphClass = Manager::getInstance()->getMorphMap($this->getClass());
1164
+		return $this->morphClass ?: $morphClass;
1165
+	}
1166
+
1167
+	/**
1168
+	 * Create a new Entity Collection instance.
1169
+	 *
1170
+	 * @param  array $entities
1171
+	 * @return \Analogue\ORM\EntityCollection
1172
+	 */
1173
+	public function newCollection(array $entities = [])
1174
+	{
1175
+		$collection = new EntityCollection($entities, $this);
1176
+		return $collection->keyBy($this->getKeyName());
1177
+	}
1178
+
1179
+	/**
1180
+	 * Process EntityMap parsing at initialization time
1181
+	 *
1182
+	 * @return void
1183
+	 */
1184
+	public function initialize()
1185
+	{
1186
+		$userMethods = $this->getCustomMethods();
1187
+
1188
+		// Parse EntityMap for method based relationship
1189
+		if (count($userMethods) > 0) {
1190
+			$this->relationships = $this->parseMethodsForRelationship($userMethods);
1191
+		}
1192
+
1193
+		// Parse EntityMap for dynamic relationships
1194
+		if (count($this->dynamicRelationships) > 0) {
1195
+			$this->relationships = $this->relationships + $this->getDynamicRelationships();
1196
+		}
1197
+	}
1198
+
1199
+	/**
1200
+	 * Parse every relationships on the EntityMap and sort
1201
+	 * them by type.
1202
+	 *
1203
+	 * @return void
1204
+	 */
1205
+	public function boot()
1206
+	{
1207
+		if (count($this->relationships > 0)) {
1208
+			$this->sortRelationshipsByType();
1209
+		}
1210
+	}
1211
+
1212
+	/**
1213
+	 * Get Methods that has been added in the child class.
1214
+	 *
1215
+	 * @return array
1216
+	 */
1217
+	protected function getCustomMethods()
1218
+	{
1219
+		$mapMethods = get_class_methods($this);
1220
+
1221
+		$parentsMethods = get_class_methods('Analogue\ORM\EntityMap');
1222
+
1223
+		return array_diff($mapMethods, $parentsMethods);
1224
+	}
1225
+
1226
+	/**
1227
+	 * Parse user's class methods for relationships
1228
+	 *
1229
+	 * @param  array $customMethods
1230
+	 * @return array
1231
+	 */
1232
+	protected function parseMethodsForRelationship(array $customMethods)
1233
+	{
1234
+		$relationships = [];
1235
+
1236
+		$class = new ReflectionClass(get_class($this));
1237
+
1238
+		// Get the mapped Entity class, as we will detect relationships
1239
+		// methods by testing that the first argument is type-hinted to
1240
+		// the same class as the mapped Entity.
1241
+		$entityClass = $this->getClass();
1242
+
1243
+		foreach ($customMethods as $methodName) {
1244
+			$method = $class->getMethod($methodName);
1245
+
1246
+			if ($method->getNumberOfParameters() > 0) {
1247
+				$params = $method->getParameters();
1248
+
1249
+				if ($params[0]->getClass() && ($params[0]->getClass()->name == $entityClass || is_subclass_of($entityClass, $params[0]->getClass()->name))) {
1250
+					$relationships[] = $methodName;
1251
+				}
1252
+			}
1253
+		}
1254
+
1255
+		return $relationships;
1256
+	}
1257
+
1258
+	/**
1259
+	 * Sort Relationships methods by type
1260
+	 * 
1261
+	 * TODO : replace this by direclty setting these value
1262
+	 * in the corresponding methods, so we won't need
1263
+	 * the correpondancy tabble
1264
+	 * 
1265
+	 * @return void
1266
+	 */
1267
+	protected function sortRelationshipsByType()
1268
+	{
1269
+		$entityClass = $this->getClass();
1270
+
1271
+		// Instantiate a dummy entity which we will pass to relationship methods.
1272
+		$entity = unserialize(sprintf('O:%d:"%s":0:{}', strlen($entityClass), $entityClass));
1273
+
1274
+		foreach ($this->relationships as $relation) {
1275
+			$this->$relation($entity);
1276
+		}
1277
+	}
1278
+
1279
+	/**
1280
+	 * Override this method for custom entity instantiation
1281
+	 *
1282
+	 * @return null
1283
+	 */
1284
+	public function activator()
1285
+	{
1286
+		return null;
1287
+	}
1288
+
1289
+	/**
1290
+	 * Call dynamic relationship, if it exists
1291
+	 *
1292
+	 * @param  string $method
1293
+	 * @param  array  $parameters
1294
+	 * @throws Exception
1295
+	 * @return mixed
1296
+	 */
1297
+	public function __call($method, $parameters)
1298
+	{
1299
+		if (!array_key_exists($method, $this->dynamicRelationships)) {
1300
+			throw new Exception(get_class($this) . " has no method $method");
1301
+		}
1302
+
1303
+		// Add $this to parameters so the closure can call relationship method on the map.
1304
+		$parameters[] = $this;
1305
+
1306
+		return  call_user_func_array([$this->dynamicRelationships[$method], $parameters]);
1307
+	}
1308 1308
 }
Please login to merge, or discard this patch.
src/MagicCasting.php 1 patch
Indentation   +95 added lines, -95 removed lines patch added patch discarded remove patch
@@ -10,109 +10,109 @@
 block discarded – undo
10 10
 {
11 11
 
12 12
  	/**
13
-     * Determine if the given attribute exists.
14
-     *
15
-     * @param  mixed $offset
16
-     * @return bool
17
-     */
18
-    public function offsetExists($offset)
19
-    {
20
-        return isset($this->$offset);
21
-    }
13
+ 	 * Determine if the given attribute exists.
14
+ 	 *
15
+ 	 * @param  mixed $offset
16
+ 	 * @return bool
17
+ 	 */
18
+	public function offsetExists($offset)
19
+	{
20
+		return isset($this->$offset);
21
+	}
22 22
 
23
-    /**
24
-     * Get the value for a given offset.
25
-     *
26
-     * @param  mixed $offset
27
-     * @return mixed
28
-     */
29
-    public function offsetGet($offset)
30
-    {
31
-        return $this->$offset;
32
-    }
23
+	/**
24
+	 * Get the value for a given offset.
25
+	 *
26
+	 * @param  mixed $offset
27
+	 * @return mixed
28
+	 */
29
+	public function offsetGet($offset)
30
+	{
31
+		return $this->$offset;
32
+	}
33 33
 
34
-    /**
35
-     * Set the value for a given offset.
36
-     *
37
-     * @param  mixed $offset
38
-     * @param  mixed $value
39
-     * @return void
40
-     */
41
-    public function offsetSet($offset, $value)
42
-    {
43
-        $this->$offset = $value;
44
-    }
34
+	/**
35
+	 * Set the value for a given offset.
36
+	 *
37
+	 * @param  mixed $offset
38
+	 * @param  mixed $value
39
+	 * @return void
40
+	 */
41
+	public function offsetSet($offset, $value)
42
+	{
43
+		$this->$offset = $value;
44
+	}
45 45
 
46
-    /**
47
-     * Unset the value for a given offset.
48
-     *
49
-     * @param  mixed $offset
50
-     * @return void
51
-     */
52
-    public function offsetUnset($offset)
53
-    {
54
-        unset($this->$offset);
55
-    }
46
+	/**
47
+	 * Unset the value for a given offset.
48
+	 *
49
+	 * @param  mixed $offset
50
+	 * @return void
51
+	 */
52
+	public function offsetUnset($offset)
53
+	{
54
+		unset($this->$offset);
55
+	}
56 56
 
57
-    /**
58
-     * Convert the object into something JSON serializable.
59
-     *
60
-     * @return array
61
-     */
62
-    public function jsonSerialize()
63
-    {
64
-        return $this->toArray();
65
-    }
57
+	/**
58
+	 * Convert the object into something JSON serializable.
59
+	 *
60
+	 * @return array
61
+	 */
62
+	public function jsonSerialize()
63
+	{
64
+		return $this->toArray();
65
+	}
66 66
 
67
-    /**
68
-     * Convert the entity instance to JSON.
69
-     *
70
-     * @param  int $options
71
-     * @return string
72
-     */
73
-    public function toJson($options = 0)
74
-    {
75
-        return json_encode($this->toArray(), $options);
76
-    }
67
+	/**
68
+	 * Convert the entity instance to JSON.
69
+	 *
70
+	 * @param  int $options
71
+	 * @return string
72
+	 */
73
+	public function toJson($options = 0)
74
+	{
75
+		return json_encode($this->toArray(), $options);
76
+	}
77 77
 
78
-    /**
79
-     * Convert Mappable object to array;
80
-     *
81
-     * @return array
82
-     */
83
-    public function toArray()
84
-    {
85
-        return $this->attributesToArray($this->attributes);
86
-    }
78
+	/**
79
+	 * Convert Mappable object to array;
80
+	 *
81
+	 * @return array
82
+	 */
83
+	public function toArray()
84
+	{
85
+		return $this->attributesToArray($this->attributes);
86
+	}
87 87
 
88
-    /**
89
-     * Transform the Object to array/json,
90
-     *
91
-     * @param  array $sourceAttributes
92
-     * @return array
93
-     */
94
-    protected function attributesToArray(array $sourceAttributes)
95
-    {
96
-        $attributes = [];
88
+	/**
89
+	 * Transform the Object to array/json,
90
+	 *
91
+	 * @param  array $sourceAttributes
92
+	 * @return array
93
+	 */
94
+	protected function attributesToArray(array $sourceAttributes)
95
+	{
96
+		$attributes = [];
97 97
 
98
-        foreach ($sourceAttributes as $key => $attribute) {
99
-            // If the attribute is a proxy, and hasn't be loaded, we discard
100
-            // it from the returned set.
101
-            if ($attribute instanceof ProxyInterface && !$attribute->isProxyInitialized()) {
102
-                continue;
103
-            }
98
+		foreach ($sourceAttributes as $key => $attribute) {
99
+			// If the attribute is a proxy, and hasn't be loaded, we discard
100
+			// it from the returned set.
101
+			if ($attribute instanceof ProxyInterface && !$attribute->isProxyInitialized()) {
102
+				continue;
103
+			}
104 104
 
105
-            if ($attribute instanceof Carbon) {
106
-                $attributes[$key] = $attribute->__toString();
107
-                continue;
108
-            }
105
+			if ($attribute instanceof Carbon) {
106
+				$attributes[$key] = $attribute->__toString();
107
+				continue;
108
+			}
109 109
 
110
-            if ($attribute instanceof Arrayable) {
111
-                $attributes[$key] = $attribute->toArray();
112
-            } else {
113
-                $attributes[$key] = $attribute;
114
-            }
115
-        }
116
-        return $attributes;
117
-    }
110
+			if ($attribute instanceof Arrayable) {
111
+				$attributes[$key] = $attribute->toArray();
112
+			} else {
113
+				$attributes[$key] = $attribute;
114
+			}
115
+		}
116
+		return $attributes;
117
+	}
118 118
 }
Please login to merge, or discard this patch.
src/ValueObject.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -9,9 +9,9 @@
 block discarded – undo
9 9
 
10 10
 class ValueObject implements Mappable, ArrayAccess, Jsonable, JsonSerializable, Arrayable
11 11
 {
12
-    use MappableTrait;
13
-    use MagicGetters;
14
-    use MagicSetters;
15
-    use MagicCasting;
12
+	use MappableTrait;
13
+	use MagicGetters;
14
+	use MagicSetters;
15
+	use MagicCasting;
16 16
 }
17 17
 
Please login to merge, or discard this patch.
src/System/EntityBuilder.php 1 patch
Indentation   +185 added lines, -185 removed lines patch added patch discarded remove patch
@@ -11,190 +11,190 @@
 block discarded – undo
11 11
  */
12 12
 class EntityBuilder
13 13
 {
14
-    /**
15
-     * The mapper for the entity to build
16
-     * @var \Analogue\ORM\System\Mapper
17
-     */
18
-    protected $mapper;
19
-
20
-    /**
21
-     * The Entity Map for the entity to build.
22
-     *
23
-     * @var \Analogue\ORM\EntityMap
24
-     */
25
-    protected $entityMap;
26
-
27
-    /**
28
-     * Relations that will be eager loaded on this query
29
-     *
30
-     * @var array
31
-     */
32
-    protected $eagerLoads;
33
-
34
-    /**
35
-     * Relations that will be lazy loaded on this query
36
-     *
37
-     * @var array
38
-     */
39
-    protected $lazyLoads;
40
-
41
-    /**
42
-     * @var array
43
-     */
44
-    protected $casts;
45
-
46
-    /**
47
-     * Entity Wrapper Factory
48
-     * @var \Analogue\ORM\System\Wrappers\Factory
49
-     */
50
-    protected $factory;
51
-
52
-    /**
53
-     * EntityBuilder constructor.
54
-     * @param Mapper $mapper
55
-     * @param array  $eagerLoads
56
-     */
57
-    public function __construct(Mapper $mapper, array $eagerLoads)
58
-    {
59
-        $this->mapper = $mapper;
60
-
61
-        $this->entityMap = $mapper->getEntityMap();
62
-
63
-        $this->eagerLoads = $eagerLoads;
64
-
65
-        $this->lazyLoads = $this->getRelationshipsToProxy();
66
-
67
-        $this->factory = new Factory;
68
-    }
69
-
70
-    /**
71
-     * Convert an array of values into an entity.
72
-     *
73
-     * @param  array $result
74
-     * @return array
75
-     */
76
-    public function build(array $result)
77
-    {
78
-        $keyName = $this->entityMap->getKeyName();
79
-
80
-        $tmpCache = [];
81
-
82
-        $instance = $this->getWrapperInstance();
14
+	/**
15
+	 * The mapper for the entity to build
16
+	 * @var \Analogue\ORM\System\Mapper
17
+	 */
18
+	protected $mapper;
19
+
20
+	/**
21
+	 * The Entity Map for the entity to build.
22
+	 *
23
+	 * @var \Analogue\ORM\EntityMap
24
+	 */
25
+	protected $entityMap;
26
+
27
+	/**
28
+	 * Relations that will be eager loaded on this query
29
+	 *
30
+	 * @var array
31
+	 */
32
+	protected $eagerLoads;
33
+
34
+	/**
35
+	 * Relations that will be lazy loaded on this query
36
+	 *
37
+	 * @var array
38
+	 */
39
+	protected $lazyLoads;
40
+
41
+	/**
42
+	 * @var array
43
+	 */
44
+	protected $casts;
45
+
46
+	/**
47
+	 * Entity Wrapper Factory
48
+	 * @var \Analogue\ORM\System\Wrappers\Factory
49
+	 */
50
+	protected $factory;
51
+
52
+	/**
53
+	 * EntityBuilder constructor.
54
+	 * @param Mapper $mapper
55
+	 * @param array  $eagerLoads
56
+	 */
57
+	public function __construct(Mapper $mapper, array $eagerLoads)
58
+	{
59
+		$this->mapper = $mapper;
60
+
61
+		$this->entityMap = $mapper->getEntityMap();
62
+
63
+		$this->eagerLoads = $eagerLoads;
64
+
65
+		$this->lazyLoads = $this->getRelationshipsToProxy();
66
+
67
+		$this->factory = new Factory;
68
+	}
69
+
70
+	/**
71
+	 * Convert an array of values into an entity.
72
+	 *
73
+	 * @param  array $result
74
+	 * @return array
75
+	 */
76
+	public function build(array $result)
77
+	{
78
+		$keyName = $this->entityMap->getKeyName();
79
+
80
+		$tmpCache = [];
81
+
82
+		$instance = $this->getWrapperInstance();
83 83
         
84
-        $tmpCache[$result[$keyName]] = $result;
85
-
86
-        // Hydrate any embedded Value Object
87
-        $this->hydrateValueObjects($result);
88
-
89
-        $instance->setEntityAttributes($result);
90
-
91
-        // Hydrate relationship attributes with lazyloading proxies
92
-        if (count($this->lazyLoads) > 0) {
93
-            $instance->setProxies($this->lazyLoads);
94
-        }
95
-
96
-        // Directly Unwrap the entity now that it has been hydrated
97
-        $entity = $instance->getObject();
98
-
99
-        $this->mapper->getEntityCache()->add($tmpCache);
100
-
101
-        return $entity;
102
-    }
103
-
104
-    /**
105
-     * Get the correct wrapper prototype corresponding to the object type
106
-     *
107
-     * @throws \Analogue\ORM\Exceptions\MappingException
108
-     * @return InternallyMappable
109
-     */
110
-    protected function getWrapperInstance()
111
-    {
112
-        return $this->factory->make($this->mapper->newInstance());
113
-    }
114
-
115
-    /**
116
-     * Hydrate value object embedded in this entity
117
-     *
118
-     * @param  array $attributes
119
-     * @throws \Analogue\ORM\Exceptions\MappingException
120
-     * @return void
121
-     */
122
-    protected function hydrateValueObjects(& $attributes)
123
-    {
124
-        foreach ($this->entityMap->getEmbeddables() as $localKey => $valueClass) {
125
-            $this->hydrateValueObject($attributes, $localKey, $valueClass);
126
-        }
127
-    }
128
-
129
-    /**
130
-     * Hydrate a single value object
131
-     *
132
-     * @param  array  $attributes
133
-     * @param  string $localKey
134
-     * @param  string $valueClass
135
-     * @throws \Analogue\ORM\Exceptions\MappingException
136
-     * @return void
137
-     */
138
-    protected function hydrateValueObject(& $attributes, $localKey, $valueClass)
139
-    {
140
-        $map = $this->mapper->getManager()->getValueMap($valueClass);
141
-
142
-        $embeddedAttributes = $map->getAttributes();
143
-
144
-        $valueObject = $this->mapper->getManager()->getValueObjectInstance($valueClass);
145
-
146
-        foreach ($embeddedAttributes as $key) {
147
-            $prefix = snake_case(class_basename($valueClass)) . '_';
148
-
149
-            $voWrapper = $this->factory->make($valueObject);
150
-
151
-            $voWrapper->setEntityAttribute($key, $attributes[$prefix . $key]);
152
-
153
-            unset($attributes[$prefix . $key]);
154
-        }
155
-
156
-        $attributes[$localKey] = $valueObject;
157
-    }
158
-
159
-    /**
160
-     * Deduce the relationships for which we will build lazy loading proxies
161
-     *
162
-     * @return array
163
-     */
164
-    protected function getRelationshipsToProxy()
165
-    {
166
-        $relations = $this->entityMap->getRelationships();
167
-
168
-        return array_diff($relations, $this->eagerLoads);
169
-    }
170
-
171
-    /**
172
-     * Build lazy loading proxies for the current entity
173
-     *
174
-     * @param InternallyMappable $entity
175
-     *
176
-     * @return array
177
-     */
178
-    protected function getLazyLoadingProxies(InternallyMappable $entity)
179
-    {
180
-        $proxies = [];
181
-
182
-        $singleRelations = $this->entityMap->getSingleRelationships();
183
-        $manyRelations = $this->entityMap->getManyRelationships();
184
-
185
-        $proxyFactory = new ProxyFactory;
186
-
187
-        foreach ($this->lazyLoads as $relation) {
188
-            if (in_array($relation, $singleRelations)) {
189
-                //$proxies[$relation] = new EntityProxy($entity->getObject(), $relation);
190
-                $targetClass = $this->entityMap->getTargetClass($relation);
191
-                $proxies[$relation] = $proxyFactory->make($entity->getObject(), $relation, $targetClass);
192
-            }
193
-            if (in_array($relation, $manyRelations)) {
194
-                $proxies[$relation] = new CollectionProxy($entity->getObject(), $relation);
195
-            }
196
-        }
197
-
198
-        return $proxies;
199
-    }
84
+		$tmpCache[$result[$keyName]] = $result;
85
+
86
+		// Hydrate any embedded Value Object
87
+		$this->hydrateValueObjects($result);
88
+
89
+		$instance->setEntityAttributes($result);
90
+
91
+		// Hydrate relationship attributes with lazyloading proxies
92
+		if (count($this->lazyLoads) > 0) {
93
+			$instance->setProxies($this->lazyLoads);
94
+		}
95
+
96
+		// Directly Unwrap the entity now that it has been hydrated
97
+		$entity = $instance->getObject();
98
+
99
+		$this->mapper->getEntityCache()->add($tmpCache);
100
+
101
+		return $entity;
102
+	}
103
+
104
+	/**
105
+	 * Get the correct wrapper prototype corresponding to the object type
106
+	 *
107
+	 * @throws \Analogue\ORM\Exceptions\MappingException
108
+	 * @return InternallyMappable
109
+	 */
110
+	protected function getWrapperInstance()
111
+	{
112
+		return $this->factory->make($this->mapper->newInstance());
113
+	}
114
+
115
+	/**
116
+	 * Hydrate value object embedded in this entity
117
+	 *
118
+	 * @param  array $attributes
119
+	 * @throws \Analogue\ORM\Exceptions\MappingException
120
+	 * @return void
121
+	 */
122
+	protected function hydrateValueObjects(& $attributes)
123
+	{
124
+		foreach ($this->entityMap->getEmbeddables() as $localKey => $valueClass) {
125
+			$this->hydrateValueObject($attributes, $localKey, $valueClass);
126
+		}
127
+	}
128
+
129
+	/**
130
+	 * Hydrate a single value object
131
+	 *
132
+	 * @param  array  $attributes
133
+	 * @param  string $localKey
134
+	 * @param  string $valueClass
135
+	 * @throws \Analogue\ORM\Exceptions\MappingException
136
+	 * @return void
137
+	 */
138
+	protected function hydrateValueObject(& $attributes, $localKey, $valueClass)
139
+	{
140
+		$map = $this->mapper->getManager()->getValueMap($valueClass);
141
+
142
+		$embeddedAttributes = $map->getAttributes();
143
+
144
+		$valueObject = $this->mapper->getManager()->getValueObjectInstance($valueClass);
145
+
146
+		foreach ($embeddedAttributes as $key) {
147
+			$prefix = snake_case(class_basename($valueClass)) . '_';
148
+
149
+			$voWrapper = $this->factory->make($valueObject);
150
+
151
+			$voWrapper->setEntityAttribute($key, $attributes[$prefix . $key]);
152
+
153
+			unset($attributes[$prefix . $key]);
154
+		}
155
+
156
+		$attributes[$localKey] = $valueObject;
157
+	}
158
+
159
+	/**
160
+	 * Deduce the relationships for which we will build lazy loading proxies
161
+	 *
162
+	 * @return array
163
+	 */
164
+	protected function getRelationshipsToProxy()
165
+	{
166
+		$relations = $this->entityMap->getRelationships();
167
+
168
+		return array_diff($relations, $this->eagerLoads);
169
+	}
170
+
171
+	/**
172
+	 * Build lazy loading proxies for the current entity
173
+	 *
174
+	 * @param InternallyMappable $entity
175
+	 *
176
+	 * @return array
177
+	 */
178
+	protected function getLazyLoadingProxies(InternallyMappable $entity)
179
+	{
180
+		$proxies = [];
181
+
182
+		$singleRelations = $this->entityMap->getSingleRelationships();
183
+		$manyRelations = $this->entityMap->getManyRelationships();
184
+
185
+		$proxyFactory = new ProxyFactory;
186
+
187
+		foreach ($this->lazyLoads as $relation) {
188
+			if (in_array($relation, $singleRelations)) {
189
+				//$proxies[$relation] = new EntityProxy($entity->getObject(), $relation);
190
+				$targetClass = $this->entityMap->getTargetClass($relation);
191
+				$proxies[$relation] = $proxyFactory->make($entity->getObject(), $relation, $targetClass);
192
+			}
193
+			if (in_array($relation, $manyRelations)) {
194
+				$proxies[$relation] = new CollectionProxy($entity->getObject(), $relation);
195
+			}
196
+		}
197
+
198
+		return $proxies;
199
+	}
200 200
 }
201 201
\ No newline at end of file
Please login to merge, or discard this patch.
src/System/Wrappers/Wrapper.php 1 patch
Indentation   +175 added lines, -175 removed lines patch added patch discarded remove patch
@@ -11,180 +11,180 @@
 block discarded – undo
11 11
  */
12 12
 abstract class Wrapper implements InternallyMappable
13 13
 {
14
-    /**
15
-     * Original Entity Object
16
-     *
17
-     * @var mixed
18
-     */
19
-    protected $entity;
20
-
21
-    /**
22
-     * Corresponding EntityMap
23
-     *
24
-     * @var \Analogue\ORM\EntityMap
25
-     */
26
-    protected $entityMap;
27
-
28
-    /**
29
-     * @var \Analogue\ORM\System\Proxirs\ProxyFactory
30
-     */
31
-    protected $proxyFactory;
32
-
33
-    /**
34
-     * Wrapper constructor.
35
-     * @param $entity
36
-     * @param $entityMap
37
-     */
38
-    public function __construct($entity, $entityMap)
39
-    {
40
-        $this->entity = $entity;
41
-        $this->entityMap = $entityMap;
42
-        $this->proxyFactory = new ProxyFactory;
43
-    }
44
-
45
-    /**
46
-     * Return the wrapped entity class
47
-     *
48
-     * @return mixed
49
-     */
50
-    public function getEntityClass()
51
-    {
52
-        return get_class($this->entity);
53
-    }
54
-
55
-    /**
56
-     * Returns the wrapped entity
57
-     *
58
-     * @return mixed
59
-     */
60
-    public function getObject()
61
-    {
62
-        return $this->entity;
63
-    }
64
-
65
-    /**
66
-     * Returns the wrapped entity's map
67
-     *
68
-     * @return mixed
69
-     */
70
-    public function getMap()
71
-    {
72
-        return $this->entityMap;
73
-    }
74
-
75
-    /**
76
-     * Set the lazyloading proxies on the wrapped entity objet
77
-     * 
78
-     * @param  array  $relations  list of relations to be lazy loaded
79
-     * 
80
-     * @return void
81
-     */
82
-    public function setProxies(array $relations = null)
83
-    {
84
-        $attributes = $this->getEntityAttributes();
85
-        $proxies = [];
86
-
87
-        if(is_null($relations)) {
88
-            $relations = $this->getRelationsToProxy();
89
-        }
90
-
91
-        foreach ($relations as $relation) {
92
-            // We first look if we need to build the proxy on the relationship.
93
-            // If the key is handled locally and we know it not to be set,
94
-            // we'll set the relationship to null
95
-            if (! $this->relationNeedsProxy($relation, $attributes))  {
96
-                $proxies[$relation] = null;
97
-            }
98
-            else {
99
-                $targetClass = $this->entityMap->getTargettedClass($relation);
100
-                $proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
101
-            }
102
-        }
103
-
104
-        foreach ($proxies as $key => $value) {
105
-            $this->setEntityAttribute($key, $value);
106
-        }
107
-    }
108
-
109
-    /**  
110
-     * Determine which relations we have to build proxy for, by parsing
111
-     * attributes and finding methods that aren't set.
112
-     * 
113
-     * @return array
114
-     */
115
-    protected function getRelationsToProxy()
116
-    {
117
-        $proxies = [];
118
-        $attributes = $this->getEntityAttributes();
119
-
120
-        foreach ($this->entityMap->getRelationships() as $relation) {
121
-            if (!array_key_exists($relation, $attributes)) {
122
-                $proxies[] = $relation;
123
-            }
124
-        }
125
-
126
-        return $proxies;
127
-    }
128
-
129
-    /**  
130
-     * Determine if the relation needs a proxy or not
131
-     * 
132
-     * @param  string $relation  
133
-     * @param  array $attributes 
134
-     * @return boolean
135
-     */
136
-    protected function relationNeedsProxy($relation, $attributes)
137
-    {
138
-        if(in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
139
-            return false;
140
-        }
141
-
142
-        $localKey = $this->entityMap->getLocalKeys($relation);
143
-
144
-        if(is_null($localKey)) return true;
145
-
146
-        if(is_array($localKey)) {
147
-            $localKey = $localKey['id'];
148
-        }
149
-
150
-        if(! isset($attributes[$localKey])) {
151
-            return false;
152
-        }
153
-
154
-        if(is_null($attributes[$localKey])) {
155
-            return false;
156
-        }
14
+	/**
15
+	 * Original Entity Object
16
+	 *
17
+	 * @var mixed
18
+	 */
19
+	protected $entity;
20
+
21
+	/**
22
+	 * Corresponding EntityMap
23
+	 *
24
+	 * @var \Analogue\ORM\EntityMap
25
+	 */
26
+	protected $entityMap;
27
+
28
+	/**
29
+	 * @var \Analogue\ORM\System\Proxirs\ProxyFactory
30
+	 */
31
+	protected $proxyFactory;
32
+
33
+	/**
34
+	 * Wrapper constructor.
35
+	 * @param $entity
36
+	 * @param $entityMap
37
+	 */
38
+	public function __construct($entity, $entityMap)
39
+	{
40
+		$this->entity = $entity;
41
+		$this->entityMap = $entityMap;
42
+		$this->proxyFactory = new ProxyFactory;
43
+	}
44
+
45
+	/**
46
+	 * Return the wrapped entity class
47
+	 *
48
+	 * @return mixed
49
+	 */
50
+	public function getEntityClass()
51
+	{
52
+		return get_class($this->entity);
53
+	}
54
+
55
+	/**
56
+	 * Returns the wrapped entity
57
+	 *
58
+	 * @return mixed
59
+	 */
60
+	public function getObject()
61
+	{
62
+		return $this->entity;
63
+	}
64
+
65
+	/**
66
+	 * Returns the wrapped entity's map
67
+	 *
68
+	 * @return mixed
69
+	 */
70
+	public function getMap()
71
+	{
72
+		return $this->entityMap;
73
+	}
74
+
75
+	/**
76
+	 * Set the lazyloading proxies on the wrapped entity objet
77
+	 * 
78
+	 * @param  array  $relations  list of relations to be lazy loaded
79
+	 * 
80
+	 * @return void
81
+	 */
82
+	public function setProxies(array $relations = null)
83
+	{
84
+		$attributes = $this->getEntityAttributes();
85
+		$proxies = [];
86
+
87
+		if(is_null($relations)) {
88
+			$relations = $this->getRelationsToProxy();
89
+		}
90
+
91
+		foreach ($relations as $relation) {
92
+			// We first look if we need to build the proxy on the relationship.
93
+			// If the key is handled locally and we know it not to be set,
94
+			// we'll set the relationship to null
95
+			if (! $this->relationNeedsProxy($relation, $attributes))  {
96
+				$proxies[$relation] = null;
97
+			}
98
+			else {
99
+				$targetClass = $this->entityMap->getTargettedClass($relation);
100
+				$proxies[$relation] = $this->proxyFactory->make($this->getObject(), $relation, $targetClass);
101
+			}
102
+		}
103
+
104
+		foreach ($proxies as $key => $value) {
105
+			$this->setEntityAttribute($key, $value);
106
+		}
107
+	}
108
+
109
+	/**  
110
+	 * Determine which relations we have to build proxy for, by parsing
111
+	 * attributes and finding methods that aren't set.
112
+	 * 
113
+	 * @return array
114
+	 */
115
+	protected function getRelationsToProxy()
116
+	{
117
+		$proxies = [];
118
+		$attributes = $this->getEntityAttributes();
119
+
120
+		foreach ($this->entityMap->getRelationships() as $relation) {
121
+			if (!array_key_exists($relation, $attributes)) {
122
+				$proxies[] = $relation;
123
+			}
124
+		}
125
+
126
+		return $proxies;
127
+	}
128
+
129
+	/**  
130
+	 * Determine if the relation needs a proxy or not
131
+	 * 
132
+	 * @param  string $relation  
133
+	 * @param  array $attributes 
134
+	 * @return boolean
135
+	 */
136
+	protected function relationNeedsProxy($relation, $attributes)
137
+	{
138
+		if(in_array($relation, $this->entityMap->getRelationshipsWithoutProxy())) {
139
+			return false;
140
+		}
141
+
142
+		$localKey = $this->entityMap->getLocalKeys($relation);
143
+
144
+		if(is_null($localKey)) return true;
145
+
146
+		if(is_array($localKey)) {
147
+			$localKey = $localKey['id'];
148
+		}
149
+
150
+		if(! isset($attributes[$localKey])) {
151
+			return false;
152
+		}
153
+
154
+		if(is_null($attributes[$localKey])) {
155
+			return false;
156
+		}
157 157
         
158
-        return true;
159
-    }
160
-
161
-    /**
162
-     * @param string $key
163
-     * @param string $value
164
-     * @return mixed
165
-     */
166
-    abstract public function setEntityAttribute($key, $value);
167
-
168
-    /**
169
-     * @param string $key
170
-     * @return mixed
171
-     */
172
-    abstract public function getEntityAttribute($key);
173
-
174
-    /**
175
-     * @param array $attributes
176
-     * @return mixed
177
-     */
178
-    abstract public function setEntityAttributes(array $attributes);
179
-
180
-    /**
181
-     * @return mixed
182
-     */
183
-    abstract public function getEntityAttributes();
184
-
185
-    /**
186
-     * @param string $key
187
-     * @return mixed
188
-     */
189
-    abstract public function hasAttribute($key);
158
+		return true;
159
+	}
160
+
161
+	/**
162
+	 * @param string $key
163
+	 * @param string $value
164
+	 * @return mixed
165
+	 */
166
+	abstract public function setEntityAttribute($key, $value);
167
+
168
+	/**
169
+	 * @param string $key
170
+	 * @return mixed
171
+	 */
172
+	abstract public function getEntityAttribute($key);
173
+
174
+	/**
175
+	 * @param array $attributes
176
+	 * @return mixed
177
+	 */
178
+	abstract public function setEntityAttributes(array $attributes);
179
+
180
+	/**
181
+	 * @return mixed
182
+	 */
183
+	abstract public function getEntityAttributes();
184
+
185
+	/**
186
+	 * @param string $key
187
+	 * @return mixed
188
+	 */
189
+	abstract public function hasAttribute($key);
190 190
 }
Please login to merge, or discard this patch.
src/System/Query.php 1 patch
Indentation   +842 added lines, -842 removed lines patch added patch discarded remove patch
@@ -19,846 +19,846 @@
 block discarded – undo
19 19
  */
20 20
 class Query
21 21
 {
22
-    /**
23
-     * Mapper Instance
24
-     *
25
-     * @var \Analogue\ORM\System\Mapper
26
-     */
27
-    protected $mapper;
28
-
29
-    /**
30
-     * DB Adatper
31
-     *
32
-     * @var \Analogue\ORM\Drivers\DBAdapter
33
-     */
34
-    protected $adapter;
35
-
36
-    /**
37
-     * Query Builder Instance
38
-     *
39
-     * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
-     */
41
-    protected $query;
42
-
43
-    /**
44
-     * Entity Map Instance
45
-     *
46
-     * @var \Analogue\ORM\EntityMap
47
-     */
48
-    protected $entityMap;
49
-
50
-    /**
51
-     * The relationships that should be eager loaded.
52
-     *
53
-     * @var array
54
-     */
55
-    protected $eagerLoad = [];
56
-
57
-    /**
58
-     * All of the registered builder macros.
59
-     *
60
-     * @var array
61
-     */
62
-    protected $macros = [];
63
-
64
-    /**
65
-     * The methods that should be returned from query builder.
66
-     *
67
-     * @var array
68
-     */
69
-    protected $passthru = [
70
-        'toSql',
71
-        'lists',
72
-        'pluck',
73
-        'count',
74
-        'min',
75
-        'max',
76
-        'avg',
77
-        'sum',
78
-        'exists',
79
-        'getBindings',
80
-    ];
81
-
82
-    /**
83
-     * Query Builder Blacklist
84
-     */
85
-    protected $blacklist = [
86
-        'insert',
87
-        'insertGetId',
88
-        'lock',
89
-        'lockForUpdate',
90
-        'sharedLock',
91
-        'update',
92
-        'increment',
93
-        'decrement',
94
-        'delete',
95
-        'truncate',
96
-        'raw',
97
-    ];
98
-
99
-    /**
100
-     * Create a new Analogue Query Builder instance.
101
-     *
102
-     * @param  Mapper    $mapper
103
-     * @param  DBAdapter $adapter
104
-     */
105
-    public function __construct(Mapper $mapper, DBAdapter $adapter)
106
-    {
107
-        $this->mapper = $mapper;
108
-
109
-        $this->adapter = $adapter;
110
-
111
-        $this->entityMap = $mapper->getEntityMap();
112
-
113
-        // Specify the table to work on
114
-        $this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
-
116
-        $this->with($this->entityMap->getEagerloadedRelationships());
117
-    }
118
-
119
-    /**
120
-     * Run the query and return the result
121
-     *
122
-     * @param  array $columns
123
-     * @return \Analogue\ORM\EntityCollection
124
-     */
125
-    public function get($columns = ['*'])
126
-    {
127
-        $entities = $this->getEntities($columns);
128
-
129
-        // If we actually found models we will also eager load any relationships that
130
-        // have been specified as needing to be eager loaded, which will solve the
131
-        // n+1 query issue for the developers to avoid running a lot of queries.
132
-
133
-        if (count($entities) > 0) {
134
-            $entities = $this->eagerLoadRelations($entities);
135
-        }
136
-
137
-        return $this->entityMap->newCollection($entities);
138
-    }
139
-
140
-    /**
141
-     * Find an entity by its primary key
142
-     *
143
-     * @param  string|integer $id
144
-     * @param  array          $columns
145
-     * @return \Analogue\ORM\Mappable
146
-     */
147
-    public function find($id, $columns = ['*'])
148
-    {
149
-        if (is_array($id)) {
150
-            return $this->findMany($id, $columns);
151
-        }
152
-
153
-        $this->query->where($this->entityMap->getKeyName(), '=', $id);
154
-
155
-        return $this->first($columns);
156
-    }
157
-
158
-    /**
159
-     * Find many entities by their primary keys.
160
-     *
161
-     * @param  array $id
162
-     * @param  array $columns
163
-     * @return EntityCollection
164
-     */
165
-    public function findMany($id, $columns = ['*'])
166
-    {
167
-        if (empty($id)) {
168
-            return new EntityCollection;
169
-        }
170
-
171
-        $this->query->whereIn($this->entityMap->getKeyName(), $id);
172
-
173
-        return $this->get($columns);
174
-    }
175
-
176
-    /**
177
-     * Find a model by its primary key or throw an exception.
178
-     *
179
-     * @param  mixed $id
180
-     * @param  array $columns
181
-     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
182
-     * @return mixed|self
183
-     */
184
-    public function findOrFail($id, $columns = ['*'])
185
-    {
186
-        if (!is_null($entity = $this->find($id, $columns))) {
187
-            return $entity;
188
-        }
189
-
190
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
191
-    }
192
-
193
-
194
-    /**
195
-     * Execute the query and get the first result.
196
-     *
197
-     * @param  array $columns
198
-     * @return \Analogue\ORM\Entity
199
-     */
200
-    public function first($columns = ['*'])
201
-    {
202
-        return $this->take(1)->get($columns)->first();
203
-    }
204
-
205
-    /**
206
-     * Execute the query and get the first result or throw an exception.
207
-     *
208
-     * @param  array $columns
209
-     * @throws EntityNotFoundException
210
-     * @return \Analogue\ORM\Entity
211
-     */
212
-    public function firstOrFail($columns = ['*'])
213
-    {
214
-        if (!is_null($entity = $this->first($columns))) {
215
-            return $entity;
216
-        }
217
-
218
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
219
-    }
220
-
221
-    /**
222
-     * Pluck a single column from the database.
223
-     *
224
-     * @param  string $column
225
-     * @return mixed
226
-     */
227
-    public function pluck($column)
228
-    {
229
-        $result = $this->first([$column]);
230
-
231
-        if ($result) {
232
-            return $result->{$column};
233
-        }
234
-    }
235
-
236
-    /**
237
-     * Chunk the results of the query.
238
-     *
239
-     * @param  int      $count
240
-     * @param  callable $callback
241
-     * @return void
242
-     */
243
-    public function chunk($count, callable $callback)
244
-    {
245
-        $results = $this->forPage($page = 1, $count)->get();
246
-
247
-        while (count($results) > 0) {
248
-            // On each chunk result set, we will pass them to the callback and then let the
249
-            // developer take care of everything within the callback, which allows us to
250
-            // keep the memory low for spinning through large result sets for working.
251
-            call_user_func($callback, $results);
252
-
253
-            $page++;
254
-
255
-            $results = $this->forPage($page, $count)->get();
256
-        }
257
-    }
258
-
259
-    /**
260
-     * Get an array with the values of a given column.
261
-     *
262
-     * @param  string $column
263
-     * @param  string $key
264
-     * @return array
265
-     */
266
-    public function lists($column, $key = null)
267
-    {
268
-        return $this->query->pluck($column, $key);
269
-    }
270
-
271
-    /**
272
-     * Get a paginator for the "select" statement.
273
-     *
274
-     * @param  int   $perPage
275
-     * @param  array $columns
276
-     * @return LengthAwarePaginator
277
-     */
278
-    public function paginate($perPage = null, $columns = ['*'])
279
-    {
280
-        $total = $this->query->getCountForPagination();
281
-
282
-        $this->query->forPage(
283
-            $page = Paginator::resolveCurrentPage(),
284
-            $perPage = $perPage ?: $this->entityMap->getPerPage()
285
-        );
286
-
287
-        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
288
-            'path' => Paginator::resolveCurrentPath()
289
-        ]);
290
-    }
291
-
292
-    /**
293
-     * Get a paginator for a grouped statement.
294
-     *
295
-     * @param  \Illuminate\Pagination\Factory $paginator
296
-     * @param  int                            $perPage
297
-     * @param  array                          $columns
298
-     * @return \Illuminate\Pagination\Paginator
299
-     */
300
-    protected function groupedPaginate($paginator, $perPage, $columns)
301
-    {
302
-        $results = $this->get($columns)->all();
303
-
304
-        return $this->query->buildRawPaginator($paginator, $results, $perPage);
305
-    }
306
-
307
-    /**
308
-     * Get a paginator for an ungrouped statement.
309
-     *
310
-     * @param  \Illuminate\Pagination\Factory $paginator
311
-     * @param  int                            $perPage
312
-     * @param  array                          $columns
313
-     * @return \Illuminate\Pagination\Paginator
314
-     */
315
-    protected function ungroupedPaginate($paginator, $perPage, $columns)
316
-    {
317
-        $total = $this->query->getPaginationCount();
318
-
319
-        // Once we have the paginator we need to set the limit and offset values for
320
-        // the query so we can get the properly paginated items. Once we have an
321
-        // array of items we can create the paginator instances for the items.
322
-        $page = $paginator->getCurrentPage($total);
323
-
324
-        $this->query->forPage($page, $perPage);
325
-
326
-        return $paginator->make($this->get($columns)->all(), $total, $perPage);
327
-    }
328
-
329
-    /**
330
-     * Paginate the given query into a simple paginator.
331
-     *
332
-     * @param  int   $perPage
333
-     * @param  array $columns
334
-     * @return \Illuminate\Contracts\Pagination\Paginator
335
-     */
336
-    public function simplePaginate($perPage = null, $columns = ['*'])
337
-    {
338
-        $page = Paginator::resolveCurrentPage();
339
-
340
-        $perPage = $perPage ?: $this->entityMap->getPerPage();
341
-
342
-        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
343
-
344
-        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
345
-    }
346
-
347
-    /**
348
-     * Add a basic where clause to the query.
349
-     *
350
-     * @param  string $column
351
-     * @param  string $operator
352
-     * @param  mixed  $value
353
-     * @param  string $boolean
354
-     * @return $this
355
-     */
356
-    public function where($column, $operator = null, $value = null, $boolean = 'and')
357
-    {
358
-        if ($column instanceof Closure) {
359
-            $query = $this->newQueryWithoutScopes();
360
-
361
-            call_user_func($column, $query);
362
-
363
-            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
364
-        } else {
365
-            call_user_func_array([$this->query, 'where'], func_get_args());
366
-        }
367
-
368
-        return $this;
369
-    }
370
-
371
-    /**
372
-     * Add an "or where" clause to the query.
373
-     *
374
-     * @param  string $column
375
-     * @param  string $operator
376
-     * @param  mixed  $value
377
-     * @return \Analogue\ORM\System\Query
378
-     */
379
-    public function orWhere($column, $operator = null, $value = null)
380
-    {
381
-        return $this->where($column, $operator, $value, 'or');
382
-    }
383
-
384
-    /**
385
-     * Add a relationship count condition to the query.
386
-     *
387
-     * @param  string   $relation
388
-     * @param  string   $operator
389
-     * @param  int      $count
390
-     * @param  string   $boolean
391
-     * @param  \Closure $callback
392
-     * @return \Analogue\ORM\System\Query
393
-     */
394
-    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
395
-    {
396
-        $entity = $this->mapper->newInstance();
397
-
398
-        $relation = $this->getHasRelationQuery($relation, $entity);
399
-
400
-        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
401
-
402
-        if ($callback) {
403
-            call_user_func($callback, $query);
404
-        }
405
-
406
-        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
407
-    }
408
-
409
-    /**
410
-     * Add a relationship count condition to the query with where clauses.
411
-     *
412
-     * @param  string   $relation
413
-     * @param  \Closure $callback
414
-     * @param  string   $operator
415
-     * @param  int      $count
416
-     * @return \Analogue\ORM\System\Query
417
-     */
418
-    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
419
-    {
420
-        return $this->has($relation, $operator, $count, 'and', $callback);
421
-    }
422
-
423
-    /**
424
-     * Add a relationship count condition to the query with an "or".
425
-     *
426
-     * @param  string $relation
427
-     * @param  string $operator
428
-     * @param  int    $count
429
-     * @return \Analogue\ORM\System\Query
430
-     */
431
-    public function orHas($relation, $operator = '>=', $count = 1)
432
-    {
433
-        return $this->has($relation, $operator, $count, 'or');
434
-    }
435
-
436
-    /**
437
-     * Add a relationship count condition to the query with where clauses and an "or".
438
-     *
439
-     * @param  string   $relation
440
-     * @param  \Closure $callback
441
-     * @param  string   $operator
442
-     * @param  int      $count
443
-     * @return \Analogue\ORM\System\Query
444
-     */
445
-    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
446
-    {
447
-        return $this->has($relation, $operator, $count, 'or', $callback);
448
-    }
449
-
450
-    /**
451
-     * Add the "has" condition where clause to the query.
452
-     *
453
-     * @param  \Analogue\ORM\System\Query               $hasQuery
454
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
455
-     * @param  string                                   $operator
456
-     * @param  int                                      $count
457
-     * @param  string                                   $boolean
458
-     * @return \Analogue\ORM\System\Query
459
-     */
460
-    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
461
-    {
462
-        $this->mergeWheresToHas($hasQuery, $relation);
463
-
464
-        if (is_numeric($count)) {
465
-            $count = new Expression($count);
466
-        }
467
-
468
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
469
-    }
470
-
471
-    /**
472
-     * Merge the "wheres" from a relation query to a has query.
473
-     *
474
-     * @param  \Analogue\ORM\System\Query               $hasQuery
475
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
476
-     * @return void
477
-     */
478
-    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
479
-    {
480
-        // Here we have the "has" query and the original relation. We need to copy over any
481
-        // where clauses the developer may have put in the relationship function over to
482
-        // the has query, and then copy the bindings from the "has" query to the main.
483
-        $relationQuery = $relation->getBaseQuery();
484
-
485
-        $hasQuery->mergeWheres(
486
-            $relationQuery->wheres, $relationQuery->getBindings()
487
-        );
488
-
489
-        $this->query->mergeBindings($hasQuery->getQuery());
490
-    }
491
-
492
-    /**
493
-     * Get the "has relation" base query instance.
494
-     *
495
-     * @param  string $relation
496
-     * @param         $entity
497
-     * @return \Analogue\ORM\System\Query
498
-     */
499
-    protected function getHasRelationQuery($relation, $entity)
500
-    {
501
-        return Relationship::noConstraints(function () use ($relation, $entity) {
502
-            return $this->entityMap->$relation($entity);
503
-        });
504
-    }
505
-
506
-    /**
507
-     * Get the table for the current query object
508
-     *
509
-     * @return string
510
-     */
511
-    public function getTable()
512
-    {
513
-        return $this->entityMap->getTable();
514
-    }
515
-
516
-    /**
517
-     * Set the relationships that should be eager loaded.
518
-     *
519
-     * @param  mixed $relations
520
-     * @return $this
521
-     */
522
-    public function with($relations)
523
-    {
524
-        if (is_string($relations)) {
525
-            $relations = func_get_args();
526
-        }
527
-
528
-        $eagers = $this->parseRelations($relations);
529
-
530
-        $this->eagerLoad = array_merge($this->eagerLoad, $eagers);
531
-
532
-        return $this;
533
-    }
534
-
535
-    /**
536
-     * Parse a list of relations into individuals.
537
-     *
538
-     * @param  array $relations
539
-     * @return array
540
-     */
541
-    protected function parseRelations(array $relations)
542
-    {
543
-        $results = [];
544
-
545
-        foreach ($relations as $name => $constraints) {
546
-            // If the "relation" value is actually a numeric key, we can assume that no
547
-            // constraints have been specified for the eager load and we'll just put
548
-            // an empty Closure with the loader so that we can treat all the same.
549
-            if (is_numeric($name)) {
550
-                $f = function () {};
551
-
552
-                list($name, $constraints) = [$constraints, $f];
553
-            }
554
-
555
-            // We need to separate out any nested includes. Which allows the developers
556
-            // to load deep relationships using "dots" without stating each level of
557
-            // the relationship with its own key in the array of eager load names.
558
-            $results = $this->parseNested($name, $results);
559
-
560
-            $results[$name] = $constraints;
561
-        }
562
-
563
-        return $results;
564
-    }
565
-
566
-
567
-    /**
568
-     * Parse the nested relationships in a relation.
569
-     *
570
-     * @param  string $name
571
-     * @param  array  $results
572
-     * @return array
573
-     */
574
-    protected function parseNested($name, $results)
575
-    {
576
-        $progress = [];
577
-
578
-        // If the relation has already been set on the result array, we will not set it
579
-        // again, since that would override any constraints that were already placed
580
-        // on the relationships. We will only set the ones that are not specified.
581
-        foreach (explode('.', $name) as $segment) {
582
-            $progress[] = $segment;
583
-
584
-            if (!isset($results[$last = implode('.', $progress)])) {
585
-                $results[$last] = function () {};
586
-            }
587
-        }
588
-
589
-        return $results;
590
-    }
591
-
592
-    /**
593
-     * Get the relationships being eagerly loaded.
594
-     *
595
-     * @return array
596
-     */
597
-    public function getEagerLoads()
598
-    {
599
-        return $this->eagerLoad;
600
-    }
601
-
602
-    /**
603
-     * Set the relationships being eagerly loaded.
604
-     *
605
-     * @param  array $eagerLoad
606
-     * @return void
607
-     */
608
-    public function setEagerLoads(array $eagerLoad)
609
-    {
610
-        $this->eagerLoad = $eagerLoad;
611
-    }
612
-
613
-    /**
614
-     * Eager load the relationships for the entities.
615
-     *
616
-     * @param  array $entities
617
-     * @return array
618
-     */
619
-    public function eagerLoadRelations($entities)
620
-    {
621
-        foreach ($this->eagerLoad as $name => $constraints) {
622
-            // For nested eager loads we'll skip loading them here and they will be set as an
623
-            // eager load on the query to retrieve the relation so that they will be eager
624
-            // loaded on that query, because that is where they get hydrated as models.
625
-            if (strpos($name, '.') === false) {
626
-                $entities = $this->loadRelation($entities, $name, $constraints);
627
-            }
628
-        }
629
-
630
-        return $entities;
631
-    }
632
-
633
-    /**
634
-     * Eagerly load the relationship on a set of entities.
635
-     *
636
-     * @param  array    $entities
637
-     * @param  string   $name
638
-     * @param  \Closure $constraints
639
-     * @return array
640
-     */
641
-    protected function loadRelation(array $entities, $name, Closure $constraints)
642
-    {
643
-        // First we will "back up" the existing where conditions on the query so we can
644
-        // add our eager constraints. Then we will merge the wheres that were on the
645
-        // query back to it in order that any where conditions might be specified.
646
-        $relation = $this->getRelation($name);
647
-
648
-        $relation->addEagerConstraints($entities);
649
-
650
-        call_user_func($constraints, $relation);
651
-
652
-        $entities = $relation->initRelation($entities, $name);
653
-
654
-        // Once we have the results, we just match those back up to their parent models
655
-        // using the relationship instance. Then we just return the finished arrays
656
-        // of models which have been eagerly hydrated and are readied for return.
657
-
658
-        $results = $relation->getEager();
659
-
660
-        return $relation->match($entities, $results, $name);
661
-    }
662
-
663
-    /**
664
-     * Get the relation instance for the given relation name.
665
-     *
666
-     * @param  string $relation
667
-     * @return \Analogue\ORM\Relationships\Relationship
668
-     */
669
-    public function getRelation($relation)
670
-    {
671
-        // We want to run a relationship query without any constrains so that we will
672
-        // not have to remove these where clauses manually which gets really hacky
673
-        // and is error prone while we remove the developer's own where clauses.
674
-        $query = Relationship::noConstraints(function () use ($relation) {
675
-            return $this->entityMap->$relation($this->getEntityInstance());
676
-        });
677
-
678
-        $nested = $this->nestedRelations($relation);
679
-
680
-        // If there are nested relationships set on the query, we will put those onto
681
-        // the query instances so that they can be handled after this relationship
682
-        // is loaded. In this way they will all trickle down as they are loaded.
683
-        if (count($nested) > 0) {
684
-            $query->getQuery()->with($nested);
685
-        }
686
-
687
-        return $query;
688
-    }
689
-
690
-    /**
691
-     * Get the deeply nested relations for a given top-level relation.
692
-     *
693
-     * @param  string $relation
694
-     * @return array
695
-     */
696
-    protected function nestedRelations($relation)
697
-    {
698
-        $nested = [];
699
-
700
-        // We are basically looking for any relationships that are nested deeper than
701
-        // the given top-level relationship. We will just check for any relations
702
-        // that start with the given top relations and adds them to our arrays.
703
-        foreach ($this->eagerLoad as $name => $constraints) {
704
-            if ($this->isNested($name, $relation)) {
705
-                $nested[substr($name, strlen($relation . '.'))] = $constraints;
706
-            }
707
-        }
708
-
709
-        return $nested;
710
-    }
711
-
712
-    /**
713
-     * Determine if the relationship is nested.
714
-     *
715
-     * @param  string $name
716
-     * @param  string $relation
717
-     * @return bool
718
-     */
719
-    protected function isNested($name, $relation)
720
-    {
721
-        $dots = str_contains($name, '.');
722
-
723
-        return $dots && starts_with($name, $relation . '.');
724
-    }
725
-
726
-    /**
727
-     * Add the Entity primary key if not in requested columns
728
-     *
729
-     * @param  array $columns
730
-     * @return array
731
-     */
732
-    protected function enforceIdColumn($columns)
733
-    {
734
-        if (!in_array($this->entityMap->getKeyName(), $columns)) {
735
-            $columns[] = $this->entityMap->getKeyName();
736
-        }
737
-        return $columns;
738
-    }
739
-
740
-    /**
741
-     * Get the hydrated models without eager loading.
742
-     *
743
-     * @param  array  $columns
744
-     * @return \Analogue\ORM\EntityCollection
745
-     */
746
-    public function getEntities($columns = ['*'])
747
-    {
748
-        // As we need the primary key to feed the
749
-        // entity cache, we need it loaded on each
750
-        // request
751
-        $columns = $this->enforceIdColumn($columns);
752
-
753
-        // Run the query
754
-        $results = $this->query->get($columns)->toArray();
755
-
756
-        // Create a result builder.
757
-        $builder = new ResultBuilder(Manager::getInstance(), $this->mapper, array_keys($this->getEagerLoads()));
758
-
759
-        return $builder->build($results);
760
-    }
761
-
762
-    /**
763
-     * Get a new instance for the entity
764
-     *
765
-     * @return \Analogue\ORM\Entity
766
-     */
767
-    public function getEntityInstance()
768
-    {
769
-        return $this->mapper->newInstance();
770
-    }
771
-
772
-    /**
773
-     * Extend the builder with a given callback.
774
-     *
775
-     * @param  string   $name
776
-     * @param  \Closure $callback
777
-     * @return void
778
-     */
779
-    public function macro($name, Closure $callback)
780
-    {
781
-        $this->macros[$name] = $callback;
782
-    }
783
-
784
-    /**
785
-     * Get the given macro by name.
786
-     *
787
-     * @param  string $name
788
-     * @return \Closure
789
-     */
790
-    public function getMacro($name)
791
-    {
792
-        return array_get($this->macros, $name);
793
-    }
794
-
795
-    /**
796
-     * Get a new query builder for the model's table.
797
-     *
798
-     * @return \Analogue\ORM\System\Query
799
-     */
800
-    public function newQuery()
801
-    {
802
-        $builder = new Query($this->mapper, $this->adapter);
803
-
804
-        return $this->applyGlobalScopes($builder);
805
-    }
806
-
807
-    /**
808
-     * Get a new query builder without any scope applied.
809
-     *
810
-     * @return \Analogue\ORM\System\Query
811
-     */
812
-    public function newQueryWithoutScopes()
813
-    {
814
-        return new Query($this->mapper, $this->adapter);
815
-    }
816
-
817
-    /**
818
-     * Get the Mapper instance for this Query Builder
819
-     *
820
-     * @return \Analogue\ORM\System\Mapper
821
-     */
822
-    public function getMapper()
823
-    {
824
-        return $this->mapper;
825
-    }
826
-
827
-    /**
828
-     * Get the underlying query adapter
829
-     *
830
-     * (REFACTOR: this method should move out, we need to provide the client classes
831
-     * with the adapter instead.)
832
-     *
833
-     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
834
-     */
835
-    public function getQuery()
836
-    {
837
-        return $this->query;
838
-    }
839
-
840
-    /**
841
-     * Dynamically handle calls into the query instance.
842
-     *
843
-     * @param  string $method
844
-     * @param  array  $parameters
845
-     * @throws Exception
846
-     * @return mixed
847
-     */
848
-    public function __call($method, $parameters)
849
-    {
850
-        if (isset($this->macros[$method])) {
851
-            array_unshift($parameters, $this);
852
-
853
-            return call_user_func_array($this->macros[$method], $parameters);
854
-        }
855
-
856
-        if (in_array($method, $this->blacklist)) {
857
-            throw new Exception("Method $method doesn't exist");
858
-        }
859
-
860
-        $result = call_user_func_array([$this->query, $method], $parameters);
861
-
862
-        return in_array($method, $this->passthru) ? $result : $this;
863
-    }
22
+	/**
23
+	 * Mapper Instance
24
+	 *
25
+	 * @var \Analogue\ORM\System\Mapper
26
+	 */
27
+	protected $mapper;
28
+
29
+	/**
30
+	 * DB Adatper
31
+	 *
32
+	 * @var \Analogue\ORM\Drivers\DBAdapter
33
+	 */
34
+	protected $adapter;
35
+
36
+	/**
37
+	 * Query Builder Instance
38
+	 *
39
+	 * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
40
+	 */
41
+	protected $query;
42
+
43
+	/**
44
+	 * Entity Map Instance
45
+	 *
46
+	 * @var \Analogue\ORM\EntityMap
47
+	 */
48
+	protected $entityMap;
49
+
50
+	/**
51
+	 * The relationships that should be eager loaded.
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $eagerLoad = [];
56
+
57
+	/**
58
+	 * All of the registered builder macros.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $macros = [];
63
+
64
+	/**
65
+	 * The methods that should be returned from query builder.
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $passthru = [
70
+		'toSql',
71
+		'lists',
72
+		'pluck',
73
+		'count',
74
+		'min',
75
+		'max',
76
+		'avg',
77
+		'sum',
78
+		'exists',
79
+		'getBindings',
80
+	];
81
+
82
+	/**
83
+	 * Query Builder Blacklist
84
+	 */
85
+	protected $blacklist = [
86
+		'insert',
87
+		'insertGetId',
88
+		'lock',
89
+		'lockForUpdate',
90
+		'sharedLock',
91
+		'update',
92
+		'increment',
93
+		'decrement',
94
+		'delete',
95
+		'truncate',
96
+		'raw',
97
+	];
98
+
99
+	/**
100
+	 * Create a new Analogue Query Builder instance.
101
+	 *
102
+	 * @param  Mapper    $mapper
103
+	 * @param  DBAdapter $adapter
104
+	 */
105
+	public function __construct(Mapper $mapper, DBAdapter $adapter)
106
+	{
107
+		$this->mapper = $mapper;
108
+
109
+		$this->adapter = $adapter;
110
+
111
+		$this->entityMap = $mapper->getEntityMap();
112
+
113
+		// Specify the table to work on
114
+		$this->query = $adapter->getQuery()->from($this->entityMap->getTable());
115
+
116
+		$this->with($this->entityMap->getEagerloadedRelationships());
117
+	}
118
+
119
+	/**
120
+	 * Run the query and return the result
121
+	 *
122
+	 * @param  array $columns
123
+	 * @return \Analogue\ORM\EntityCollection
124
+	 */
125
+	public function get($columns = ['*'])
126
+	{
127
+		$entities = $this->getEntities($columns);
128
+
129
+		// If we actually found models we will also eager load any relationships that
130
+		// have been specified as needing to be eager loaded, which will solve the
131
+		// n+1 query issue for the developers to avoid running a lot of queries.
132
+
133
+		if (count($entities) > 0) {
134
+			$entities = $this->eagerLoadRelations($entities);
135
+		}
136
+
137
+		return $this->entityMap->newCollection($entities);
138
+	}
139
+
140
+	/**
141
+	 * Find an entity by its primary key
142
+	 *
143
+	 * @param  string|integer $id
144
+	 * @param  array          $columns
145
+	 * @return \Analogue\ORM\Mappable
146
+	 */
147
+	public function find($id, $columns = ['*'])
148
+	{
149
+		if (is_array($id)) {
150
+			return $this->findMany($id, $columns);
151
+		}
152
+
153
+		$this->query->where($this->entityMap->getKeyName(), '=', $id);
154
+
155
+		return $this->first($columns);
156
+	}
157
+
158
+	/**
159
+	 * Find many entities by their primary keys.
160
+	 *
161
+	 * @param  array $id
162
+	 * @param  array $columns
163
+	 * @return EntityCollection
164
+	 */
165
+	public function findMany($id, $columns = ['*'])
166
+	{
167
+		if (empty($id)) {
168
+			return new EntityCollection;
169
+		}
170
+
171
+		$this->query->whereIn($this->entityMap->getKeyName(), $id);
172
+
173
+		return $this->get($columns);
174
+	}
175
+
176
+	/**
177
+	 * Find a model by its primary key or throw an exception.
178
+	 *
179
+	 * @param  mixed $id
180
+	 * @param  array $columns
181
+	 * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
182
+	 * @return mixed|self
183
+	 */
184
+	public function findOrFail($id, $columns = ['*'])
185
+	{
186
+		if (!is_null($entity = $this->find($id, $columns))) {
187
+			return $entity;
188
+		}
189
+
190
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
191
+	}
192
+
193
+
194
+	/**
195
+	 * Execute the query and get the first result.
196
+	 *
197
+	 * @param  array $columns
198
+	 * @return \Analogue\ORM\Entity
199
+	 */
200
+	public function first($columns = ['*'])
201
+	{
202
+		return $this->take(1)->get($columns)->first();
203
+	}
204
+
205
+	/**
206
+	 * Execute the query and get the first result or throw an exception.
207
+	 *
208
+	 * @param  array $columns
209
+	 * @throws EntityNotFoundException
210
+	 * @return \Analogue\ORM\Entity
211
+	 */
212
+	public function firstOrFail($columns = ['*'])
213
+	{
214
+		if (!is_null($entity = $this->first($columns))) {
215
+			return $entity;
216
+		}
217
+
218
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
219
+	}
220
+
221
+	/**
222
+	 * Pluck a single column from the database.
223
+	 *
224
+	 * @param  string $column
225
+	 * @return mixed
226
+	 */
227
+	public function pluck($column)
228
+	{
229
+		$result = $this->first([$column]);
230
+
231
+		if ($result) {
232
+			return $result->{$column};
233
+		}
234
+	}
235
+
236
+	/**
237
+	 * Chunk the results of the query.
238
+	 *
239
+	 * @param  int      $count
240
+	 * @param  callable $callback
241
+	 * @return void
242
+	 */
243
+	public function chunk($count, callable $callback)
244
+	{
245
+		$results = $this->forPage($page = 1, $count)->get();
246
+
247
+		while (count($results) > 0) {
248
+			// On each chunk result set, we will pass them to the callback and then let the
249
+			// developer take care of everything within the callback, which allows us to
250
+			// keep the memory low for spinning through large result sets for working.
251
+			call_user_func($callback, $results);
252
+
253
+			$page++;
254
+
255
+			$results = $this->forPage($page, $count)->get();
256
+		}
257
+	}
258
+
259
+	/**
260
+	 * Get an array with the values of a given column.
261
+	 *
262
+	 * @param  string $column
263
+	 * @param  string $key
264
+	 * @return array
265
+	 */
266
+	public function lists($column, $key = null)
267
+	{
268
+		return $this->query->pluck($column, $key);
269
+	}
270
+
271
+	/**
272
+	 * Get a paginator for the "select" statement.
273
+	 *
274
+	 * @param  int   $perPage
275
+	 * @param  array $columns
276
+	 * @return LengthAwarePaginator
277
+	 */
278
+	public function paginate($perPage = null, $columns = ['*'])
279
+	{
280
+		$total = $this->query->getCountForPagination();
281
+
282
+		$this->query->forPage(
283
+			$page = Paginator::resolveCurrentPage(),
284
+			$perPage = $perPage ?: $this->entityMap->getPerPage()
285
+		);
286
+
287
+		return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
288
+			'path' => Paginator::resolveCurrentPath()
289
+		]);
290
+	}
291
+
292
+	/**
293
+	 * Get a paginator for a grouped statement.
294
+	 *
295
+	 * @param  \Illuminate\Pagination\Factory $paginator
296
+	 * @param  int                            $perPage
297
+	 * @param  array                          $columns
298
+	 * @return \Illuminate\Pagination\Paginator
299
+	 */
300
+	protected function groupedPaginate($paginator, $perPage, $columns)
301
+	{
302
+		$results = $this->get($columns)->all();
303
+
304
+		return $this->query->buildRawPaginator($paginator, $results, $perPage);
305
+	}
306
+
307
+	/**
308
+	 * Get a paginator for an ungrouped statement.
309
+	 *
310
+	 * @param  \Illuminate\Pagination\Factory $paginator
311
+	 * @param  int                            $perPage
312
+	 * @param  array                          $columns
313
+	 * @return \Illuminate\Pagination\Paginator
314
+	 */
315
+	protected function ungroupedPaginate($paginator, $perPage, $columns)
316
+	{
317
+		$total = $this->query->getPaginationCount();
318
+
319
+		// Once we have the paginator we need to set the limit and offset values for
320
+		// the query so we can get the properly paginated items. Once we have an
321
+		// array of items we can create the paginator instances for the items.
322
+		$page = $paginator->getCurrentPage($total);
323
+
324
+		$this->query->forPage($page, $perPage);
325
+
326
+		return $paginator->make($this->get($columns)->all(), $total, $perPage);
327
+	}
328
+
329
+	/**
330
+	 * Paginate the given query into a simple paginator.
331
+	 *
332
+	 * @param  int   $perPage
333
+	 * @param  array $columns
334
+	 * @return \Illuminate\Contracts\Pagination\Paginator
335
+	 */
336
+	public function simplePaginate($perPage = null, $columns = ['*'])
337
+	{
338
+		$page = Paginator::resolveCurrentPage();
339
+
340
+		$perPage = $perPage ?: $this->entityMap->getPerPage();
341
+
342
+		$this->skip(($page - 1) * $perPage)->take($perPage + 1);
343
+
344
+		return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
345
+	}
346
+
347
+	/**
348
+	 * Add a basic where clause to the query.
349
+	 *
350
+	 * @param  string $column
351
+	 * @param  string $operator
352
+	 * @param  mixed  $value
353
+	 * @param  string $boolean
354
+	 * @return $this
355
+	 */
356
+	public function where($column, $operator = null, $value = null, $boolean = 'and')
357
+	{
358
+		if ($column instanceof Closure) {
359
+			$query = $this->newQueryWithoutScopes();
360
+
361
+			call_user_func($column, $query);
362
+
363
+			$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
364
+		} else {
365
+			call_user_func_array([$this->query, 'where'], func_get_args());
366
+		}
367
+
368
+		return $this;
369
+	}
370
+
371
+	/**
372
+	 * Add an "or where" clause to the query.
373
+	 *
374
+	 * @param  string $column
375
+	 * @param  string $operator
376
+	 * @param  mixed  $value
377
+	 * @return \Analogue\ORM\System\Query
378
+	 */
379
+	public function orWhere($column, $operator = null, $value = null)
380
+	{
381
+		return $this->where($column, $operator, $value, 'or');
382
+	}
383
+
384
+	/**
385
+	 * Add a relationship count condition to the query.
386
+	 *
387
+	 * @param  string   $relation
388
+	 * @param  string   $operator
389
+	 * @param  int      $count
390
+	 * @param  string   $boolean
391
+	 * @param  \Closure $callback
392
+	 * @return \Analogue\ORM\System\Query
393
+	 */
394
+	public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
395
+	{
396
+		$entity = $this->mapper->newInstance();
397
+
398
+		$relation = $this->getHasRelationQuery($relation, $entity);
399
+
400
+		$query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
401
+
402
+		if ($callback) {
403
+			call_user_func($callback, $query);
404
+		}
405
+
406
+		return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
407
+	}
408
+
409
+	/**
410
+	 * Add a relationship count condition to the query with where clauses.
411
+	 *
412
+	 * @param  string   $relation
413
+	 * @param  \Closure $callback
414
+	 * @param  string   $operator
415
+	 * @param  int      $count
416
+	 * @return \Analogue\ORM\System\Query
417
+	 */
418
+	public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
419
+	{
420
+		return $this->has($relation, $operator, $count, 'and', $callback);
421
+	}
422
+
423
+	/**
424
+	 * Add a relationship count condition to the query with an "or".
425
+	 *
426
+	 * @param  string $relation
427
+	 * @param  string $operator
428
+	 * @param  int    $count
429
+	 * @return \Analogue\ORM\System\Query
430
+	 */
431
+	public function orHas($relation, $operator = '>=', $count = 1)
432
+	{
433
+		return $this->has($relation, $operator, $count, 'or');
434
+	}
435
+
436
+	/**
437
+	 * Add a relationship count condition to the query with where clauses and an "or".
438
+	 *
439
+	 * @param  string   $relation
440
+	 * @param  \Closure $callback
441
+	 * @param  string   $operator
442
+	 * @param  int      $count
443
+	 * @return \Analogue\ORM\System\Query
444
+	 */
445
+	public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
446
+	{
447
+		return $this->has($relation, $operator, $count, 'or', $callback);
448
+	}
449
+
450
+	/**
451
+	 * Add the "has" condition where clause to the query.
452
+	 *
453
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
454
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
455
+	 * @param  string                                   $operator
456
+	 * @param  int                                      $count
457
+	 * @param  string                                   $boolean
458
+	 * @return \Analogue\ORM\System\Query
459
+	 */
460
+	protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
461
+	{
462
+		$this->mergeWheresToHas($hasQuery, $relation);
463
+
464
+		if (is_numeric($count)) {
465
+			$count = new Expression($count);
466
+		}
467
+
468
+		return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
469
+	}
470
+
471
+	/**
472
+	 * Merge the "wheres" from a relation query to a has query.
473
+	 *
474
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
475
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
476
+	 * @return void
477
+	 */
478
+	protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
479
+	{
480
+		// Here we have the "has" query and the original relation. We need to copy over any
481
+		// where clauses the developer may have put in the relationship function over to
482
+		// the has query, and then copy the bindings from the "has" query to the main.
483
+		$relationQuery = $relation->getBaseQuery();
484
+
485
+		$hasQuery->mergeWheres(
486
+			$relationQuery->wheres, $relationQuery->getBindings()
487
+		);
488
+
489
+		$this->query->mergeBindings($hasQuery->getQuery());
490
+	}
491
+
492
+	/**
493
+	 * Get the "has relation" base query instance.
494
+	 *
495
+	 * @param  string $relation
496
+	 * @param         $entity
497
+	 * @return \Analogue\ORM\System\Query
498
+	 */
499
+	protected function getHasRelationQuery($relation, $entity)
500
+	{
501
+		return Relationship::noConstraints(function () use ($relation, $entity) {
502
+			return $this->entityMap->$relation($entity);
503
+		});
504
+	}
505
+
506
+	/**
507
+	 * Get the table for the current query object
508
+	 *
509
+	 * @return string
510
+	 */
511
+	public function getTable()
512
+	{
513
+		return $this->entityMap->getTable();
514
+	}
515
+
516
+	/**
517
+	 * Set the relationships that should be eager loaded.
518
+	 *
519
+	 * @param  mixed $relations
520
+	 * @return $this
521
+	 */
522
+	public function with($relations)
523
+	{
524
+		if (is_string($relations)) {
525
+			$relations = func_get_args();
526
+		}
527
+
528
+		$eagers = $this->parseRelations($relations);
529
+
530
+		$this->eagerLoad = array_merge($this->eagerLoad, $eagers);
531
+
532
+		return $this;
533
+	}
534
+
535
+	/**
536
+	 * Parse a list of relations into individuals.
537
+	 *
538
+	 * @param  array $relations
539
+	 * @return array
540
+	 */
541
+	protected function parseRelations(array $relations)
542
+	{
543
+		$results = [];
544
+
545
+		foreach ($relations as $name => $constraints) {
546
+			// If the "relation" value is actually a numeric key, we can assume that no
547
+			// constraints have been specified for the eager load and we'll just put
548
+			// an empty Closure with the loader so that we can treat all the same.
549
+			if (is_numeric($name)) {
550
+				$f = function () {};
551
+
552
+				list($name, $constraints) = [$constraints, $f];
553
+			}
554
+
555
+			// We need to separate out any nested includes. Which allows the developers
556
+			// to load deep relationships using "dots" without stating each level of
557
+			// the relationship with its own key in the array of eager load names.
558
+			$results = $this->parseNested($name, $results);
559
+
560
+			$results[$name] = $constraints;
561
+		}
562
+
563
+		return $results;
564
+	}
565
+
566
+
567
+	/**
568
+	 * Parse the nested relationships in a relation.
569
+	 *
570
+	 * @param  string $name
571
+	 * @param  array  $results
572
+	 * @return array
573
+	 */
574
+	protected function parseNested($name, $results)
575
+	{
576
+		$progress = [];
577
+
578
+		// If the relation has already been set on the result array, we will not set it
579
+		// again, since that would override any constraints that were already placed
580
+		// on the relationships. We will only set the ones that are not specified.
581
+		foreach (explode('.', $name) as $segment) {
582
+			$progress[] = $segment;
583
+
584
+			if (!isset($results[$last = implode('.', $progress)])) {
585
+				$results[$last] = function () {};
586
+			}
587
+		}
588
+
589
+		return $results;
590
+	}
591
+
592
+	/**
593
+	 * Get the relationships being eagerly loaded.
594
+	 *
595
+	 * @return array
596
+	 */
597
+	public function getEagerLoads()
598
+	{
599
+		return $this->eagerLoad;
600
+	}
601
+
602
+	/**
603
+	 * Set the relationships being eagerly loaded.
604
+	 *
605
+	 * @param  array $eagerLoad
606
+	 * @return void
607
+	 */
608
+	public function setEagerLoads(array $eagerLoad)
609
+	{
610
+		$this->eagerLoad = $eagerLoad;
611
+	}
612
+
613
+	/**
614
+	 * Eager load the relationships for the entities.
615
+	 *
616
+	 * @param  array $entities
617
+	 * @return array
618
+	 */
619
+	public function eagerLoadRelations($entities)
620
+	{
621
+		foreach ($this->eagerLoad as $name => $constraints) {
622
+			// For nested eager loads we'll skip loading them here and they will be set as an
623
+			// eager load on the query to retrieve the relation so that they will be eager
624
+			// loaded on that query, because that is where they get hydrated as models.
625
+			if (strpos($name, '.') === false) {
626
+				$entities = $this->loadRelation($entities, $name, $constraints);
627
+			}
628
+		}
629
+
630
+		return $entities;
631
+	}
632
+
633
+	/**
634
+	 * Eagerly load the relationship on a set of entities.
635
+	 *
636
+	 * @param  array    $entities
637
+	 * @param  string   $name
638
+	 * @param  \Closure $constraints
639
+	 * @return array
640
+	 */
641
+	protected function loadRelation(array $entities, $name, Closure $constraints)
642
+	{
643
+		// First we will "back up" the existing where conditions on the query so we can
644
+		// add our eager constraints. Then we will merge the wheres that were on the
645
+		// query back to it in order that any where conditions might be specified.
646
+		$relation = $this->getRelation($name);
647
+
648
+		$relation->addEagerConstraints($entities);
649
+
650
+		call_user_func($constraints, $relation);
651
+
652
+		$entities = $relation->initRelation($entities, $name);
653
+
654
+		// Once we have the results, we just match those back up to their parent models
655
+		// using the relationship instance. Then we just return the finished arrays
656
+		// of models which have been eagerly hydrated and are readied for return.
657
+
658
+		$results = $relation->getEager();
659
+
660
+		return $relation->match($entities, $results, $name);
661
+	}
662
+
663
+	/**
664
+	 * Get the relation instance for the given relation name.
665
+	 *
666
+	 * @param  string $relation
667
+	 * @return \Analogue\ORM\Relationships\Relationship
668
+	 */
669
+	public function getRelation($relation)
670
+	{
671
+		// We want to run a relationship query without any constrains so that we will
672
+		// not have to remove these where clauses manually which gets really hacky
673
+		// and is error prone while we remove the developer's own where clauses.
674
+		$query = Relationship::noConstraints(function () use ($relation) {
675
+			return $this->entityMap->$relation($this->getEntityInstance());
676
+		});
677
+
678
+		$nested = $this->nestedRelations($relation);
679
+
680
+		// If there are nested relationships set on the query, we will put those onto
681
+		// the query instances so that they can be handled after this relationship
682
+		// is loaded. In this way they will all trickle down as they are loaded.
683
+		if (count($nested) > 0) {
684
+			$query->getQuery()->with($nested);
685
+		}
686
+
687
+		return $query;
688
+	}
689
+
690
+	/**
691
+	 * Get the deeply nested relations for a given top-level relation.
692
+	 *
693
+	 * @param  string $relation
694
+	 * @return array
695
+	 */
696
+	protected function nestedRelations($relation)
697
+	{
698
+		$nested = [];
699
+
700
+		// We are basically looking for any relationships that are nested deeper than
701
+		// the given top-level relationship. We will just check for any relations
702
+		// that start with the given top relations and adds them to our arrays.
703
+		foreach ($this->eagerLoad as $name => $constraints) {
704
+			if ($this->isNested($name, $relation)) {
705
+				$nested[substr($name, strlen($relation . '.'))] = $constraints;
706
+			}
707
+		}
708
+
709
+		return $nested;
710
+	}
711
+
712
+	/**
713
+	 * Determine if the relationship is nested.
714
+	 *
715
+	 * @param  string $name
716
+	 * @param  string $relation
717
+	 * @return bool
718
+	 */
719
+	protected function isNested($name, $relation)
720
+	{
721
+		$dots = str_contains($name, '.');
722
+
723
+		return $dots && starts_with($name, $relation . '.');
724
+	}
725
+
726
+	/**
727
+	 * Add the Entity primary key if not in requested columns
728
+	 *
729
+	 * @param  array $columns
730
+	 * @return array
731
+	 */
732
+	protected function enforceIdColumn($columns)
733
+	{
734
+		if (!in_array($this->entityMap->getKeyName(), $columns)) {
735
+			$columns[] = $this->entityMap->getKeyName();
736
+		}
737
+		return $columns;
738
+	}
739
+
740
+	/**
741
+	 * Get the hydrated models without eager loading.
742
+	 *
743
+	 * @param  array  $columns
744
+	 * @return \Analogue\ORM\EntityCollection
745
+	 */
746
+	public function getEntities($columns = ['*'])
747
+	{
748
+		// As we need the primary key to feed the
749
+		// entity cache, we need it loaded on each
750
+		// request
751
+		$columns = $this->enforceIdColumn($columns);
752
+
753
+		// Run the query
754
+		$results = $this->query->get($columns)->toArray();
755
+
756
+		// Create a result builder.
757
+		$builder = new ResultBuilder(Manager::getInstance(), $this->mapper, array_keys($this->getEagerLoads()));
758
+
759
+		return $builder->build($results);
760
+	}
761
+
762
+	/**
763
+	 * Get a new instance for the entity
764
+	 *
765
+	 * @return \Analogue\ORM\Entity
766
+	 */
767
+	public function getEntityInstance()
768
+	{
769
+		return $this->mapper->newInstance();
770
+	}
771
+
772
+	/**
773
+	 * Extend the builder with a given callback.
774
+	 *
775
+	 * @param  string   $name
776
+	 * @param  \Closure $callback
777
+	 * @return void
778
+	 */
779
+	public function macro($name, Closure $callback)
780
+	{
781
+		$this->macros[$name] = $callback;
782
+	}
783
+
784
+	/**
785
+	 * Get the given macro by name.
786
+	 *
787
+	 * @param  string $name
788
+	 * @return \Closure
789
+	 */
790
+	public function getMacro($name)
791
+	{
792
+		return array_get($this->macros, $name);
793
+	}
794
+
795
+	/**
796
+	 * Get a new query builder for the model's table.
797
+	 *
798
+	 * @return \Analogue\ORM\System\Query
799
+	 */
800
+	public function newQuery()
801
+	{
802
+		$builder = new Query($this->mapper, $this->adapter);
803
+
804
+		return $this->applyGlobalScopes($builder);
805
+	}
806
+
807
+	/**
808
+	 * Get a new query builder without any scope applied.
809
+	 *
810
+	 * @return \Analogue\ORM\System\Query
811
+	 */
812
+	public function newQueryWithoutScopes()
813
+	{
814
+		return new Query($this->mapper, $this->adapter);
815
+	}
816
+
817
+	/**
818
+	 * Get the Mapper instance for this Query Builder
819
+	 *
820
+	 * @return \Analogue\ORM\System\Mapper
821
+	 */
822
+	public function getMapper()
823
+	{
824
+		return $this->mapper;
825
+	}
826
+
827
+	/**
828
+	 * Get the underlying query adapter
829
+	 *
830
+	 * (REFACTOR: this method should move out, we need to provide the client classes
831
+	 * with the adapter instead.)
832
+	 *
833
+	 * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
834
+	 */
835
+	public function getQuery()
836
+	{
837
+		return $this->query;
838
+	}
839
+
840
+	/**
841
+	 * Dynamically handle calls into the query instance.
842
+	 *
843
+	 * @param  string $method
844
+	 * @param  array  $parameters
845
+	 * @throws Exception
846
+	 * @return mixed
847
+	 */
848
+	public function __call($method, $parameters)
849
+	{
850
+		if (isset($this->macros[$method])) {
851
+			array_unshift($parameters, $this);
852
+
853
+			return call_user_func_array($this->macros[$method], $parameters);
854
+		}
855
+
856
+		if (in_array($method, $this->blacklist)) {
857
+			throw new Exception("Method $method doesn't exist");
858
+		}
859
+
860
+		$result = call_user_func_array([$this->query, $method], $parameters);
861
+
862
+		return in_array($method, $this->passthru) ? $result : $this;
863
+	}
864 864
 }
865 865
\ No newline at end of file
Please login to merge, or discard this patch.