Completed
Push — 5.1 ( e03b8d...78924a )
by Rémi
02:58
created
src/Commands/Store.php 1 patch
Indentation   +227 added lines, -227 removed lines patch added patch discarded remove patch
@@ -14,241 +14,241 @@
 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
-        }
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 48
         
49
-        /**
50
-         * We'll only run an update if the entity
51
-         * is actually dirty
52
-         */
53
-        if ($this->aggregate->isDirty()) {
54
-            if ($mapper->fireEvent('updating', $entity) === false) {
55
-                return false;
56
-            }
57
-            $this->update();
58
-
59
-            $mapper->fireEvent('updated', $entity, false);
60
-        }
61
-
62
-        $this->postStoreProcess();
63
-
64
-        $mapper->fireEvent('stored', $entity, false);
65
-
66
-        return $entity;
67
-    }
68
-
69
-    /**
70
-     * Run all operations that have to occur before actually
71
-     * storing the entity
72
-     *
73
-     * @throws \InvalidArgumentException
74
-     * @return void
75
-     */
76
-    protected function preStoreProcess()
77
-    {
78
-        // Create any related object that doesn't exist in the database.
79
-        $localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
80
-
81
-        $this->createRelatedEntities($localRelationships);
82
-
83
-        // Now we can sync the related collections
84
-        $this->aggregate->syncRelationships($localRelationships);
85
-    }
86
-
87
-    /**
88
-     * Check for existence and create non-existing related entities
89
-     *
90
-     * @param  array
91
-     * @throws \InvalidArgumentException
92
-     * @return void
93
-     */
94
-    protected function createRelatedEntities($relations)
95
-    {
96
-        $entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
97
-
98
-        foreach ($entitiesToCreate as $aggregate) {
99
-            $this->createStoreCommand($aggregate)->execute();
100
-        }
101
-    }
102
-
103
-    /**
104
-     * Create a new store command
105
-     *
106
-     * @param  Aggregate $aggregate
107
-     * @return Store
108
-     */
109
-    protected function createStoreCommand(Aggregate $aggregate)
110
-    {
111
-        // We gotta retrieve the corresponding query adapter to use.
112
-        $mapper = $aggregate->getMapper();
113
-
114
-        return new Store($aggregate, $mapper->newQueryBuilder());
115
-    }
116
-
117
-    /**
118
-     * Run all operations that have to occur after the entity
119
-     * is stored.
120
-     *
121
-     * @throws \InvalidArgumentException
122
-     * @return void
123
-     */
124
-    protected function postStoreProcess()
125
-    {
126
-        $aggregate = $this->aggregate;
127
-
128
-        // Create any related object that doesn't exist in the database.
129
-        $foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
130
-        $this->createRelatedEntities($foreignRelationships);
131
-
132
-        // Update any pivot tables that has been modified.
133
-        $aggregate->updatePivotRecords();
134
-
135
-        // Now we can sync the related collections
136
-        if ($this->aggregate->exists()) {
137
-            $this->aggregate->syncRelationships($foreignRelationships);
138
-        }
139
-
140
-        // Update any dirty relationship. This include relationships that already exists, have
141
-        // dirty attributes / newly created related entities / dirty related entities.
142
-        $dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
143
-
144
-        foreach ($dirtyRelatedAggregates as $related) {
145
-            $this->createStoreCommand($related)->execute();
146
-        }
147
-
148
-
149
-        // TODO be move it to the wrapper class
150
-        // so it's the same code for the entity builder
151
-        $aggregate->setProxies();
152
-
153
-        // Update Entity Cache
154
-        $aggregate->getMapper()->getEntityCache()->refresh($aggregate);
155
-    }
156
-
157
-    /**
158
-     * Update Related Entities which attributes have
159
-     * been modified.
160
-     *
161
-     * @return void
162
-     */
163
-    protected function updateDirtyRelated()
164
-    {
165
-        $relations = $this->entityMap->getRelationships();
166
-        $attributes = $this->getAttributes();
167
-
168
-        foreach ($relations as $relation) {
169
-            if (!array_key_exists($relation, $attributes)) {
170
-                continue;
171
-            }
172
-
173
-            $value = $attributes[$relation];
174
-
175
-            if ($value == null) {
176
-                continue;
177
-            }
178
-
179
-            if ($value instanceof EntityProxy) {
180
-                continue;
181
-            }
182
-
183
-            if ($value instanceof CollectionProxy && $value->isLoaded()) {
184
-                $value = $value->getUnderlyingCollection();
185
-            }
186
-            if ($value instanceof CollectionProxy && !$value->isLoaded()) {
187
-                foreach ($value->getAddedItems() as $entity) {
188
-                    $this->updateEntityIfDirty($entity);
189
-                }
190
-                continue;
191
-            }
192
-
193
-            if ($value instanceof EntityCollection) {
194
-                foreach ($value as $entity) {
195
-                    if (!$this->createEntityIfNotExists($entity)) {
196
-                        $this->updateEntityIfDirty($entity);
197
-                    }
198
-                }
199
-                continue;
200
-            }
201
-            if ($value instanceof Mappable) {
202
-                $this->updateEntityIfDirty($value);
203
-                continue;
204
-            }
205
-        }
206
-    }
207
-
208
-    /**
209
-     * Execute an insert statement on the database
210
-     *
211
-     * @return void
212
-     */
213
-    protected function insert()
214
-    {
215
-        $aggregate = $this->aggregate;
216
-
217
-        $attributes = $aggregate->getRawAttributes();
49
+		/**
50
+		 * We'll only run an update if the entity
51
+		 * is actually dirty
52
+		 */
53
+		if ($this->aggregate->isDirty()) {
54
+			if ($mapper->fireEvent('updating', $entity) === false) {
55
+				return false;
56
+			}
57
+			$this->update();
58
+
59
+			$mapper->fireEvent('updated', $entity, false);
60
+		}
61
+
62
+		$this->postStoreProcess();
63
+
64
+		$mapper->fireEvent('stored', $entity, false);
65
+
66
+		return $entity;
67
+	}
68
+
69
+	/**
70
+	 * Run all operations that have to occur before actually
71
+	 * storing the entity
72
+	 *
73
+	 * @throws \InvalidArgumentException
74
+	 * @return void
75
+	 */
76
+	protected function preStoreProcess()
77
+	{
78
+		// Create any related object that doesn't exist in the database.
79
+		$localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
80
+
81
+		$this->createRelatedEntities($localRelationships);
82
+
83
+		// Now we can sync the related collections
84
+		$this->aggregate->syncRelationships($localRelationships);
85
+	}
86
+
87
+	/**
88
+	 * Check for existence and create non-existing related entities
89
+	 *
90
+	 * @param  array
91
+	 * @throws \InvalidArgumentException
92
+	 * @return void
93
+	 */
94
+	protected function createRelatedEntities($relations)
95
+	{
96
+		$entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
97
+
98
+		foreach ($entitiesToCreate as $aggregate) {
99
+			$this->createStoreCommand($aggregate)->execute();
100
+		}
101
+	}
102
+
103
+	/**
104
+	 * Create a new store command
105
+	 *
106
+	 * @param  Aggregate $aggregate
107
+	 * @return Store
108
+	 */
109
+	protected function createStoreCommand(Aggregate $aggregate)
110
+	{
111
+		// We gotta retrieve the corresponding query adapter to use.
112
+		$mapper = $aggregate->getMapper();
113
+
114
+		return new Store($aggregate, $mapper->newQueryBuilder());
115
+	}
116
+
117
+	/**
118
+	 * Run all operations that have to occur after the entity
119
+	 * is stored.
120
+	 *
121
+	 * @throws \InvalidArgumentException
122
+	 * @return void
123
+	 */
124
+	protected function postStoreProcess()
125
+	{
126
+		$aggregate = $this->aggregate;
127
+
128
+		// Create any related object that doesn't exist in the database.
129
+		$foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
130
+		$this->createRelatedEntities($foreignRelationships);
131
+
132
+		// Update any pivot tables that has been modified.
133
+		$aggregate->updatePivotRecords();
134
+
135
+		// Now we can sync the related collections
136
+		if ($this->aggregate->exists()) {
137
+			$this->aggregate->syncRelationships($foreignRelationships);
138
+		}
139
+
140
+		// Update any dirty relationship. This include relationships that already exists, have
141
+		// dirty attributes / newly created related entities / dirty related entities.
142
+		$dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
143
+
144
+		foreach ($dirtyRelatedAggregates as $related) {
145
+			$this->createStoreCommand($related)->execute();
146
+		}
147
+
148
+
149
+		// TODO be move it to the wrapper class
150
+		// so it's the same code for the entity builder
151
+		$aggregate->setProxies();
152
+
153
+		// Update Entity Cache
154
+		$aggregate->getMapper()->getEntityCache()->refresh($aggregate);
155
+	}
156
+
157
+	/**
158
+	 * Update Related Entities which attributes have
159
+	 * been modified.
160
+	 *
161
+	 * @return void
162
+	 */
163
+	protected function updateDirtyRelated()
164
+	{
165
+		$relations = $this->entityMap->getRelationships();
166
+		$attributes = $this->getAttributes();
167
+
168
+		foreach ($relations as $relation) {
169
+			if (!array_key_exists($relation, $attributes)) {
170
+				continue;
171
+			}
172
+
173
+			$value = $attributes[$relation];
174
+
175
+			if ($value == null) {
176
+				continue;
177
+			}
178
+
179
+			if ($value instanceof EntityProxy) {
180
+				continue;
181
+			}
182
+
183
+			if ($value instanceof CollectionProxy && $value->isLoaded()) {
184
+				$value = $value->getUnderlyingCollection();
185
+			}
186
+			if ($value instanceof CollectionProxy && !$value->isLoaded()) {
187
+				foreach ($value->getAddedItems() as $entity) {
188
+					$this->updateEntityIfDirty($entity);
189
+				}
190
+				continue;
191
+			}
192
+
193
+			if ($value instanceof EntityCollection) {
194
+				foreach ($value as $entity) {
195
+					if (!$this->createEntityIfNotExists($entity)) {
196
+						$this->updateEntityIfDirty($entity);
197
+					}
198
+				}
199
+				continue;
200
+			}
201
+			if ($value instanceof Mappable) {
202
+				$this->updateEntityIfDirty($value);
203
+				continue;
204
+			}
205
+		}
206
+	}
207
+
208
+	/**
209
+	 * Execute an insert statement on the database
210
+	 *
211
+	 * @return void
212
+	 */
213
+	protected function insert()
214
+	{
215
+		$aggregate = $this->aggregate;
216
+
217
+		$attributes = $aggregate->getRawAttributes();
218 218
         
219
-        $keyName = $aggregate->getEntityMap()->getKeyName();
219
+		$keyName = $aggregate->getEntityMap()->getKeyName();
220 220
 
221
-        // Check if the primary key is defined in the attributes
222
-        if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
223
-            $this->query->insert($attributes);
224
-        } else {
225
-            $sequence = $aggregate->getEntityMap()->getSequence();
221
+		// Check if the primary key is defined in the attributes
222
+		if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
223
+			$this->query->insert($attributes);
224
+		} else {
225
+			$sequence = $aggregate->getEntityMap()->getSequence();
226 226
 
227
-            $id = $this->query->insertGetId($attributes, $sequence);
227
+			$id = $this->query->insertGetId($attributes, $sequence);
228 228
 
229
-            $aggregate->setEntityAttribute($keyName, $id);
230
-        }
231
-    }
229
+			$aggregate->setEntityAttribute($keyName, $id);
230
+		}
231
+	}
232 232
 
233
-    /**
234
-     * Run an update statement on the entity
235
-     *
236
-     * @throws \InvalidArgumentException
237
-     *
238
-     * @return void
239
-     */
240
-    protected function update()
241
-    {
242
-        $query = $this->query;
233
+	/**
234
+	 * Run an update statement on the entity
235
+	 *
236
+	 * @throws \InvalidArgumentException
237
+	 *
238
+	 * @return void
239
+	 */
240
+	protected function update()
241
+	{
242
+		$query = $this->query;
243 243
 
244
-        $keyName = $this->aggregate->getEntityKey();
244
+		$keyName = $this->aggregate->getEntityKey();
245 245
 
246
-        $query = $query->where($keyName, '=', $this->aggregate->getEntityId());
246
+		$query = $query->where($keyName, '=', $this->aggregate->getEntityId());
247 247
 
248
-        $dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
248
+		$dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
249 249
 
250
-        if (count($dirtyAttributes) > 0) {
251
-            $query->update($dirtyAttributes);
252
-        }
253
-    }
250
+		if (count($dirtyAttributes) > 0) {
251
+			$query->update($dirtyAttributes);
252
+		}
253
+	}
254 254
 }
Please login to merge, or discard this patch.
src/EntityCollection.php 2 patches
Indentation   +379 added lines, -379 removed lines patch added patch discarded remove patch
@@ -10,387 +10,387 @@
 block discarded – undo
10 10
 
11 11
 class EntityCollection extends Collection
12 12
 {
13
-    /**
14
-     * Wrapper Factory
15
-     *
16
-     * @var \Analogue\ORM\System\Wrappers\Factory
17
-     */
18
-    protected $factory;
19
-
20
-    /**
21
-     * EntityCollection constructor.
22
-     * @param array|null $entities
23
-     */
24
-    public function __construct(array $entities = null)
25
-    {
26
-        $this->factory = new Factory;
27
-
28
-        parent::__construct($entities);
29
-    }
30
-
31
-    /**
32
-     * Find an entity in the collection by key.
33
-     *
34
-     * @param  mixed $key
35
-     * @param  mixed $default
36
-     * @throws MappingException
37
-     * @return \Analogue\ORM\Entity
38
-     */
39
-    public function find($key, $default = null)
40
-    {
41
-        if ($key instanceof Mappable) {
42
-            $key = $this->getEntityKey($key);
43
-        }
44
-
45
-        return array_first($this->items, function ($itemKey, $entity) use ($key) {
46
-            return $this->getEntityKey($entity) == $key;
47
-        }, $default);
48
-    }
49
-
50
-    /**
51
-     * Add an entity to the collection.
52
-     *
53
-     * @param  Mappable $entity
54
-     * @return $this
55
-     */
56
-    public function add($entity)
57
-    {
58
-        $this->push($entity);
59
-
60
-        return $this;
61
-    }
62
-
63
-    /**
64
-     * Remove an entity from the collection
65
-     *
66
-     * @param $entity
67
-     * @throws MappingException
68
-     * @return mixed
69
-     */
70
-    public function remove($entity)
71
-    {
72
-        $key = $this->getEntityKey($entity);
73
-
74
-        return $this->pull($key);
75
-    }
76
-
77
-    /**
78
-     * Push an item onto the end of the collection.
79
-     *
80
-     * @param  mixed $value
81
-     * @return void
82
-     */
83
-    public function push($value)
84
-    {
85
-        $this->offsetSet(null, $value);
86
-    }
87
-
88
-    /**
89
-     * Put an item in the collection by key.
90
-     *
91
-     * @param  mixed $key
92
-     * @param  mixed $value
93
-     * @return void
94
-     */
95
-    public function put($key, $value)
96
-    {
97
-        $this->offsetSet($key, $value);
98
-    }
99
-
100
-    /**
101
-     * Set the item at a given offset.
102
-     *
103
-     * @param  mixed $key
104
-     * @param  mixed $value
105
-     * @return void
106
-     */
107
-    public function offsetSet($key, $value)
108
-    {
109
-        if (is_null($key)) {
110
-            $this->items[] = $value;
111
-        } else {
112
-            $this->items[$key] = $value;
113
-        }
114
-    }
115
-
116
-    /**
117
-     * Determine if a key exists in the collection.
118
-     *
119
-     * @param  mixed      $key
120
-     * @param  mixed|null $value
121
-     * @return bool
122
-     */
123
-    public function contains($key, $value = null)
124
-    {
125
-        return !is_null($this->find($key));
126
-    }
127
-
128
-    /**
129
-     * Fetch a nested element of the collection.
130
-     *
131
-     * @param  string $key
132
-     * @return self
133
-     */
134
-    public function fetch($key)
135
-    {
136
-        return new static(array_fetch($this->toArray(), $key));
137
-    }
138
-
139
-    /**
140
-     * Generic function for returning class.key value pairs
141
-     *
142
-     * @throws MappingException
143
-     * @return string
144
-     */
145
-    public function getEntityHashes()
146
-    {
147
-        return array_map(function ($entity) {
148
-            $class = get_class($entity);
149
-
150
-            $mapper = Manager::getMapper($class);
13
+	/**
14
+	 * Wrapper Factory
15
+	 *
16
+	 * @var \Analogue\ORM\System\Wrappers\Factory
17
+	 */
18
+	protected $factory;
19
+
20
+	/**
21
+	 * EntityCollection constructor.
22
+	 * @param array|null $entities
23
+	 */
24
+	public function __construct(array $entities = null)
25
+	{
26
+		$this->factory = new Factory;
27
+
28
+		parent::__construct($entities);
29
+	}
30
+
31
+	/**
32
+	 * Find an entity in the collection by key.
33
+	 *
34
+	 * @param  mixed $key
35
+	 * @param  mixed $default
36
+	 * @throws MappingException
37
+	 * @return \Analogue\ORM\Entity
38
+	 */
39
+	public function find($key, $default = null)
40
+	{
41
+		if ($key instanceof Mappable) {
42
+			$key = $this->getEntityKey($key);
43
+		}
44
+
45
+		return array_first($this->items, function ($itemKey, $entity) use ($key) {
46
+			return $this->getEntityKey($entity) == $key;
47
+		}, $default);
48
+	}
49
+
50
+	/**
51
+	 * Add an entity to the collection.
52
+	 *
53
+	 * @param  Mappable $entity
54
+	 * @return $this
55
+	 */
56
+	public function add($entity)
57
+	{
58
+		$this->push($entity);
59
+
60
+		return $this;
61
+	}
62
+
63
+	/**
64
+	 * Remove an entity from the collection
65
+	 *
66
+	 * @param $entity
67
+	 * @throws MappingException
68
+	 * @return mixed
69
+	 */
70
+	public function remove($entity)
71
+	{
72
+		$key = $this->getEntityKey($entity);
73
+
74
+		return $this->pull($key);
75
+	}
76
+
77
+	/**
78
+	 * Push an item onto the end of the collection.
79
+	 *
80
+	 * @param  mixed $value
81
+	 * @return void
82
+	 */
83
+	public function push($value)
84
+	{
85
+		$this->offsetSet(null, $value);
86
+	}
87
+
88
+	/**
89
+	 * Put an item in the collection by key.
90
+	 *
91
+	 * @param  mixed $key
92
+	 * @param  mixed $value
93
+	 * @return void
94
+	 */
95
+	public function put($key, $value)
96
+	{
97
+		$this->offsetSet($key, $value);
98
+	}
99
+
100
+	/**
101
+	 * Set the item at a given offset.
102
+	 *
103
+	 * @param  mixed $key
104
+	 * @param  mixed $value
105
+	 * @return void
106
+	 */
107
+	public function offsetSet($key, $value)
108
+	{
109
+		if (is_null($key)) {
110
+			$this->items[] = $value;
111
+		} else {
112
+			$this->items[$key] = $value;
113
+		}
114
+	}
115
+
116
+	/**
117
+	 * Determine if a key exists in the collection.
118
+	 *
119
+	 * @param  mixed      $key
120
+	 * @param  mixed|null $value
121
+	 * @return bool
122
+	 */
123
+	public function contains($key, $value = null)
124
+	{
125
+		return !is_null($this->find($key));
126
+	}
127
+
128
+	/**
129
+	 * Fetch a nested element of the collection.
130
+	 *
131
+	 * @param  string $key
132
+	 * @return self
133
+	 */
134
+	public function fetch($key)
135
+	{
136
+		return new static(array_fetch($this->toArray(), $key));
137
+	}
138
+
139
+	/**
140
+	 * Generic function for returning class.key value pairs
141
+	 *
142
+	 * @throws MappingException
143
+	 * @return string
144
+	 */
145
+	public function getEntityHashes()
146
+	{
147
+		return array_map(function ($entity) {
148
+			$class = get_class($entity);
149
+
150
+			$mapper = Manager::getMapper($class);
151 151
             
152
-            $keyName = $mapper->getEntityMap()->getKeyName();
152
+			$keyName = $mapper->getEntityMap()->getKeyName();
153 153
             
154
-            return $class . '.' . $entity->getEntityAttribute($keyName);
155
-        },
156
-        $this->items);
157
-    }
158
-
159
-    /**
160
-     * Get a subset of the collection from entity hashes
161
-     *
162
-     * @param  array $hashes
163
-     * @throws MappingException
164
-     * @return array
165
-     */
166
-    public function getSubsetByHashes(array $hashes)
167
-    {
168
-        $subset = [];
169
-
170
-        foreach ($this->items as $item) {
171
-            $class = get_class($item);
172
-
173
-            $mapper = Manager::getMapper($class);
154
+			return $class . '.' . $entity->getEntityAttribute($keyName);
155
+		},
156
+		$this->items);
157
+	}
158
+
159
+	/**
160
+	 * Get a subset of the collection from entity hashes
161
+	 *
162
+	 * @param  array $hashes
163
+	 * @throws MappingException
164
+	 * @return array
165
+	 */
166
+	public function getSubsetByHashes(array $hashes)
167
+	{
168
+		$subset = [];
169
+
170
+		foreach ($this->items as $item) {
171
+			$class = get_class($item);
172
+
173
+			$mapper = Manager::getMapper($class);
174 174
             
175
-            $keyName = $mapper->getEntityMap()->getKeyName();
176
-
177
-            if (in_array($class . '.' . $item->$keyName, $hashes)) {
178
-                $subset[] = $item;
179
-            }
180
-        }
181
-
182
-        return $subset;
183
-    }
184
-
185
-    /**
186
-     * Merge the collection with the given items.
187
-     *
188
-     * @param  array $items
189
-     * @throws MappingException
190
-     * @return self
191
-     */
192
-    public function merge($items)
193
-    {
194
-        $dictionary = $this->getDictionary();
195
-
196
-        foreach ($items as $item) {
197
-            $dictionary[$this->getEntityKey($item)] = $item;
198
-        }
199
-
200
-        return new static(array_values($dictionary));
201
-    }
202
-
203
-    /**
204
-     * Diff the collection with the given items.
205
-     *
206
-     * @param  \ArrayAccess|array $items
207
-     * @return self
208
-     */
209
-    public function diff($items)
210
-    {
211
-        $diff = new static;
212
-
213
-        $dictionary = $this->getDictionary($items);
214
-
215
-        foreach ($this->items as $item) {
216
-            if (!isset($dictionary[$this->getEntityKey($item)])) {
217
-                $diff->add($item);
218
-            }
219
-        }
220
-
221
-        return $diff;
222
-    }
223
-
224
-    /**
225
-     * Intersect the collection with the given items.
226
-     *
227
-     * @param  \ArrayAccess|array $items
228
-     * @throws MappingException
229
-     * @return self
230
-     */
231
-    public function intersect($items)
232
-    {
233
-        $intersect = new static;
234
-
235
-        $dictionary = $this->getDictionary($items);
236
-
237
-        foreach ($this->items as $item) {
238
-            if (isset($dictionary[$this->getEntityKey($item)])) {
239
-                $intersect->add($item);
240
-            }
241
-        }
242
-
243
-        return $intersect;
244
-    }
245
-
246
-    /**
247
-     * Returns only the models from the collection with the specified keys.
248
-     *
249
-     * @param  mixed $keys
250
-     * @return self
251
-     */
252
-    public function only($keys)
253
-    {
254
-        $dictionary = array_only($this->getDictionary(), $keys);
255
-
256
-        return new static(array_values($dictionary));
257
-    }
258
-
259
-    /**
260
-     * Returns all models in the collection except the models with specified keys.
261
-     *
262
-     * @param  mixed $keys
263
-     * @return self
264
-     */
265
-    public function except($keys)
266
-    {
267
-        $dictionary = array_except($this->getDictionary(), $keys);
268
-
269
-        return new static(array_values($dictionary));
270
-    }
271
-
272
-    /**
273
-     * Get a dictionary keyed by primary keys.
274
-     *
275
-     * @param  \ArrayAccess|array $items
276
-     * @throws MappingException
277
-     * @return array
278
-     */
279
-    public function getDictionary($items = null)
280
-    {
281
-        $items = is_null($items) ? $this->items : $items;
282
-
283
-        $dictionary = [];
284
-
285
-        foreach ($items as $value) {
286
-            $dictionary[$this->getEntityKey($value)] = $value;
287
-        }
288
-
289
-        return $dictionary;
290
-    }
291
-
292
-    /**
293
-     * @throws MappingException
294
-     * @return array
295
-     */
296
-    public function getEntityKeys()
297
-    {
298
-        return array_keys($this->getDictionary());
299
-    }
300
-
301
-    /**
302
-     * @param $entity
303
-     * @throws MappingException
304
-     * @return mixed
305
-     */
306
-    protected function getEntityKey($entity)
307
-    {
308
-        $keyName = Manager::getMapper($entity)->getEntityMap()->getKeyName();
175
+			$keyName = $mapper->getEntityMap()->getKeyName();
176
+
177
+			if (in_array($class . '.' . $item->$keyName, $hashes)) {
178
+				$subset[] = $item;
179
+			}
180
+		}
181
+
182
+		return $subset;
183
+	}
184
+
185
+	/**
186
+	 * Merge the collection with the given items.
187
+	 *
188
+	 * @param  array $items
189
+	 * @throws MappingException
190
+	 * @return self
191
+	 */
192
+	public function merge($items)
193
+	{
194
+		$dictionary = $this->getDictionary();
195
+
196
+		foreach ($items as $item) {
197
+			$dictionary[$this->getEntityKey($item)] = $item;
198
+		}
199
+
200
+		return new static(array_values($dictionary));
201
+	}
202
+
203
+	/**
204
+	 * Diff the collection with the given items.
205
+	 *
206
+	 * @param  \ArrayAccess|array $items
207
+	 * @return self
208
+	 */
209
+	public function diff($items)
210
+	{
211
+		$diff = new static;
212
+
213
+		$dictionary = $this->getDictionary($items);
214
+
215
+		foreach ($this->items as $item) {
216
+			if (!isset($dictionary[$this->getEntityKey($item)])) {
217
+				$diff->add($item);
218
+			}
219
+		}
220
+
221
+		return $diff;
222
+	}
223
+
224
+	/**
225
+	 * Intersect the collection with the given items.
226
+	 *
227
+	 * @param  \ArrayAccess|array $items
228
+	 * @throws MappingException
229
+	 * @return self
230
+	 */
231
+	public function intersect($items)
232
+	{
233
+		$intersect = new static;
234
+
235
+		$dictionary = $this->getDictionary($items);
236
+
237
+		foreach ($this->items as $item) {
238
+			if (isset($dictionary[$this->getEntityKey($item)])) {
239
+				$intersect->add($item);
240
+			}
241
+		}
242
+
243
+		return $intersect;
244
+	}
245
+
246
+	/**
247
+	 * Returns only the models from the collection with the specified keys.
248
+	 *
249
+	 * @param  mixed $keys
250
+	 * @return self
251
+	 */
252
+	public function only($keys)
253
+	{
254
+		$dictionary = array_only($this->getDictionary(), $keys);
255
+
256
+		return new static(array_values($dictionary));
257
+	}
258
+
259
+	/**
260
+	 * Returns all models in the collection except the models with specified keys.
261
+	 *
262
+	 * @param  mixed $keys
263
+	 * @return self
264
+	 */
265
+	public function except($keys)
266
+	{
267
+		$dictionary = array_except($this->getDictionary(), $keys);
268
+
269
+		return new static(array_values($dictionary));
270
+	}
271
+
272
+	/**
273
+	 * Get a dictionary keyed by primary keys.
274
+	 *
275
+	 * @param  \ArrayAccess|array $items
276
+	 * @throws MappingException
277
+	 * @return array
278
+	 */
279
+	public function getDictionary($items = null)
280
+	{
281
+		$items = is_null($items) ? $this->items : $items;
282
+
283
+		$dictionary = [];
284
+
285
+		foreach ($items as $value) {
286
+			$dictionary[$this->getEntityKey($value)] = $value;
287
+		}
288
+
289
+		return $dictionary;
290
+	}
291
+
292
+	/**
293
+	 * @throws MappingException
294
+	 * @return array
295
+	 */
296
+	public function getEntityKeys()
297
+	{
298
+		return array_keys($this->getDictionary());
299
+	}
300
+
301
+	/**
302
+	 * @param $entity
303
+	 * @throws MappingException
304
+	 * @return mixed
305
+	 */
306
+	protected function getEntityKey($entity)
307
+	{
308
+		$keyName = Manager::getMapper($entity)->getEntityMap()->getKeyName();
309 309
         
310
-        $wrapper = $this->factory->make($entity);
311
-
312
-        return $wrapper->getEntityAttribute($keyName);
313
-    }
314
-
315
-    /**
316
-     * Get the max value of a given key.
317
-     *
318
-     * @param  string|null $key
319
-     * @throws MappingException
320
-     * @return mixed
321
-     */
322
-    public function max($key = null)
323
-    {
324
-        return $this->reduce(function ($result, $item) use ($key) {
325
-            $wrapper = $this->factory->make($item);
326
-
327
-            return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
328
-                $wrapper->getEntityAttribute($key) : $result;
329
-        });
330
-    }
331
-
332
-    /**
333
-     * Get the min value of a given key.
334
-     *
335
-     * @param  string|null $key
336
-     * @throws MappingException
337
-     * @return mixed
338
-     */
339
-    public function min($key = null)
340
-    {
341
-        return $this->reduce(function ($result, $item) use ($key) {
342
-            $wrapper = $this->factory->make($item);
343
-
344
-            return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
345
-                ? $wrapper->getEntityAttribute($key) : $result;
346
-        });
347
-    }
348
-
349
-    /**
350
-     * Get an array with the values of a given key.
351
-     *
352
-     * @param  string $value
353
-     * @param  string|null $key
354
-     * @return self
355
-     */
356
-    public function pluck($value, $key = null)
357
-    {
358
-        return new Collection(Arr::pluck($this->items, $value, $key));
359
-    }
360
-
361
-    /**
362
-     * Alias for the "pluck" method.
363
-     *
364
-     * @param  string $value
365
-     * @param  string|null $key
366
-     * @return self
367
-     */
368
-    public function lists($value, $key = null)
369
-    {
370
-        return $this->pluck($value, $key);
371
-    }
372
-
373
-    /**
374
-     * Return only unique items from the collection.
375
-     *
376
-     * @param  string|null $key
377
-     * @throws MappingException
378
-     * @return self
379
-     */
380
-    public function unique($key = null)
381
-    {
382
-        $dictionary = $this->getDictionary();
383
-
384
-        return new static(array_values($dictionary));
385
-    }
386
-
387
-    /**
388
-     * Get a base Support collection instance from this collection.
389
-     *
390
-     * @return \Illuminate\Support\Collection
391
-     */
392
-    public function toBase()
393
-    {
394
-        return new Collection($this->items);
395
-    }
310
+		$wrapper = $this->factory->make($entity);
311
+
312
+		return $wrapper->getEntityAttribute($keyName);
313
+	}
314
+
315
+	/**
316
+	 * Get the max value of a given key.
317
+	 *
318
+	 * @param  string|null $key
319
+	 * @throws MappingException
320
+	 * @return mixed
321
+	 */
322
+	public function max($key = null)
323
+	{
324
+		return $this->reduce(function ($result, $item) use ($key) {
325
+			$wrapper = $this->factory->make($item);
326
+
327
+			return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
328
+				$wrapper->getEntityAttribute($key) : $result;
329
+		});
330
+	}
331
+
332
+	/**
333
+	 * Get the min value of a given key.
334
+	 *
335
+	 * @param  string|null $key
336
+	 * @throws MappingException
337
+	 * @return mixed
338
+	 */
339
+	public function min($key = null)
340
+	{
341
+		return $this->reduce(function ($result, $item) use ($key) {
342
+			$wrapper = $this->factory->make($item);
343
+
344
+			return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
345
+				? $wrapper->getEntityAttribute($key) : $result;
346
+		});
347
+	}
348
+
349
+	/**
350
+	 * Get an array with the values of a given key.
351
+	 *
352
+	 * @param  string $value
353
+	 * @param  string|null $key
354
+	 * @return self
355
+	 */
356
+	public function pluck($value, $key = null)
357
+	{
358
+		return new Collection(Arr::pluck($this->items, $value, $key));
359
+	}
360
+
361
+	/**
362
+	 * Alias for the "pluck" method.
363
+	 *
364
+	 * @param  string $value
365
+	 * @param  string|null $key
366
+	 * @return self
367
+	 */
368
+	public function lists($value, $key = null)
369
+	{
370
+		return $this->pluck($value, $key);
371
+	}
372
+
373
+	/**
374
+	 * Return only unique items from the collection.
375
+	 *
376
+	 * @param  string|null $key
377
+	 * @throws MappingException
378
+	 * @return self
379
+	 */
380
+	public function unique($key = null)
381
+	{
382
+		$dictionary = $this->getDictionary();
383
+
384
+		return new static(array_values($dictionary));
385
+	}
386
+
387
+	/**
388
+	 * Get a base Support collection instance from this collection.
389
+	 *
390
+	 * @return \Illuminate\Support\Collection
391
+	 */
392
+	public function toBase()
393
+	{
394
+		return new Collection($this->items);
395
+	}
396 396
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
             $key = $this->getEntityKey($key);
43 43
         }
44 44
 
45
-        return array_first($this->items, function ($itemKey, $entity) use ($key) {
45
+        return array_first($this->items, function($itemKey, $entity) use ($key) {
46 46
             return $this->getEntityKey($entity) == $key;
47 47
         }, $default);
48 48
     }
@@ -144,14 +144,14 @@  discard block
 block discarded – undo
144 144
      */
145 145
     public function getEntityHashes()
146 146
     {
147
-        return array_map(function ($entity) {
147
+        return array_map(function($entity) {
148 148
             $class = get_class($entity);
149 149
 
150 150
             $mapper = Manager::getMapper($class);
151 151
             
152 152
             $keyName = $mapper->getEntityMap()->getKeyName();
153 153
             
154
-            return $class . '.' . $entity->getEntityAttribute($keyName);
154
+            return $class.'.'.$entity->getEntityAttribute($keyName);
155 155
         },
156 156
         $this->items);
157 157
     }
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
             
175 175
             $keyName = $mapper->getEntityMap()->getKeyName();
176 176
 
177
-            if (in_array($class . '.' . $item->$keyName, $hashes)) {
177
+            if (in_array($class.'.'.$item->$keyName, $hashes)) {
178 178
                 $subset[] = $item;
179 179
             }
180 180
         }
@@ -321,7 +321,7 @@  discard block
 block discarded – undo
321 321
      */
322 322
     public function max($key = null)
323 323
     {
324
-        return $this->reduce(function ($result, $item) use ($key) {
324
+        return $this->reduce(function($result, $item) use ($key) {
325 325
             $wrapper = $this->factory->make($item);
326 326
 
327 327
             return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
@@ -338,7 +338,7 @@  discard block
 block discarded – undo
338 338
      */
339 339
     public function min($key = null)
340 340
     {
341
-        return $this->reduce(function ($result, $item) use ($key) {
341
+        return $this->reduce(function($result, $item) use ($key) {
342 342
             $wrapper = $this->factory->make($item);
343 343
 
344 344
             return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
Please login to merge, or discard this patch.
src/Relationships/BelongsTo.php 1 patch
Indentation   +314 added lines, -314 removed lines patch added patch discarded remove patch
@@ -10,319 +10,319 @@
 block discarded – undo
10 10
 
11 11
 class BelongsTo extends Relationship
12 12
 {
13
-    /**
14
-     * The foreign key of the parent model.
15
-     *
16
-     * @var string
17
-     */
18
-    protected $foreignKey;
19
-
20
-    /**
21
-     * The associated key on the parent model.
22
-     *
23
-     * @var string
24
-     */
25
-    protected $otherKey;
26
-
27
-    /**
28
-     * The name of the relationship.
29
-     *
30
-     * @var string
31
-     */
32
-    protected $relation;
33
-
34
-    /**
35
-     * Indicate if the parent entity hold the key for the relation.
36
-     *
37
-     * @var boolean
38
-     */
39
-    protected static $ownForeignKey = true;
40
-
41
-    /**
42
-     * Create a new belongs to relationship instance.
43
-     *
44
-     * @param Mapper   $mapper
45
-     * @param Mappable $parent
46
-     * @param string   $foreignKey
47
-     * @param string   $otherKey
48
-     * @param string   $relation
49
-     */
50
-    public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $relation)
51
-    {
52
-        $this->otherKey = $otherKey;
53
-        $this->relation = $relation;
54
-        $this->foreignKey = $foreignKey;
55
-
56
-        parent::__construct($mapper, $parent);
57
-    }
58
-
59
-    /**
60
-     * @param  $related
61
-     * @return mixed
62
-     */
63
-    public function attachTo($related)
64
-    {
65
-        $this->associate($related);
66
-    }
67
-
68
-    /**
69
-     * @param $related
70
-     * @return Mappable
71
-     */
72
-    public function detachFrom($related)
73
-    {
74
-        return $this->dissociate($related); //todo
75
-    }
76
-
77
-    /**
78
-     * Get the results of the relationship.
79
-     *
80
-     * @param  $relation
81
-     *
82
-     * @return \Analogue\ORM\Entity
83
-     */
84
-    public function getResults($relation)
85
-    {
86
-        $result = $this->query->first();
87
-
88
-        $this->cacheRelation($result, $relation);
89
-
90
-        return $result;
91
-    }
92
-
93
-    /**
94
-     * Set the base constraints on the relation query.
95
-     *
96
-     * @return void
97
-     */
98
-    public function addConstraints()
99
-    {
100
-        if (static::$constraints) {
101
-            // For belongs to relationships, which are essentially the inverse of has one
102
-            // or has many relationships, we need to actually query on the primary key
103
-            // of the related models matching on the foreign key that's on a parent.
104
-            $table = $this->relatedMap->getTable();
105
-
106
-            $this->query->where($table . '.' . $this->otherKey, '=', $this->parent->getEntityAttribute($this->foreignKey));
107
-        }
108
-    }
109
-
110
-    /**
111
-     * Add the constraints for a relationship count query.
112
-     *
113
-     * @param  Query $query
114
-     * @param  Query $parent
115
-     * @return Query
116
-     */
117
-    public function getRelationCountQuery(Query $query, Query $parent)
118
-    {
119
-        $query->select(new Expression('count(*)'));
120
-
121
-        $otherKey = $this->wrap($query->getTable() . '.' . $this->otherKey);
122
-
123
-        return $query->where($this->getQualifiedForeignKey(), '=', new Expression($otherKey));
124
-    }
125
-
126
-    /**
127
-     * Set the constraints for an eager load of the relation.
128
-     *
129
-     * @param  array $entities
130
-     * @return void
131
-     */
132
-    public function addEagerConstraints(array $entities)
133
-    {
134
-        // We'll grab the primary key name of the related models since it could be set to
135
-        // a non-standard name and not "id". We will then construct the constraint for
136
-        // our eagerly loading query so it returns the proper models from execution.
137
-        $key = $this->relatedMap->getTable() . '.' . $this->otherKey;
138
-
139
-        $this->query->whereIn($key, $this->getEagerModelKeys($entities));
140
-    }
141
-
142
-    /**
143
-     * Gather the keys from an array of related models.
144
-     *
145
-     * @param  array $entities
146
-     * @return array
147
-     */
148
-    protected function getEagerModelKeys(array $entities)
149
-    {
150
-        $keys = [];
151
-
152
-        // First we need to gather all of the keys from the parent models so we know what
153
-        // to query for via the eager loading query. We will add them to an array then
154
-        // execute a "where in" statement to gather up all of those related records.
155
-        foreach ($entities as $entity) {
156
-            $entity = $this->factory->make($entity);
157
-
158
-            if (!is_null($value = $entity->getEntityAttribute($this->foreignKey))) {
159
-                $keys[] = $value;
160
-            }
161
-        }
162
-
163
-        // If there are no keys that were not null we will just return an array with 0 in
164
-        // it so the query doesn't fail, but will not return any results, which should
165
-        // be what this developer is expecting in a case where this happens to them.
166
-        if (count($keys) == 0) {
167
-            return [0];
168
-        }
169
-
170
-        return array_values(array_unique($keys));
171
-    }
172
-
173
-    /**
174
-     * Initialize the relation on a set of models.
175
-     *
176
-     * @param  array  $entities
177
-     * @param  string $relation
178
-     * @return array
179
-     */
180
-    public function initRelation(array $entities, $relation)
181
-    {
182
-        foreach ($entities as $entity) {
183
-            $entity = $this->factory->make($entity);
184
-            $entity->setEntityAttribute($relation, null);
185
-        }
186
-
187
-        return $entities;
188
-    }
189
-
190
-    /**
191
-     * Match the eagerly loaded results to their parents.
192
-     *
193
-     * @param  array            $entities
194
-     * @param  EntityCollection $results
195
-     * @param  string           $relation
196
-     * @return array
197
-     */
198
-    public function match(array $entities, EntityCollection $results, $relation)
199
-    {
200
-        $foreign = $this->foreignKey;
201
-
202
-        $other = $this->otherKey;
203
-
204
-        // First we will get to build a dictionary of the child models by their primary
205
-        // key of the relationship, then we can easily match the children back onto
206
-        // the parents using that dictionary and the primary key of the children.
207
-        $dictionary = [];
208
-
209
-        foreach ($results as $result) {
210
-            $result = $this->factory->make($result);
211
-            $dictionary[$result->getEntityAttribute($other)] = $result->getObject();
212
-        }
213
-
214
-        // Once we have the dictionary constructed, we can loop through all the parents
215
-        // and match back onto their children using these keys of the dictionary and
216
-        // the primary key of the children to map them onto the correct instances.
217
-        foreach ($entities as $entity) {
218
-            $entity = $this->factory->make($entity);
219
-
220
-            if (isset($dictionary[$entity->getEntityAttribute($foreign)])) {
221
-                $entity->setEntityAttribute($relation, $dictionary[$entity->getEntityAttribute($foreign)]);
222
-            }
223
-        }
224
-
225
-        return $entities;
226
-    }
227
-
228
-    public function sync(array $entities)
229
-    {
230
-        if (count($entities) > 1) {
231
-            throw new MappingException("Single Relationship shouldn't be synced with more than one entity");
232
-        }
13
+	/**
14
+	 * The foreign key of the parent model.
15
+	 *
16
+	 * @var string
17
+	 */
18
+	protected $foreignKey;
19
+
20
+	/**
21
+	 * The associated key on the parent model.
22
+	 *
23
+	 * @var string
24
+	 */
25
+	protected $otherKey;
26
+
27
+	/**
28
+	 * The name of the relationship.
29
+	 *
30
+	 * @var string
31
+	 */
32
+	protected $relation;
33
+
34
+	/**
35
+	 * Indicate if the parent entity hold the key for the relation.
36
+	 *
37
+	 * @var boolean
38
+	 */
39
+	protected static $ownForeignKey = true;
40
+
41
+	/**
42
+	 * Create a new belongs to relationship instance.
43
+	 *
44
+	 * @param Mapper   $mapper
45
+	 * @param Mappable $parent
46
+	 * @param string   $foreignKey
47
+	 * @param string   $otherKey
48
+	 * @param string   $relation
49
+	 */
50
+	public function __construct(Mapper $mapper, $parent, $foreignKey, $otherKey, $relation)
51
+	{
52
+		$this->otherKey = $otherKey;
53
+		$this->relation = $relation;
54
+		$this->foreignKey = $foreignKey;
55
+
56
+		parent::__construct($mapper, $parent);
57
+	}
58
+
59
+	/**
60
+	 * @param  $related
61
+	 * @return mixed
62
+	 */
63
+	public function attachTo($related)
64
+	{
65
+		$this->associate($related);
66
+	}
67
+
68
+	/**
69
+	 * @param $related
70
+	 * @return Mappable
71
+	 */
72
+	public function detachFrom($related)
73
+	{
74
+		return $this->dissociate($related); //todo
75
+	}
76
+
77
+	/**
78
+	 * Get the results of the relationship.
79
+	 *
80
+	 * @param  $relation
81
+	 *
82
+	 * @return \Analogue\ORM\Entity
83
+	 */
84
+	public function getResults($relation)
85
+	{
86
+		$result = $this->query->first();
87
+
88
+		$this->cacheRelation($result, $relation);
89
+
90
+		return $result;
91
+	}
92
+
93
+	/**
94
+	 * Set the base constraints on the relation query.
95
+	 *
96
+	 * @return void
97
+	 */
98
+	public function addConstraints()
99
+	{
100
+		if (static::$constraints) {
101
+			// For belongs to relationships, which are essentially the inverse of has one
102
+			// or has many relationships, we need to actually query on the primary key
103
+			// of the related models matching on the foreign key that's on a parent.
104
+			$table = $this->relatedMap->getTable();
105
+
106
+			$this->query->where($table . '.' . $this->otherKey, '=', $this->parent->getEntityAttribute($this->foreignKey));
107
+		}
108
+	}
109
+
110
+	/**
111
+	 * Add the constraints for a relationship count query.
112
+	 *
113
+	 * @param  Query $query
114
+	 * @param  Query $parent
115
+	 * @return Query
116
+	 */
117
+	public function getRelationCountQuery(Query $query, Query $parent)
118
+	{
119
+		$query->select(new Expression('count(*)'));
120
+
121
+		$otherKey = $this->wrap($query->getTable() . '.' . $this->otherKey);
122
+
123
+		return $query->where($this->getQualifiedForeignKey(), '=', new Expression($otherKey));
124
+	}
125
+
126
+	/**
127
+	 * Set the constraints for an eager load of the relation.
128
+	 *
129
+	 * @param  array $entities
130
+	 * @return void
131
+	 */
132
+	public function addEagerConstraints(array $entities)
133
+	{
134
+		// We'll grab the primary key name of the related models since it could be set to
135
+		// a non-standard name and not "id". We will then construct the constraint for
136
+		// our eagerly loading query so it returns the proper models from execution.
137
+		$key = $this->relatedMap->getTable() . '.' . $this->otherKey;
138
+
139
+		$this->query->whereIn($key, $this->getEagerModelKeys($entities));
140
+	}
141
+
142
+	/**
143
+	 * Gather the keys from an array of related models.
144
+	 *
145
+	 * @param  array $entities
146
+	 * @return array
147
+	 */
148
+	protected function getEagerModelKeys(array $entities)
149
+	{
150
+		$keys = [];
151
+
152
+		// First we need to gather all of the keys from the parent models so we know what
153
+		// to query for via the eager loading query. We will add them to an array then
154
+		// execute a "where in" statement to gather up all of those related records.
155
+		foreach ($entities as $entity) {
156
+			$entity = $this->factory->make($entity);
157
+
158
+			if (!is_null($value = $entity->getEntityAttribute($this->foreignKey))) {
159
+				$keys[] = $value;
160
+			}
161
+		}
162
+
163
+		// If there are no keys that were not null we will just return an array with 0 in
164
+		// it so the query doesn't fail, but will not return any results, which should
165
+		// be what this developer is expecting in a case where this happens to them.
166
+		if (count($keys) == 0) {
167
+			return [0];
168
+		}
169
+
170
+		return array_values(array_unique($keys));
171
+	}
172
+
173
+	/**
174
+	 * Initialize the relation on a set of models.
175
+	 *
176
+	 * @param  array  $entities
177
+	 * @param  string $relation
178
+	 * @return array
179
+	 */
180
+	public function initRelation(array $entities, $relation)
181
+	{
182
+		foreach ($entities as $entity) {
183
+			$entity = $this->factory->make($entity);
184
+			$entity->setEntityAttribute($relation, null);
185
+		}
186
+
187
+		return $entities;
188
+	}
189
+
190
+	/**
191
+	 * Match the eagerly loaded results to their parents.
192
+	 *
193
+	 * @param  array            $entities
194
+	 * @param  EntityCollection $results
195
+	 * @param  string           $relation
196
+	 * @return array
197
+	 */
198
+	public function match(array $entities, EntityCollection $results, $relation)
199
+	{
200
+		$foreign = $this->foreignKey;
201
+
202
+		$other = $this->otherKey;
203
+
204
+		// First we will get to build a dictionary of the child models by their primary
205
+		// key of the relationship, then we can easily match the children back onto
206
+		// the parents using that dictionary and the primary key of the children.
207
+		$dictionary = [];
208
+
209
+		foreach ($results as $result) {
210
+			$result = $this->factory->make($result);
211
+			$dictionary[$result->getEntityAttribute($other)] = $result->getObject();
212
+		}
213
+
214
+		// Once we have the dictionary constructed, we can loop through all the parents
215
+		// and match back onto their children using these keys of the dictionary and
216
+		// the primary key of the children to map them onto the correct instances.
217
+		foreach ($entities as $entity) {
218
+			$entity = $this->factory->make($entity);
219
+
220
+			if (isset($dictionary[$entity->getEntityAttribute($foreign)])) {
221
+				$entity->setEntityAttribute($relation, $dictionary[$entity->getEntityAttribute($foreign)]);
222
+			}
223
+		}
224
+
225
+		return $entities;
226
+	}
227
+
228
+	public function sync(array $entities)
229
+	{
230
+		if (count($entities) > 1) {
231
+			throw new MappingException("Single Relationship shouldn't be synced with more than one entity");
232
+		}
233 233
         
234
-        if (count($entities) == 1) {
235
-            return $this->associate($entities[0]);
236
-        }
237
-
238
-        return false;
239
-    }
240
-
241
-    /**
242
-     * Associate the model instance to the given parent.
243
-     *
244
-     * @param  mixed $entity
245
-     * @return void
246
-     */
247
-    public function associate($entity)
248
-    {
249
-        $this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
250
-    }
251
-
252
-    /**
253
-     * Dissociate previously associated model from the given parent.
254
-     *
255
-     * @return Mappable
256
-     */
257
-    public function dissociate()
258
-    {
259
-        // The Mapper will retrieve this association within the object model, we won't be using
260
-        // the foreign key attribute inside the parent Entity.
261
-        //
262
-        //$this->parent->setEntityAttribute($this->foreignKey, null);
263
-
264
-        $this->parent->setEntityAttribute($this->relation, null);
265
-    }
266
-
267
-    /**
268
-     * Get the foreign key of the relationship.
269
-     *
270
-     * @return string
271
-     */
272
-    public function getForeignKey()
273
-    {
274
-        return $this->foreignKey;
275
-    }
276
-
277
-    /**
278
-     * Get the foreign key value pair for a related object
279
-     *
280
-     * @param  mixed $related
281
-     *
282
-     * @return array
283
-     */
284
-    public function getForeignKeyValuePair($related)
285
-    {
286
-        $foreignKey = $this->getForeignKey();
287
-
288
-        if ($related) {
289
-            $wrapper = $this->factory->make($related);
290
-
291
-            $relatedKey = $this->relatedMap->getKeyName();
292
-
293
-            return [$foreignKey => $wrapper->getEntityAttribute($relatedKey)];
294
-        } else {
295
-            return [$foreignKey => null];
296
-        }
297
-    }
298
-
299
-    /**
300
-     * Get the fully qualified foreign key of the relationship.
301
-     *
302
-     * @return string
303
-     */
304
-    public function getQualifiedForeignKey()
305
-    {
306
-        return $this->parentMap->getTable() . '.' . $this->foreignKey;
307
-    }
308
-
309
-    /**
310
-     * Get the associated key of the relationship.
311
-     *
312
-     * @return string
313
-     */
314
-    public function getOtherKey()
315
-    {
316
-        return $this->otherKey;
317
-    }
318
-
319
-    /**
320
-     * Get the fully qualified associated key of the relationship.
321
-     *
322
-     * @return string
323
-     */
324
-    public function getQualifiedOtherKeyName()
325
-    {
326
-        return $this->relatedMap->getTable() . '.' . $this->otherKey;
327
-    }
234
+		if (count($entities) == 1) {
235
+			return $this->associate($entities[0]);
236
+		}
237
+
238
+		return false;
239
+	}
240
+
241
+	/**
242
+	 * Associate the model instance to the given parent.
243
+	 *
244
+	 * @param  mixed $entity
245
+	 * @return void
246
+	 */
247
+	public function associate($entity)
248
+	{
249
+		$this->parent->setEntityAttribute($this->foreignKey, $entity->getEntityAttribute($this->otherKey));
250
+	}
251
+
252
+	/**
253
+	 * Dissociate previously associated model from the given parent.
254
+	 *
255
+	 * @return Mappable
256
+	 */
257
+	public function dissociate()
258
+	{
259
+		// The Mapper will retrieve this association within the object model, we won't be using
260
+		// the foreign key attribute inside the parent Entity.
261
+		//
262
+		//$this->parent->setEntityAttribute($this->foreignKey, null);
263
+
264
+		$this->parent->setEntityAttribute($this->relation, null);
265
+	}
266
+
267
+	/**
268
+	 * Get the foreign key of the relationship.
269
+	 *
270
+	 * @return string
271
+	 */
272
+	public function getForeignKey()
273
+	{
274
+		return $this->foreignKey;
275
+	}
276
+
277
+	/**
278
+	 * Get the foreign key value pair for a related object
279
+	 *
280
+	 * @param  mixed $related
281
+	 *
282
+	 * @return array
283
+	 */
284
+	public function getForeignKeyValuePair($related)
285
+	{
286
+		$foreignKey = $this->getForeignKey();
287
+
288
+		if ($related) {
289
+			$wrapper = $this->factory->make($related);
290
+
291
+			$relatedKey = $this->relatedMap->getKeyName();
292
+
293
+			return [$foreignKey => $wrapper->getEntityAttribute($relatedKey)];
294
+		} else {
295
+			return [$foreignKey => null];
296
+		}
297
+	}
298
+
299
+	/**
300
+	 * Get the fully qualified foreign key of the relationship.
301
+	 *
302
+	 * @return string
303
+	 */
304
+	public function getQualifiedForeignKey()
305
+	{
306
+		return $this->parentMap->getTable() . '.' . $this->foreignKey;
307
+	}
308
+
309
+	/**
310
+	 * Get the associated key of the relationship.
311
+	 *
312
+	 * @return string
313
+	 */
314
+	public function getOtherKey()
315
+	{
316
+		return $this->otherKey;
317
+	}
318
+
319
+	/**
320
+	 * Get the fully qualified associated key of the relationship.
321
+	 *
322
+	 * @return string
323
+	 */
324
+	public function getQualifiedOtherKeyName()
325
+	{
326
+		return $this->relatedMap->getTable() . '.' . $this->otherKey;
327
+	}
328 328
 }
Please login to merge, or discard this patch.
src/Relationships/Relationship.php 1 patch
Indentation   +425 added lines, -425 removed lines patch added patch discarded remove patch
@@ -19,430 +19,430 @@
 block discarded – undo
19 19
  */
20 20
 abstract class Relationship
21 21
 {
22
-    /**
23
-     * The mapper instance for the related entity
24
-     *
25
-     * @var Mapper
26
-     */
27
-    protected $relatedMapper;
28
-
29
-    /**
30
-     * The Analogue Query Builder instance.
31
-     *
32
-     * @var Query
33
-     */
34
-    protected $query;
35
-
36
-    /**
37
-     * The parent entity proxy instance.
38
-     *
39
-     * @var InternallyMappable
40
-     */
41
-    protected $parent;
42
-
43
-    /**
44
-     * The parent entity map
45
-     * @var \Analogue\ORM\EntityMap
46
-     */
47
-    protected $parentMap;
48
-
49
-    /**
50
-     * The Parent Mapper instance
51
-     *
52
-     * @var Mapper
53
-     */
54
-    protected $parentMapper;
55
-
56
-    /**
57
-     * The related entity instance.
58
-     *
59
-     * @var object
60
-     */
61
-    protected $related;
62
-
63
-    /**
64
-     * The related entity Map
65
-     * @var \Analogue\ORM\EntityMap
66
-     */
67
-    protected $relatedMap;
68
-
69
-    /**
70
-     * Indicate if the parent entity hold the key for the relation.
71
-     *
72
-     * @var boolean
73
-     */
74
-    protected static $ownForeignKey = false;
75
-
76
-    /**
77
-     * Indicate if the relationships use a pivot table.*
78
-     *
79
-     * @var boolean
80
-     */
81
-    protected static $hasPivot = false;
82
-
83
-    /**
84
-     * Indicates if the relation is adding constraints.
85
-     *
86
-     * @var bool
87
-     */
88
-    protected static $constraints = true;
89
-
90
-    /**
91
-     * Wrapper factory
92
-     *
93
-     * @var \Analogue\ORM\System\Wrappers\Factory
94
-     */
95
-    protected $factory;
96
-
97
-    /**
98
-     * Create a new relation instance.
99
-     *
100
-     * @param  Mapper   $mapper
101
-     * @param  Mappable $parent
102
-     * @throws \Analogue\ORM\Exceptions\MappingException
103
-     */
104
-    public function __construct(Mapper $mapper, $parent)
105
-    {
106
-        $this->relatedMapper = $mapper;
107
-
108
-        $this->query = $mapper->getQuery();
109
-
110
-        $this->factory = new Factory;
111
-
112
-        $this->parent = $this->factory->make($parent);
113
-
114
-        $this->parentMapper = $mapper->getManager()->getMapper($parent);
115
-
116
-        $this->parentMap = $this->parentMapper->getEntityMap();
117
-
118
-        $this->related = $this->query->getEntityInstance();
119
-
120
-        $this->relatedMap = $mapper->getEntityMap();
121
-
122
-        $this->addConstraints();
123
-    }
124
-
125
-    /**
126
-     * @param $related
127
-     * @return mixed
128
-     */
129
-    abstract public function attachTo($related);
130
-
131
-    /**
132
-     * @param $related
133
-     * @return mixed
134
-     */
135
-    abstract public function detachFrom($related);
136
-
137
-    /**
138
-     * Indicate if the parent entity hold the foreign key for relation.
139
-     *
140
-     * @return boolean
141
-     */
142
-    public function ownForeignKey()
143
-    {
144
-        return static::$ownForeignKey;
145
-    }
146
-
147
-    /**
148
-     * Indicate if the relationship uses a pivot table
149
-     *
150
-     * @return boolean
151
-     */
152
-    public function hasPivot()
153
-    {
154
-        return static::$hasPivot;
155
-    }
156
-
157
-    /**
158
-     * Set the base constraints on the relation query.
159
-     *
160
-     * @return void
161
-     */
162
-    abstract public function addConstraints();
163
-
164
-    /**
165
-     * Set the constraints for an eager load of the relation.
166
-     *
167
-     * @param  array $models
168
-     * @return void
169
-     */
170
-    abstract public function addEagerConstraints(array $models);
171
-
172
-    /**
173
-     * Initialize the relation on a set of models.
174
-     *
175
-     * @param  array  $models
176
-     * @param  string $relation
177
-     * @return array
178
-     */
179
-    abstract public function initRelation(array $models, $relation);
180
-
181
-    /**
182
-     * Match the eagerly loaded results to their parents.
183
-     *
184
-     * @param  array            $entities
185
-     * @param  EntityCollection $results
186
-     * @param  string           $relation
187
-     * @return array
188
-     */
189
-    abstract public function match(array $entities, EntityCollection $results, $relation);
190
-
191
-    /**
192
-     * Get the results of the relationship.
193
-     *
194
-     * @param string $relation relation name in parent's entity map
195
-     * @return mixed
196
-     */
197
-    abstract public function getResults($relation);
198
-
199
-    /**
200
-     * Get the relationship for eager loading.
201
-     *
202
-     * @return EntityCollection
203
-     */
204
-    public function getEager()
205
-    {
206
-        return $this->get();
207
-    }
208
-
209
-    /**
210
-     * Add the constraints for a relationship count query.
211
-     *
212
-     * @param  Query $query
213
-     * @param  Query $parent
214
-     * @return Query
215
-     */
216
-    public function getRelationCountQuery(Query $query, Query $parent)
217
-    {
218
-        $query->select(new Expression('count(*)'));
219
-
220
-        $key = $this->wrap($this->getQualifiedParentKeyName());
221
-
222
-        return $query->where($this->getHasCompareKey(), '=', new Expression($key));
223
-    }
224
-
225
-    /**
226
-     * Run a callback with constraints disabled on the relation.
227
-     *
228
-     * @param  Closure $callback
229
-     * @return mixed
230
-     */
231
-    public static function noConstraints(Closure $callback)
232
-    {
233
-        static::$constraints = false;
234
-
235
-        // When resetting the relation where clause, we want to shift the first element
236
-        // off of the bindings, leaving only the constraints that the developers put
237
-        // as "extra" on the relationships, and not original relation constraints.
238
-        $results = call_user_func($callback);
239
-
240
-        static::$constraints = true;
241
-
242
-        return $results;
243
-    }
244
-
245
-    /**
246
-     * Get all of the primary keys for an array of entities.
247
-     *
248
-     * @param  array  $entities
249
-     * @param  string $key
250
-     * @return array
251
-     */
252
-    protected function getKeys(array $entities, $key = null)
253
-    {
254
-        if (is_null($key)) {
255
-            $key = $this->relatedMap->getKeyName();
256
-        }
257
-
258
-        $host = $this;
259
-
260
-        return array_unique(array_values(array_map(function ($value) use ($key, $host) {
261
-            if (!$value instanceof InternallyMappable) {
262
-                $value = $host->factory->make($value);
263
-            }
264
-
265
-            return $value->getEntityAttribute($key);
266
-
267
-        }, $entities)));
268
-    }
269
-
270
-    /**
271
-     * Get the underlying query for the relation.
272
-     *
273
-     * @return Query
274
-     */
275
-    public function getQuery()
276
-    {
277
-        return $this->query;
278
-    }
279
-
280
-    /**
281
-     * Get the base query builder
282
-     *
283
-     * @return \Analogue\ORM\Drivers\QueryAdapter
284
-     */
285
-    public function getBaseQuery()
286
-    {
287
-        return $this->query->getQuery();
288
-    }
289
-
290
-    /**
291
-     * Get the parent model of the relation.
292
-     *
293
-     * @return InternallyMappable
294
-     */
295
-    public function getParent()
296
-    {
297
-        return $this->parent;
298
-    }
299
-
300
-    /**
301
-     * Get the fully qualified parent key name.
302
-     *
303
-     * @return string
304
-     */
305
-    protected function getQualifiedParentKeyName()
306
-    {
307
-        return $this->parent->getQualifiedKeyName();
308
-    }
309
-
310
-    /**
311
-     * Get the related entity of the relation.
312
-     *
313
-     * @return \Analogue\ORM\Entity
314
-     */
315
-    public function getRelated()
316
-    {
317
-        return $this->related;
318
-    }
319
-
320
-    /**
321
-     * Get the related mapper for the relation
322
-     *
323
-     * @return Mapper
324
-     */
325
-    public function getRelatedMapper()
326
-    {
327
-        return $this->relatedMapper;
328
-    }
329
-
330
-
331
-    /**
332
-     * Get the name of the "created at" column.
333
-     *
334
-     * @return string
335
-     */
336
-    public function createdAt()
337
-    {
338
-        return $this->parentMap->getCreatedAtColumn();
339
-    }
340
-
341
-    /**
342
-     * Get the name of the "updated at" column.
343
-     *
344
-     * @return string
345
-     */
346
-    public function updatedAt()
347
-    {
348
-        return $this->parentMap->getUpdatedAtColumn();
349
-    }
350
-
351
-    /**
352
-     * Get the name of the related model's "updated at" column.
353
-     *
354
-     * @return string
355
-     */
356
-    public function relatedUpdatedAt()
357
-    {
358
-        return $this->related->getUpdatedAtColumn();
359
-    }
360
-
361
-    /**
362
-     * Wrap the given value with the parent query's grammar.
363
-     *
364
-     * @param  string $value
365
-     * @return string
366
-     */
367
-    public function wrap($value)
368
-    {
369
-        return $this->parentMapper->getQuery()->getQuery()->getGrammar()->wrap($value);
370
-    }
371
-
372
-    /**
373
-     * Get a fresh timestamp
374
-     *
375
-     * @return Carbon
376
-     */
377
-    protected function freshTimestamp()
378
-    {
379
-        return new Carbon;
380
-    }
381
-
382
-    /**
383
-     * Cache the link between parent and related
384
-     * into the mapper's Entity Cache.
385
-     *
386
-     * @param  EntityCollection|Mappable $results  result of the relation query
387
-     * @param  string                    $relation name of the relation method on the parent entity
388
-     * @return void
389
-     */
390
-    protected function cacheRelation($results, $relation)
391
-    {
392
-        $cache = $this->parentMapper->getEntityCache();
393
-
394
-        $cache->cacheLoadedRelationResult($this->parent, $relation, $results, $this);
395
-    }
396
-
397
-    /**
398
-     * Return Pivot attributes when available on a relationship
399
-     *
400
-     * @return array
401
-     */
402
-    public function getPivotAttributes()
403
-    {
404
-        return [];
405
-    }
406
-
407
-    /**
408
-     * Get a combo type.primaryKey
409
-     *
410
-     * @param  Mappable $entity
411
-     * @return string
412
-     */
413
-    protected function getEntityHash(Mappable $entity)
414
-    {
415
-        $class = get_class($entity);
416
-
417
-        $keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
418
-
419
-        return $class . '.' . $entity->getEntityAttribute($keyName);
420
-    }
421
-
422
-    /**
423
-     * Run synchronization content if needed by the
424
-     * relation type.
425
-     *
426
-     * @param  array $actualContent
427
-     * @return void
428
-     */
429
-    abstract public function sync(array $actualContent);
22
+	/**
23
+	 * The mapper instance for the related entity
24
+	 *
25
+	 * @var Mapper
26
+	 */
27
+	protected $relatedMapper;
28
+
29
+	/**
30
+	 * The Analogue Query Builder instance.
31
+	 *
32
+	 * @var Query
33
+	 */
34
+	protected $query;
35
+
36
+	/**
37
+	 * The parent entity proxy instance.
38
+	 *
39
+	 * @var InternallyMappable
40
+	 */
41
+	protected $parent;
42
+
43
+	/**
44
+	 * The parent entity map
45
+	 * @var \Analogue\ORM\EntityMap
46
+	 */
47
+	protected $parentMap;
48
+
49
+	/**
50
+	 * The Parent Mapper instance
51
+	 *
52
+	 * @var Mapper
53
+	 */
54
+	protected $parentMapper;
55
+
56
+	/**
57
+	 * The related entity instance.
58
+	 *
59
+	 * @var object
60
+	 */
61
+	protected $related;
62
+
63
+	/**
64
+	 * The related entity Map
65
+	 * @var \Analogue\ORM\EntityMap
66
+	 */
67
+	protected $relatedMap;
68
+
69
+	/**
70
+	 * Indicate if the parent entity hold the key for the relation.
71
+	 *
72
+	 * @var boolean
73
+	 */
74
+	protected static $ownForeignKey = false;
75
+
76
+	/**
77
+	 * Indicate if the relationships use a pivot table.*
78
+	 *
79
+	 * @var boolean
80
+	 */
81
+	protected static $hasPivot = false;
82
+
83
+	/**
84
+	 * Indicates if the relation is adding constraints.
85
+	 *
86
+	 * @var bool
87
+	 */
88
+	protected static $constraints = true;
89
+
90
+	/**
91
+	 * Wrapper factory
92
+	 *
93
+	 * @var \Analogue\ORM\System\Wrappers\Factory
94
+	 */
95
+	protected $factory;
96
+
97
+	/**
98
+	 * Create a new relation instance.
99
+	 *
100
+	 * @param  Mapper   $mapper
101
+	 * @param  Mappable $parent
102
+	 * @throws \Analogue\ORM\Exceptions\MappingException
103
+	 */
104
+	public function __construct(Mapper $mapper, $parent)
105
+	{
106
+		$this->relatedMapper = $mapper;
107
+
108
+		$this->query = $mapper->getQuery();
109
+
110
+		$this->factory = new Factory;
111
+
112
+		$this->parent = $this->factory->make($parent);
113
+
114
+		$this->parentMapper = $mapper->getManager()->getMapper($parent);
115
+
116
+		$this->parentMap = $this->parentMapper->getEntityMap();
117
+
118
+		$this->related = $this->query->getEntityInstance();
119
+
120
+		$this->relatedMap = $mapper->getEntityMap();
121
+
122
+		$this->addConstraints();
123
+	}
124
+
125
+	/**
126
+	 * @param $related
127
+	 * @return mixed
128
+	 */
129
+	abstract public function attachTo($related);
130
+
131
+	/**
132
+	 * @param $related
133
+	 * @return mixed
134
+	 */
135
+	abstract public function detachFrom($related);
136
+
137
+	/**
138
+	 * Indicate if the parent entity hold the foreign key for relation.
139
+	 *
140
+	 * @return boolean
141
+	 */
142
+	public function ownForeignKey()
143
+	{
144
+		return static::$ownForeignKey;
145
+	}
146
+
147
+	/**
148
+	 * Indicate if the relationship uses a pivot table
149
+	 *
150
+	 * @return boolean
151
+	 */
152
+	public function hasPivot()
153
+	{
154
+		return static::$hasPivot;
155
+	}
156
+
157
+	/**
158
+	 * Set the base constraints on the relation query.
159
+	 *
160
+	 * @return void
161
+	 */
162
+	abstract public function addConstraints();
163
+
164
+	/**
165
+	 * Set the constraints for an eager load of the relation.
166
+	 *
167
+	 * @param  array $models
168
+	 * @return void
169
+	 */
170
+	abstract public function addEagerConstraints(array $models);
171
+
172
+	/**
173
+	 * Initialize the relation on a set of models.
174
+	 *
175
+	 * @param  array  $models
176
+	 * @param  string $relation
177
+	 * @return array
178
+	 */
179
+	abstract public function initRelation(array $models, $relation);
180
+
181
+	/**
182
+	 * Match the eagerly loaded results to their parents.
183
+	 *
184
+	 * @param  array            $entities
185
+	 * @param  EntityCollection $results
186
+	 * @param  string           $relation
187
+	 * @return array
188
+	 */
189
+	abstract public function match(array $entities, EntityCollection $results, $relation);
190
+
191
+	/**
192
+	 * Get the results of the relationship.
193
+	 *
194
+	 * @param string $relation relation name in parent's entity map
195
+	 * @return mixed
196
+	 */
197
+	abstract public function getResults($relation);
198
+
199
+	/**
200
+	 * Get the relationship for eager loading.
201
+	 *
202
+	 * @return EntityCollection
203
+	 */
204
+	public function getEager()
205
+	{
206
+		return $this->get();
207
+	}
208
+
209
+	/**
210
+	 * Add the constraints for a relationship count query.
211
+	 *
212
+	 * @param  Query $query
213
+	 * @param  Query $parent
214
+	 * @return Query
215
+	 */
216
+	public function getRelationCountQuery(Query $query, Query $parent)
217
+	{
218
+		$query->select(new Expression('count(*)'));
219
+
220
+		$key = $this->wrap($this->getQualifiedParentKeyName());
221
+
222
+		return $query->where($this->getHasCompareKey(), '=', new Expression($key));
223
+	}
224
+
225
+	/**
226
+	 * Run a callback with constraints disabled on the relation.
227
+	 *
228
+	 * @param  Closure $callback
229
+	 * @return mixed
230
+	 */
231
+	public static function noConstraints(Closure $callback)
232
+	{
233
+		static::$constraints = false;
234
+
235
+		// When resetting the relation where clause, we want to shift the first element
236
+		// off of the bindings, leaving only the constraints that the developers put
237
+		// as "extra" on the relationships, and not original relation constraints.
238
+		$results = call_user_func($callback);
239
+
240
+		static::$constraints = true;
241
+
242
+		return $results;
243
+	}
244
+
245
+	/**
246
+	 * Get all of the primary keys for an array of entities.
247
+	 *
248
+	 * @param  array  $entities
249
+	 * @param  string $key
250
+	 * @return array
251
+	 */
252
+	protected function getKeys(array $entities, $key = null)
253
+	{
254
+		if (is_null($key)) {
255
+			$key = $this->relatedMap->getKeyName();
256
+		}
257
+
258
+		$host = $this;
259
+
260
+		return array_unique(array_values(array_map(function ($value) use ($key, $host) {
261
+			if (!$value instanceof InternallyMappable) {
262
+				$value = $host->factory->make($value);
263
+			}
264
+
265
+			return $value->getEntityAttribute($key);
266
+
267
+		}, $entities)));
268
+	}
269
+
270
+	/**
271
+	 * Get the underlying query for the relation.
272
+	 *
273
+	 * @return Query
274
+	 */
275
+	public function getQuery()
276
+	{
277
+		return $this->query;
278
+	}
279
+
280
+	/**
281
+	 * Get the base query builder
282
+	 *
283
+	 * @return \Analogue\ORM\Drivers\QueryAdapter
284
+	 */
285
+	public function getBaseQuery()
286
+	{
287
+		return $this->query->getQuery();
288
+	}
289
+
290
+	/**
291
+	 * Get the parent model of the relation.
292
+	 *
293
+	 * @return InternallyMappable
294
+	 */
295
+	public function getParent()
296
+	{
297
+		return $this->parent;
298
+	}
299
+
300
+	/**
301
+	 * Get the fully qualified parent key name.
302
+	 *
303
+	 * @return string
304
+	 */
305
+	protected function getQualifiedParentKeyName()
306
+	{
307
+		return $this->parent->getQualifiedKeyName();
308
+	}
309
+
310
+	/**
311
+	 * Get the related entity of the relation.
312
+	 *
313
+	 * @return \Analogue\ORM\Entity
314
+	 */
315
+	public function getRelated()
316
+	{
317
+		return $this->related;
318
+	}
319
+
320
+	/**
321
+	 * Get the related mapper for the relation
322
+	 *
323
+	 * @return Mapper
324
+	 */
325
+	public function getRelatedMapper()
326
+	{
327
+		return $this->relatedMapper;
328
+	}
329
+
330
+
331
+	/**
332
+	 * Get the name of the "created at" column.
333
+	 *
334
+	 * @return string
335
+	 */
336
+	public function createdAt()
337
+	{
338
+		return $this->parentMap->getCreatedAtColumn();
339
+	}
340
+
341
+	/**
342
+	 * Get the name of the "updated at" column.
343
+	 *
344
+	 * @return string
345
+	 */
346
+	public function updatedAt()
347
+	{
348
+		return $this->parentMap->getUpdatedAtColumn();
349
+	}
350
+
351
+	/**
352
+	 * Get the name of the related model's "updated at" column.
353
+	 *
354
+	 * @return string
355
+	 */
356
+	public function relatedUpdatedAt()
357
+	{
358
+		return $this->related->getUpdatedAtColumn();
359
+	}
360
+
361
+	/**
362
+	 * Wrap the given value with the parent query's grammar.
363
+	 *
364
+	 * @param  string $value
365
+	 * @return string
366
+	 */
367
+	public function wrap($value)
368
+	{
369
+		return $this->parentMapper->getQuery()->getQuery()->getGrammar()->wrap($value);
370
+	}
371
+
372
+	/**
373
+	 * Get a fresh timestamp
374
+	 *
375
+	 * @return Carbon
376
+	 */
377
+	protected function freshTimestamp()
378
+	{
379
+		return new Carbon;
380
+	}
381
+
382
+	/**
383
+	 * Cache the link between parent and related
384
+	 * into the mapper's Entity Cache.
385
+	 *
386
+	 * @param  EntityCollection|Mappable $results  result of the relation query
387
+	 * @param  string                    $relation name of the relation method on the parent entity
388
+	 * @return void
389
+	 */
390
+	protected function cacheRelation($results, $relation)
391
+	{
392
+		$cache = $this->parentMapper->getEntityCache();
393
+
394
+		$cache->cacheLoadedRelationResult($this->parent, $relation, $results, $this);
395
+	}
396
+
397
+	/**
398
+	 * Return Pivot attributes when available on a relationship
399
+	 *
400
+	 * @return array
401
+	 */
402
+	public function getPivotAttributes()
403
+	{
404
+		return [];
405
+	}
406
+
407
+	/**
408
+	 * Get a combo type.primaryKey
409
+	 *
410
+	 * @param  Mappable $entity
411
+	 * @return string
412
+	 */
413
+	protected function getEntityHash(Mappable $entity)
414
+	{
415
+		$class = get_class($entity);
416
+
417
+		$keyName = Mapper::getMapper($class)->getEntityMap()->getKeyName();
418
+
419
+		return $class . '.' . $entity->getEntityAttribute($keyName);
420
+	}
421
+
422
+	/**
423
+	 * Run synchronization content if needed by the
424
+	 * relation type.
425
+	 *
426
+	 * @param  array $actualContent
427
+	 * @return void
428
+	 */
429
+	abstract public function sync(array $actualContent);
430 430
     
431
-    /**
432
-     * Handle dynamic method calls to the relationship.
433
-     *
434
-     * @param  string $method
435
-     * @param  array  $parameters
436
-     * @return mixed
437
-     */
438
-    public function __call($method, $parameters)
439
-    {
440
-        $result = call_user_func_array([$this->query, $method], $parameters);
441
-
442
-        if ($result === $this->query) {
443
-            return $this;
444
-        }
445
-
446
-        return $result;
447
-    }
431
+	/**
432
+	 * Handle dynamic method calls to the relationship.
433
+	 *
434
+	 * @param  string $method
435
+	 * @param  array  $parameters
436
+	 * @return mixed
437
+	 */
438
+	public function __call($method, $parameters)
439
+	{
440
+		$result = call_user_func_array([$this->query, $method], $parameters);
441
+
442
+		if ($result === $this->query) {
443
+			return $this;
444
+		}
445
+
446
+		return $result;
447
+	}
448 448
 }
Please login to merge, or discard this patch.
src/System/Aggregate.php 1 patch
Indentation   +992 added lines, -992 removed lines patch added patch discarded remove patch
@@ -15,1015 +15,1015 @@
 block discarded – undo
15 15
  */
16 16
 class Aggregate implements InternallyMappable
17 17
 {
18
-    /**
19
-     * The Root Entity
20
-     *
21
-     * @var \Analogue\ORM\System\Wrappers\Wrapper
22
-     */
23
-    protected $wrappedEntity;
24
-
25
-    /**
26
-     * Parent Root Aggregate
27
-     *
28
-     * @var \Analogue\ORM\System\Aggregate
29
-     */
30
-    protected $parent;
31
-
32
-    /**
33
-     * Parent's relationship method
34
-     *
35
-     * @var string
36
-     */
37
-    protected $parentRelationship;
38
-
39
-    /**
40
-     * Root Entity
41
-     *
42
-     * @var \Analogue\ORM\System\Aggregate
43
-     */
44
-    protected $root;
45
-
46
-    /**
47
-     * An associative array containing entity's
48
-     * relationships converted to Aggregates
49
-     *
50
-     * @var array
51
-     */
52
-    protected $relationships = [];
53
-
54
-    /**
55
-     * Relationship that need post-command synchronization
56
-     *
57
-     * @var array
58
-     */
59
-    protected $needSync = [];
60
-
61
-    /**
62
-     * Mapper
63
-     *
64
-     * @var \Analogue\ORM\System\Mapper;
65
-     */
66
-    protected $mapper;
67
-
68
-    /**
69
-     * Entity Map
70
-     *
71
-     * @var \Analogue\ORM\EntityMap;
72
-     */
73
-    protected $entityMap;
74
-
75
-    /**
76
-     * Create a new Aggregated Entity instance
77
-     *
78
-     * @param mixed          $entity
79
-     * @param Aggregate|null $parent
80
-     * @param string         $parentRelationship
81
-     * @param Aggregate|null $root
82
-     * @throws MappingException
83
-     */
84
-    public function __construct($entity, Aggregate $parent = null, $parentRelationship = null, Aggregate $root = null)
85
-    {
86
-        $factory = new Factory;
18
+	/**
19
+	 * The Root Entity
20
+	 *
21
+	 * @var \Analogue\ORM\System\Wrappers\Wrapper
22
+	 */
23
+	protected $wrappedEntity;
24
+
25
+	/**
26
+	 * Parent Root Aggregate
27
+	 *
28
+	 * @var \Analogue\ORM\System\Aggregate
29
+	 */
30
+	protected $parent;
31
+
32
+	/**
33
+	 * Parent's relationship method
34
+	 *
35
+	 * @var string
36
+	 */
37
+	protected $parentRelationship;
38
+
39
+	/**
40
+	 * Root Entity
41
+	 *
42
+	 * @var \Analogue\ORM\System\Aggregate
43
+	 */
44
+	protected $root;
45
+
46
+	/**
47
+	 * An associative array containing entity's
48
+	 * relationships converted to Aggregates
49
+	 *
50
+	 * @var array
51
+	 */
52
+	protected $relationships = [];
53
+
54
+	/**
55
+	 * Relationship that need post-command synchronization
56
+	 *
57
+	 * @var array
58
+	 */
59
+	protected $needSync = [];
60
+
61
+	/**
62
+	 * Mapper
63
+	 *
64
+	 * @var \Analogue\ORM\System\Mapper;
65
+	 */
66
+	protected $mapper;
67
+
68
+	/**
69
+	 * Entity Map
70
+	 *
71
+	 * @var \Analogue\ORM\EntityMap;
72
+	 */
73
+	protected $entityMap;
74
+
75
+	/**
76
+	 * Create a new Aggregated Entity instance
77
+	 *
78
+	 * @param mixed          $entity
79
+	 * @param Aggregate|null $parent
80
+	 * @param string         $parentRelationship
81
+	 * @param Aggregate|null $root
82
+	 * @throws MappingException
83
+	 */
84
+	public function __construct($entity, Aggregate $parent = null, $parentRelationship = null, Aggregate $root = null)
85
+	{
86
+		$factory = new Factory;
87 87
         
88
-        $this->wrappedEntity = $factory->make($entity);
88
+		$this->wrappedEntity = $factory->make($entity);
89 89
 
90
-        $this->parent = $parent;
90
+		$this->parent = $parent;
91 91
 
92
-        $this->parentRelationship = $parentRelationship;
92
+		$this->parentRelationship = $parentRelationship;
93 93
 
94
-        $this->root = $root;
94
+		$this->root = $root;
95 95
 
96
-        $this->mapper = Manager::getMapper($entity);
96
+		$this->mapper = Manager::getMapper($entity);
97 97
 
98
-        $this->entityMap = $this->mapper->getEntityMap();
98
+		$this->entityMap = $this->mapper->getEntityMap();
99 99
              
100
-        $this->parseRelationships();
101
-    }
102
-
103
-    /**
104
-     * Parse Every relationships defined on the entity
105
-     *
106
-     * @throws MappingException
107
-     * @return void
108
-     */
109
-    protected function parseRelationships()
110
-    {
111
-        foreach ($this->entityMap->getSingleRelationships() as $relation) {
112
-            $this->parseSingleRelationship($relation);
113
-        }
114
-
115
-        foreach ($this->entityMap->getManyRelationships() as $relation) {
116
-            $this->parseManyRelationship($relation);
117
-        }
118
-    }
119
-
120
-    /**
121
-     * Parse for values common to single & many relations
122
-     *
123
-     * @param  string $relation
124
-     * @throws MappingException
125
-     * @return mixed|boolean
126
-     */
127
-    protected function parseForCommonValues($relation)
128
-    {
129
-        if (!$this->hasAttribute($relation)) {
130
-            // If no attribute exists for this relationships
131
-            // we'll make it a simple empty array. This will
132
-            // save us from constantly checking for the attributes
133
-            // actual existence.
134
-            $this->relationships[$relation] = [];
135
-            return false;
136
-        }
137
-
138
-        $value = $this->getRelationshipValue($relation);
139
-
140
-        if (is_null($value)) {
141
-            $this->relationships[$relation] = [];
142
-
143
-            // If the relationship's content is the null value
144
-            // and the Entity's exist in DB, we'll interpret this
145
-            // as the need to detach all related Entities,
146
-            // therefore a sync operation is needed.
147
-            $this->needSync[] = $relation;
148
-            return false;
149
-        }
150
-
151
-        return $value;
152
-    }
153
-
154
-    /**
155
-     * Parse a 'single' relationship
156
-     *
157
-     * @param  string $relation
158
-     * @throws MappingException
159
-     * @return boolean
160
-     */
161
-    protected function parseSingleRelationship($relation)
162
-    {
163
-        if (!$value = $this->parseForCommonValues($relation)) {
164
-            return true;
165
-        }
100
+		$this->parseRelationships();
101
+	}
102
+
103
+	/**
104
+	 * Parse Every relationships defined on the entity
105
+	 *
106
+	 * @throws MappingException
107
+	 * @return void
108
+	 */
109
+	protected function parseRelationships()
110
+	{
111
+		foreach ($this->entityMap->getSingleRelationships() as $relation) {
112
+			$this->parseSingleRelationship($relation);
113
+		}
114
+
115
+		foreach ($this->entityMap->getManyRelationships() as $relation) {
116
+			$this->parseManyRelationship($relation);
117
+		}
118
+	}
119
+
120
+	/**
121
+	 * Parse for values common to single & many relations
122
+	 *
123
+	 * @param  string $relation
124
+	 * @throws MappingException
125
+	 * @return mixed|boolean
126
+	 */
127
+	protected function parseForCommonValues($relation)
128
+	{
129
+		if (!$this->hasAttribute($relation)) {
130
+			// If no attribute exists for this relationships
131
+			// we'll make it a simple empty array. This will
132
+			// save us from constantly checking for the attributes
133
+			// actual existence.
134
+			$this->relationships[$relation] = [];
135
+			return false;
136
+		}
137
+
138
+		$value = $this->getRelationshipValue($relation);
139
+
140
+		if (is_null($value)) {
141
+			$this->relationships[$relation] = [];
142
+
143
+			// If the relationship's content is the null value
144
+			// and the Entity's exist in DB, we'll interpret this
145
+			// as the need to detach all related Entities,
146
+			// therefore a sync operation is needed.
147
+			$this->needSync[] = $relation;
148
+			return false;
149
+		}
150
+
151
+		return $value;
152
+	}
153
+
154
+	/**
155
+	 * Parse a 'single' relationship
156
+	 *
157
+	 * @param  string $relation
158
+	 * @throws MappingException
159
+	 * @return boolean
160
+	 */
161
+	protected function parseSingleRelationship($relation)
162
+	{
163
+		if (!$value = $this->parseForCommonValues($relation)) {
164
+			return true;
165
+		}
166 166
         
167
-        if ($value instanceof Collection || is_array($value) || $value instanceof CollectionProxy) {
168
-            throw new MappingException("Entity's attribute $relation should not be array, or collection");
169
-        }
170
-
171
-        if ($value instanceof EntityProxy && !$value->isLoaded()) {
172
-            $this->relationships[$relation] = [];
173
-            return true;
174
-        }
175
-
176
-        // If the attribute is a loaded proxy, swap it for its
177
-        // loaded entity.
178
-        if ($value instanceof EntityProxy && $value->isLoaded()) {
179
-            $value = $value->getUnderlyingObject();
180
-        }
181
-
182
-        if ($this->isParentOrRoot($value)) {
183
-            $this->relationships[$relation] = [];
184
-            return true;
185
-        }
186
-
187
-        // At this point, we can assume the attribute is an Entity instance
188
-        // so we'll treat it as such.
189
-        $subAggregate = $this->createSubAggregate($value, $relation);
167
+		if ($value instanceof Collection || is_array($value) || $value instanceof CollectionProxy) {
168
+			throw new MappingException("Entity's attribute $relation should not be array, or collection");
169
+		}
170
+
171
+		if ($value instanceof EntityProxy && !$value->isLoaded()) {
172
+			$this->relationships[$relation] = [];
173
+			return true;
174
+		}
175
+
176
+		// If the attribute is a loaded proxy, swap it for its
177
+		// loaded entity.
178
+		if ($value instanceof EntityProxy && $value->isLoaded()) {
179
+			$value = $value->getUnderlyingObject();
180
+		}
181
+
182
+		if ($this->isParentOrRoot($value)) {
183
+			$this->relationships[$relation] = [];
184
+			return true;
185
+		}
186
+
187
+		// At this point, we can assume the attribute is an Entity instance
188
+		// so we'll treat it as such.
189
+		$subAggregate = $this->createSubAggregate($value, $relation);
190 190
          
191
-        // Even if it's a single entity, we'll store it as an array
192
-        // just for consistency with other relationships
193
-        $this->relationships[$relation] = [$subAggregate];
194
-
195
-        // We always need to check a loaded relation is in sync
196
-        // with its local key
197
-        $this->needSync[] = $relation;
198
-
199
-        return true;
200
-    }
201
-
202
-    /**
203
-     * Check if value isn't parent or root in the aggregate
204
-     *
205
-     * @param  mixed
206
-     * @return boolean|null
207
-     */
208
-    protected function isParentOrRoot($value)
209
-    {
210
-        if (!is_null($this->root)) {
211
-            $rootClass = get_class($this->root->getEntityObject());
212
-            if ($rootClass == get_class($value)) {
213
-                return true;
214
-            }
215
-        }
216
-
217
-        if (!is_null($this->parent)) {
218
-            $parentClass = get_class($this->parent->getEntityObject());
219
-            if ($parentClass == get_class($value)) {
220
-                return true;
221
-            }
222
-        }
223
-    }
224
-
225
-    /**
226
-     * Parse a 'many' relationship
227
-     *
228
-     * @param  string $relation
229
-     * @throws MappingException
230
-     * @return boolean
231
-     */
232
-    protected function parseManyRelationship($relation)
233
-    {
234
-        if (!$value = $this->parseForCommonValues($relation)) {
235
-            return true;
236
-        }
237
-
238
-        if (is_array($value) || $value instanceof Collection) {
239
-            $this->needSync[] = $relation;
240
-        }
241
-        // If the relation is a proxy, we test is the relation
242
-        // has been lazy loaded, otherwise we'll just treat
243
-        // the subset of newly added items.
244
-        if ($value instanceof CollectionProxy && $value->isLoaded()) {
245
-            $this->needSync[] = $relation;
246
-            $value = $value->getUnderlyingCollection();
247
-        }
248
-
249
-        if ($value instanceof CollectionProxy && !$value->isLoaded()) {
250
-            $value = $value->getAddedItems();
251
-        }
252
-
253
-        // At this point $value should be either an array or an instance
254
-        // of a collection class.
255
-        if (!is_array($value) && !$value instanceof Collection) {
256
-            throw new MappingException("'$relation' attribute should be array() or Collection");
257
-        }
258
-
259
-        $this->relationships[$relation] = $this->createSubAggregates($value, $relation);
191
+		// Even if it's a single entity, we'll store it as an array
192
+		// just for consistency with other relationships
193
+		$this->relationships[$relation] = [$subAggregate];
194
+
195
+		// We always need to check a loaded relation is in sync
196
+		// with its local key
197
+		$this->needSync[] = $relation;
198
+
199
+		return true;
200
+	}
201
+
202
+	/**
203
+	 * Check if value isn't parent or root in the aggregate
204
+	 *
205
+	 * @param  mixed
206
+	 * @return boolean|null
207
+	 */
208
+	protected function isParentOrRoot($value)
209
+	{
210
+		if (!is_null($this->root)) {
211
+			$rootClass = get_class($this->root->getEntityObject());
212
+			if ($rootClass == get_class($value)) {
213
+				return true;
214
+			}
215
+		}
216
+
217
+		if (!is_null($this->parent)) {
218
+			$parentClass = get_class($this->parent->getEntityObject());
219
+			if ($parentClass == get_class($value)) {
220
+				return true;
221
+			}
222
+		}
223
+	}
224
+
225
+	/**
226
+	 * Parse a 'many' relationship
227
+	 *
228
+	 * @param  string $relation
229
+	 * @throws MappingException
230
+	 * @return boolean
231
+	 */
232
+	protected function parseManyRelationship($relation)
233
+	{
234
+		if (!$value = $this->parseForCommonValues($relation)) {
235
+			return true;
236
+		}
237
+
238
+		if (is_array($value) || $value instanceof Collection) {
239
+			$this->needSync[] = $relation;
240
+		}
241
+		// If the relation is a proxy, we test is the relation
242
+		// has been lazy loaded, otherwise we'll just treat
243
+		// the subset of newly added items.
244
+		if ($value instanceof CollectionProxy && $value->isLoaded()) {
245
+			$this->needSync[] = $relation;
246
+			$value = $value->getUnderlyingCollection();
247
+		}
248
+
249
+		if ($value instanceof CollectionProxy && !$value->isLoaded()) {
250
+			$value = $value->getAddedItems();
251
+		}
252
+
253
+		// At this point $value should be either an array or an instance
254
+		// of a collection class.
255
+		if (!is_array($value) && !$value instanceof Collection) {
256
+			throw new MappingException("'$relation' attribute should be array() or Collection");
257
+		}
258
+
259
+		$this->relationships[$relation] = $this->createSubAggregates($value, $relation);
260 260
         
261
-        return true;
262
-    }
263
-
264
-    /**
265
-     * Return Entity's relationship attribute
266
-     *
267
-     * @param  string $relation
268
-     * @throws MappingException
269
-     * @return mixed
270
-     */
271
-    protected function getRelationshipValue($relation)
272
-    {
273
-        $value = $this->getEntityAttribute($relation);
274
-        //if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
275
-        if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
276
-            throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
277
-        }
278
-
279
-        return $value;
280
-    }
281
-
282
-    /**
283
-     * Create a child, aggregated entity
284
-     *
285
-     * @param  mixed $entities
286
-     * @param string $relation
287
-     * @return array
288
-     */
289
-    protected function createSubAggregates($entities, $relation)
290
-    {
291
-        $aggregates = [];
292
-
293
-        foreach ($entities as $entity) {
294
-            $aggregates[] = $this->createSubAggregate($entity, $relation);
295
-        }
296
-
297
-        return $aggregates;
298
-    }
299
-
300
-    /**
301
-     * Create a related subAggregate
302
-     *
303
-     * @param  mixed $entity
304
-     * @param  string $relation
305
-     * @throws MappingException
306
-     * @return self
307
-     */
308
-    protected function createSubAggregate($entity, $relation)
309
-    {
310
-        // If root isn't defined, then this is the Aggregate Root
311
-        if (is_null($this->root)) {
312
-            $root = $this;
313
-        } else {
314
-            $root = $this->root;
315
-        }
316
-
317
-        return new self($entity, $this, $relation, $root);
318
-    }
319
-
320
-    /**
321
-     * Get the Entity's primary key attribute
322
-     *
323
-     * @return string|integer
324
-     */
325
-    public function getEntityId()
326
-    {
327
-        return $this->wrappedEntity->getEntityAttribute($this->entityMap->getKeyName());
328
-    }
329
-
330
-    /**
331
-     * Get the name of the primary key
332
-     *
333
-     * @return string
334
-     */
335
-    public function getEntityKey()
336
-    {
337
-        return $this->entityMap->getKeyName();
338
-    }
339
-
340
-    /**
341
-     * Return the entity map for the current entity
342
-     *
343
-     * @return \Analogue\ORM\EntityMap
344
-     */
345
-    public function getEntityMap()
346
-    {
347
-        return $this->entityMap;
348
-    }
349
-
350
-    /**
351
-     * Return the Entity's hash $class.$id
352
-     *
353
-     * @return string
354
-     */
355
-    public function getEntityHash()
356
-    {
357
-        return $this->getEntityClass() . '.' . $this->getEntityId();
358
-    }
359
-
360
-    /**
361
-     * Get wrapped entity class
362
-     *
363
-     * @return string
364
-     */
365
-    public function getEntityClass()
366
-    {
367
-        return $this->entityMap->getClass();
368
-    }
369
-
370
-    /**
371
-     * Return the Mapper's entity cache
372
-     *
373
-     * @return \Analogue\ORM\System\EntityCache
374
-     */
375
-    protected function getEntityCache()
376
-    {
377
-        return $this->mapper->getEntityCache();
378
-    }
379
-
380
-    /**
381
-     * Get a relationship as an aggregated entities' array
382
-     *
383
-     * @param  string $name
384
-     * @return array
385
-     */
386
-    public function getRelationship($name)
387
-    {
388
-        if (array_key_exists($name, $this->relationships)) {
389
-            return $this->relationships[$name];
390
-        } else {
391
-            return [];
392
-        }
393
-    }
394
-
395
-    /**
396
-     * [TO IMPLEMENT]
397
-     *
398
-     * @return array
399
-     */
400
-    public function getPivotAttributes()
401
-    {
402
-        return [];
403
-    }
404
-
405
-    /**
406
-     * Get Non existing related entities from several relationships
407
-     *
408
-     * @param  array $relationships
409
-     * @return array
410
-     */
411
-    public function getNonExistingRelated(array $relationships)
412
-    {
413
-        $nonExisting = [];
414
-
415
-        foreach ($relationships as $relation) {
416
-            if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
417
-                $nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
418
-            }
419
-        }
420
-
421
-        return $nonExisting;
422
-    }
423
-
424
-    /**
425
-     * Get non-existing related entities from a single relation
426
-     *
427
-     * @param  string $relation
428
-     * @return array
429
-     */
430
-    protected function getNonExistingFromRelation($relation)
431
-    {
432
-        $nonExisting = [];
433
-
434
-        foreach ($this->relationships[$relation] as $aggregate) {
435
-            if (!$aggregate->exists()) {
436
-                $nonExisting[] = $aggregate;
437
-            }
438
-        }
439
-
440
-        return $nonExisting;
441
-    }
442
-
443
-    /**
444
-     * Synchronize relationships if needed
445
-     */
446
-    public function syncRelationships(array $relationships)
447
-    {
448
-        if ($this->exists()) {
449
-            foreach ($relationships as $relation) {
450
-                if (in_array($relation, $this->needSync)) {
451
-                    $this->synchronize($relation);
452
-                }
453
-            }
454
-        }
455
-    }
456
-
457
-    /**
458
-     * Synchronize a relationship attribute
459
-     *
460
-     * @param $relation
461
-     */
462
-    protected function synchronize($relation)
463
-    {
464
-        $actualContent = $this->relationships[$relation];
465
-
466
-        $this->entityMap->$relation($this->getEntityObject())->sync($actualContent);
467
-    }
468
-
469
-    /**
470
-     * Returns an array of Missing related Entities for the
471
-     * given $relation
472
-     *
473
-     * @param  string $relation
474
-     * @return array
475
-     */
476
-    public function getMissingEntities($relation)
477
-    {
478
-        $cachedRelations = $this->getCachedAttribute($relation);
479
-
480
-        if (!is_null($cachedRelations)) {
481
-            $missing = [];
482
-
483
-            foreach ($cachedRelations as $hash) {
484
-                if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
485
-                    $missing[] = $hash;
486
-                }
487
-            }
488
-
489
-            return $missing;
490
-        } else {
491
-            return [];
492
-        }
493
-    }
261
+		return true;
262
+	}
263
+
264
+	/**
265
+	 * Return Entity's relationship attribute
266
+	 *
267
+	 * @param  string $relation
268
+	 * @throws MappingException
269
+	 * @return mixed
270
+	 */
271
+	protected function getRelationshipValue($relation)
272
+	{
273
+		$value = $this->getEntityAttribute($relation);
274
+		//if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
275
+		if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
276
+			throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
277
+		}
278
+
279
+		return $value;
280
+	}
281
+
282
+	/**
283
+	 * Create a child, aggregated entity
284
+	 *
285
+	 * @param  mixed $entities
286
+	 * @param string $relation
287
+	 * @return array
288
+	 */
289
+	protected function createSubAggregates($entities, $relation)
290
+	{
291
+		$aggregates = [];
292
+
293
+		foreach ($entities as $entity) {
294
+			$aggregates[] = $this->createSubAggregate($entity, $relation);
295
+		}
296
+
297
+		return $aggregates;
298
+	}
299
+
300
+	/**
301
+	 * Create a related subAggregate
302
+	 *
303
+	 * @param  mixed $entity
304
+	 * @param  string $relation
305
+	 * @throws MappingException
306
+	 * @return self
307
+	 */
308
+	protected function createSubAggregate($entity, $relation)
309
+	{
310
+		// If root isn't defined, then this is the Aggregate Root
311
+		if (is_null($this->root)) {
312
+			$root = $this;
313
+		} else {
314
+			$root = $this->root;
315
+		}
316
+
317
+		return new self($entity, $this, $relation, $root);
318
+	}
319
+
320
+	/**
321
+	 * Get the Entity's primary key attribute
322
+	 *
323
+	 * @return string|integer
324
+	 */
325
+	public function getEntityId()
326
+	{
327
+		return $this->wrappedEntity->getEntityAttribute($this->entityMap->getKeyName());
328
+	}
329
+
330
+	/**
331
+	 * Get the name of the primary key
332
+	 *
333
+	 * @return string
334
+	 */
335
+	public function getEntityKey()
336
+	{
337
+		return $this->entityMap->getKeyName();
338
+	}
339
+
340
+	/**
341
+	 * Return the entity map for the current entity
342
+	 *
343
+	 * @return \Analogue\ORM\EntityMap
344
+	 */
345
+	public function getEntityMap()
346
+	{
347
+		return $this->entityMap;
348
+	}
349
+
350
+	/**
351
+	 * Return the Entity's hash $class.$id
352
+	 *
353
+	 * @return string
354
+	 */
355
+	public function getEntityHash()
356
+	{
357
+		return $this->getEntityClass() . '.' . $this->getEntityId();
358
+	}
359
+
360
+	/**
361
+	 * Get wrapped entity class
362
+	 *
363
+	 * @return string
364
+	 */
365
+	public function getEntityClass()
366
+	{
367
+		return $this->entityMap->getClass();
368
+	}
369
+
370
+	/**
371
+	 * Return the Mapper's entity cache
372
+	 *
373
+	 * @return \Analogue\ORM\System\EntityCache
374
+	 */
375
+	protected function getEntityCache()
376
+	{
377
+		return $this->mapper->getEntityCache();
378
+	}
379
+
380
+	/**
381
+	 * Get a relationship as an aggregated entities' array
382
+	 *
383
+	 * @param  string $name
384
+	 * @return array
385
+	 */
386
+	public function getRelationship($name)
387
+	{
388
+		if (array_key_exists($name, $this->relationships)) {
389
+			return $this->relationships[$name];
390
+		} else {
391
+			return [];
392
+		}
393
+	}
394
+
395
+	/**
396
+	 * [TO IMPLEMENT]
397
+	 *
398
+	 * @return array
399
+	 */
400
+	public function getPivotAttributes()
401
+	{
402
+		return [];
403
+	}
404
+
405
+	/**
406
+	 * Get Non existing related entities from several relationships
407
+	 *
408
+	 * @param  array $relationships
409
+	 * @return array
410
+	 */
411
+	public function getNonExistingRelated(array $relationships)
412
+	{
413
+		$nonExisting = [];
414
+
415
+		foreach ($relationships as $relation) {
416
+			if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
417
+				$nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
418
+			}
419
+		}
420
+
421
+		return $nonExisting;
422
+	}
423
+
424
+	/**
425
+	 * Get non-existing related entities from a single relation
426
+	 *
427
+	 * @param  string $relation
428
+	 * @return array
429
+	 */
430
+	protected function getNonExistingFromRelation($relation)
431
+	{
432
+		$nonExisting = [];
433
+
434
+		foreach ($this->relationships[$relation] as $aggregate) {
435
+			if (!$aggregate->exists()) {
436
+				$nonExisting[] = $aggregate;
437
+			}
438
+		}
439
+
440
+		return $nonExisting;
441
+	}
442
+
443
+	/**
444
+	 * Synchronize relationships if needed
445
+	 */
446
+	public function syncRelationships(array $relationships)
447
+	{
448
+		if ($this->exists()) {
449
+			foreach ($relationships as $relation) {
450
+				if (in_array($relation, $this->needSync)) {
451
+					$this->synchronize($relation);
452
+				}
453
+			}
454
+		}
455
+	}
456
+
457
+	/**
458
+	 * Synchronize a relationship attribute
459
+	 *
460
+	 * @param $relation
461
+	 */
462
+	protected function synchronize($relation)
463
+	{
464
+		$actualContent = $this->relationships[$relation];
465
+
466
+		$this->entityMap->$relation($this->getEntityObject())->sync($actualContent);
467
+	}
468
+
469
+	/**
470
+	 * Returns an array of Missing related Entities for the
471
+	 * given $relation
472
+	 *
473
+	 * @param  string $relation
474
+	 * @return array
475
+	 */
476
+	public function getMissingEntities($relation)
477
+	{
478
+		$cachedRelations = $this->getCachedAttribute($relation);
479
+
480
+		if (!is_null($cachedRelations)) {
481
+			$missing = [];
482
+
483
+			foreach ($cachedRelations as $hash) {
484
+				if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
485
+					$missing[] = $hash;
486
+				}
487
+			}
488
+
489
+			return $missing;
490
+		} else {
491
+			return [];
492
+		}
493
+	}
494 494
        
495
-    /**
496
-     * Get Relationships who have dirty attributes / dirty relationships
497
-     *
498
-     * @return array
499
-     */
500
-    public function getDirtyRelationships()
501
-    {
502
-        $dirtyAggregates = [];
503
-
504
-        foreach ($this->relationships as $relation) {
505
-            foreach ($relation as $aggregate) {
506
-                if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships() > 0)) {
507
-                    $dirtyAggregates[] = $aggregate;
508
-                }
509
-            }
510
-        }
511
-
512
-        return $dirtyAggregates;
513
-    }
495
+	/**
496
+	 * Get Relationships who have dirty attributes / dirty relationships
497
+	 *
498
+	 * @return array
499
+	 */
500
+	public function getDirtyRelationships()
501
+	{
502
+		$dirtyAggregates = [];
503
+
504
+		foreach ($this->relationships as $relation) {
505
+			foreach ($relation as $aggregate) {
506
+				if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships() > 0)) {
507
+					$dirtyAggregates[] = $aggregate;
508
+				}
509
+			}
510
+		}
511
+
512
+		return $dirtyAggregates;
513
+	}
514 514
     
515
-    /**
516
-     * Compare the object's raw attributes with the record in cache
517
-     *
518
-     * @return boolean
519
-     */
520
-    public function isDirty()
521
-    {
522
-        if (count($this->getDirtyRawAttributes()) > 0) {
523
-            return true;
524
-        } else {
525
-            return false;
526
-        }
527
-    }
528
-
529
-    /**
530
-     * Get Raw Entity's attributes, as they are represented
531
-     * in the database, including value objects & foreign keys
532
-     *
533
-     * @return array
534
-     */
535
-    public function getRawAttributes()
536
-    {
537
-        $attributes = $this->wrappedEntity->getEntityAttributes();
538
-
539
-        foreach ($this->entityMap->getRelationships() as $relation) {
540
-            unset($attributes[$relation]);
541
-        }
542
-
543
-        $attributes = $this->flattenEmbeddables($attributes);
544
-
545
-        $foreignKeys = $this->getForeignKeyAttributes();
546
-
547
-        return $attributes + $foreignKeys;
548
-    }
549
-
550
-    /**
551
-     * Convert Value Objects to raw db attributes
552
-     *
553
-     * @param  array $attributes
554
-     * @return array
555
-     */
556
-    protected function flattenEmbeddables($attributes)
557
-    {
558
-        $embeddables = $this->entityMap->getEmbeddables();
515
+	/**
516
+	 * Compare the object's raw attributes with the record in cache
517
+	 *
518
+	 * @return boolean
519
+	 */
520
+	public function isDirty()
521
+	{
522
+		if (count($this->getDirtyRawAttributes()) > 0) {
523
+			return true;
524
+		} else {
525
+			return false;
526
+		}
527
+	}
528
+
529
+	/**
530
+	 * Get Raw Entity's attributes, as they are represented
531
+	 * in the database, including value objects & foreign keys
532
+	 *
533
+	 * @return array
534
+	 */
535
+	public function getRawAttributes()
536
+	{
537
+		$attributes = $this->wrappedEntity->getEntityAttributes();
538
+
539
+		foreach ($this->entityMap->getRelationships() as $relation) {
540
+			unset($attributes[$relation]);
541
+		}
542
+
543
+		$attributes = $this->flattenEmbeddables($attributes);
544
+
545
+		$foreignKeys = $this->getForeignKeyAttributes();
546
+
547
+		return $attributes + $foreignKeys;
548
+	}
549
+
550
+	/**
551
+	 * Convert Value Objects to raw db attributes
552
+	 *
553
+	 * @param  array $attributes
554
+	 * @return array
555
+	 */
556
+	protected function flattenEmbeddables($attributes)
557
+	{
558
+		$embeddables = $this->entityMap->getEmbeddables();
559 559
         
560
-        foreach ($embeddables as $localKey => $embed) {
561
-            // Retrieve the value object from the entity's attributes
562
-            $valueObject = $attributes[$localKey];
560
+		foreach ($embeddables as $localKey => $embed) {
561
+			// Retrieve the value object from the entity's attributes
562
+			$valueObject = $attributes[$localKey];
563 563
 
564
-            // Unset the corresponding key
565
-            unset($attributes[$localKey]);
564
+			// Unset the corresponding key
565
+			unset($attributes[$localKey]);
566 566
 
567
-            // TODO Make wrapper object compatible with value objects
568
-            $valueObjectAttributes = $valueObject->getEntityAttributes();
567
+			// TODO Make wrapper object compatible with value objects
568
+			$valueObjectAttributes = $valueObject->getEntityAttributes();
569 569
 
570
-            // Now (if setup in the entity map) we prefix the value object's
571
-            // attributes with the snake_case name of the embedded class.
572
-            $prefix = snake_case(class_basename($embed));
570
+			// Now (if setup in the entity map) we prefix the value object's
571
+			// attributes with the snake_case name of the embedded class.
572
+			$prefix = snake_case(class_basename($embed));
573 573
 
574
-            foreach ($valueObjectAttributes as $key=>$value) {
575
-                $valueObjectAttributes[$prefix . '_' . $key] = $value;
576
-                unset($valueObjectAttributes[$key]);
577
-            }
574
+			foreach ($valueObjectAttributes as $key=>$value) {
575
+				$valueObjectAttributes[$prefix . '_' . $key] = $value;
576
+				unset($valueObjectAttributes[$key]);
577
+			}
578 578
 
579
-            $attributes = array_merge($attributes, $valueObjectAttributes);
580
-        }
579
+			$attributes = array_merge($attributes, $valueObjectAttributes);
580
+		}
581 581
         
582
-        return $attributes;
583
-    }
584
-
585
-    /**
586
-     * Return's entity raw attributes in the state they were at last
587
-     * query.
588
-     *
589
-     * @param  array|null $columns
590
-     * @return array
591
-     */
592
-    protected function getCachedRawAttributes(array $columns = null)
593
-    {
594
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
595
-
596
-        if (is_null($columns)) {
597
-            return $cachedAttributes;
598
-        } else {
599
-            return array_only($cachedAttributes, $columns);
600
-        }
601
-    }
602
-
603
-    /**
604
-     * Return a single attribute from the cache
605
-     * @param  string $key
606
-     * @return mixed
607
-     */
608
-    protected function getCachedAttribute($key)
609
-    {
610
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
611
-
612
-        if (!array_key_exists($key, $cachedAttributes)) {
613
-            return null;
614
-        } else {
615
-            return $cachedAttributes[$key];
616
-        }
617
-    }
618
-
619
-    /**
620
-     * Convert related Entity's attributes to foreign keys
621
-     *
622
-     * @return array
623
-     */
624
-    protected function getForeignKeyAttributes()
625
-    {
626
-        $foreignKeys = [];
627
-
628
-        foreach ($this->entityMap->getLocalRelationships() as $relation) {
629
-            // check if relationship has been parsed, meaning it has an actual object
630
-            // in the entity's attributes
631
-            if ($this->isActualRelationships($relation)) {
632
-                $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
633
-            }
634
-        }
635
-
636
-        if (!is_null($this->parent)) {
637
-            $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
638
-        }
639
-
640
-        return $foreignKeys;
641
-    }
642
-
643
-    /**
644
-     * Return an associative array containing the key-value pair(s) from
645
-     * the related entity.
646
-     *
647
-     * @param  string $relation
648
-     * @return array
649
-     */
650
-    protected function getForeignKeyAttributesFromRelation($relation)
651
-    {
652
-        $localRelations = $this->entityMap->getLocalRelationships();
653
-
654
-        if (in_array($relation, $localRelations)) {
655
-            // Call Relationship's method
656
-            $relationship = $this->entityMap->$relation($this->getEntityObject());
657
-
658
-            $relatedAggregate = $this->relationships[$relation][0];
659
-
660
-            return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
661
-        } else {
662
-            return [];
663
-        }
664
-    }
665
-
666
-    /**
667
-     * Get foreign key attribute(s) from a parent entity in this
668
-     * aggregate context
669
-     *
670
-     * @return array
671
-     */
672
-    protected function getForeignKeyAttributesFromParent()
673
-    {
674
-        $parentMap = $this->parent->getEntityMap();
675
-
676
-        $parentForeignRelations = $parentMap->getForeignRelationships();
677
-        $parentPivotRelations = $parentMap->getPivotRelationships();
678
-
679
-        $parentRelation = $this->parentRelationship;
680
-
681
-        if (in_array($parentRelation, $parentForeignRelations)
682
-            && !in_array($parentRelation, $parentPivotRelations)
683
-        ) {
684
-            $parentObject = $this->parent->getEntityObject();
685
-
686
-            // Call Relationship's method on parent map
687
-            $relationship = $parentMap->$parentRelation($parentObject);
688
-
689
-            return $relationship->getForeignKeyValuePair();
690
-        } else {
691
-            return [];
692
-        }
693
-    }
694
-
695
-    /**
696
-     * Update Pivot records on loaded relationships, by comparing the
697
-     * values from the Entity Cache to the actual relationship inside
698
-     * the aggregated entity.
699
-     *
700
-     * @return void
701
-     */
702
-    public function updatePivotRecords()
703
-    {
704
-        $pivots = $this->entityMap->getPivotRelationships();
705
-
706
-        foreach ($pivots as $pivot) {
707
-            if (array_key_exists($pivot, $this->relationships)) {
708
-                $this->updatePivotRelation($pivot);
709
-            }
710
-        }
711
-    }
712
-
713
-    /**
714
-     * Update Single pivot relationship
715
-     *
716
-     * @param  string $relation
717
-     * @return void
718
-     */
719
-    protected function updatePivotRelation($relation)
720
-    {
721
-        $hashes = $this->getEntityHashesFromRelation($relation);
722
-
723
-        $cachedAttributes = $this->getCachedRawAttributes();
724
-
725
-        if (array_key_exists($relation, $cachedAttributes)) {
726
-            // Compare the two array of hashes to find out existing
727
-            // pivot records, and the ones to be created.
728
-            $new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
729
-            $existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
730
-        } else {
731
-            $existing = [];
732
-            $new = $hashes;
733
-        }
734
-
735
-        if (count($new) > 0) {
736
-            $pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
737
-
738
-            $this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
739
-        }
740
-
741
-        if (count($existing) > 0) {
742
-            foreach ($existing as $pivotHash) {
743
-                $this->updatePivotIfDirty($pivotHash, $relation);
744
-            }
745
-        }
746
-    }
747
-
748
-    /**
749
-     * Compare existing pivot record in cache and update it
750
-     * if the pivot attributes are dirty
751
-     *
752
-     * @param  string $pivotHash
753
-     * @param  string $relation
754
-     * @return void
755
-     */
756
-    protected function updatePivotIfDirty($pivotHash, $relation)
757
-    {
758
-        $aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
759
-
760
-        if ($aggregate->hasAttribute('pivot')) {
761
-            $pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
762
-
763
-            $cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
764
-
765
-            $actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
766
-
767
-            $dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
768
-
769
-            if (count($dirty) > 0) {
770
-                $id = $aggregate->getEntityId();
771
-
772
-                $this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
773
-            }
774
-        }
775
-    }
776
-
777
-    /**
778
-     * Compare two attributes array and return dirty attributes
779
-     *
780
-     * @param  array $actual
781
-     * @param  array $cached
782
-     * @return array
783
-     */
784
-    protected function getDirtyAttributes(array $actual, array $cached)
785
-    {
786
-        $dirty = [];
787
-
788
-        foreach ($actual as $key => $value) {
789
-            if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
790
-                $dirty[$key] = $actual[$key];
791
-            }
792
-        }
793
-
794
-        return $dirty;
795
-    }
796
-
797
-    /**
798
-     *
799
-     * @param  string $pivotHash
800
-     * @param  string $relation
801
-     * @return array
802
-     */
803
-    protected function getPivotAttributesFromCache($pivotHash, $relation)
804
-    {
805
-        $cachedAttributes = $this->getCachedRawAttributes();
806
-
807
-        $cachedRelations = $cachedAttributes[$relation];
808
-
809
-        foreach ($cachedRelations as $cachedRelation) {
810
-            if ($cachedRelation == $pivotHash) {
811
-                return $cachedRelation->getPivotAttributes();
812
-            }
813
-        }
814
-    }
815
-
816
-    /**
817
-     * Returns an array of related Aggregates from its entity hashes
818
-     *
819
-     * @param  array  $hashes
820
-     * @param  string $relation
821
-     * @return array
822
-     */
823
-    protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
824
-    {
825
-        $related = [];
826
-
827
-        foreach ($hashes as $hash) {
828
-            $aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
829
-
830
-            if (!is_null($aggregate)) {
831
-                $related[] = $aggregate;
832
-            }
833
-        }
834
-
835
-        return $related;
836
-    }
837
-
838
-    /**
839
-     * Get related aggregate from its hash
840
-     *
841
-     * @param  string $hash
842
-     * @param  string $relation
843
-     * @return \Analogue\ORM\System\Aggregate|null
844
-     */
845
-    protected function getRelatedAggregateFromHash($hash, $relation)
846
-    {
847
-        foreach ($this->relationships[$relation] as $aggregate) {
848
-            if ($aggregate->getEntityHash() == $hash) {
849
-                return $aggregate;
850
-            }
851
-        }
852
-        return null;
853
-    }
854
-
855
-    /**
856
-     * Return an array of Entity Hashes from a specific relation
857
-     *
858
-     * @param  string $relation
859
-     * @return array
860
-     */
861
-    protected function getEntityHashesFromRelation($relation)
862
-    {
863
-        return array_map(function ($aggregate) {
864
-            return $aggregate->getEntityHash();
865
-        }, $this->relationships[$relation]);
866
-    }
867
-
868
-    /**
869
-     * Check the existence of an actual relationship
870
-     *
871
-     * @param  string $relation
872
-     * @return boolean
873
-     */
874
-    protected function isActualRelationships($relation)
875
-    {
876
-        return array_key_exists($relation, $this->relationships)
877
-            && count($this->relationships[$relation]) > 0;
878
-    }
879
-
880
-    /**
881
-     * Return cache instance for the current entity type
882
-     *
883
-     * @return \Analogue\ORM\System\EntityCache
884
-     */
885
-    protected function getCache()
886
-    {
887
-        return $this->mapper->getEntityCache();
888
-    }
889
-
890
-    /**
891
-     * Get Only Raw Entiy's attributes which have been modified
892
-     * since last query
893
-     *
894
-     * @return array
895
-     */
896
-    public function getDirtyRawAttributes()
897
-    {
898
-        $attributes = $this->getRawAttributes();
899
-        $cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
900
-
901
-        $dirty = [];
902
-
903
-        foreach ($attributes as $key => $value) {
904
-            if ($this->isRelation($key) || $key == 'pivot') {
905
-                continue;
906
-            }
907
-
908
-            if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
909
-                $dirty[$key] = $value;
910
-            } elseif ($value !== $cachedAttributes[$key] &&
911
-                !$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
912
-                $dirty[$key] = $value;
913
-            }
914
-        }
915
-
916
-        return $dirty;
917
-    }
918
-
919
-    /**
920
-     * @param $key
921
-     * @return bool
922
-     */
923
-    protected function isRelation($key)
924
-    {
925
-        return in_array($key, $this->entityMap->getRelationships());
926
-    }
927
-
928
-    /**
929
-     * Determine if the new and old values for a given key are numerically equivalent.
930
-     *
931
-     * @param $current
932
-     * @param $original
933
-     * @return boolean
934
-     */
935
-    protected function originalIsNumericallyEquivalent($current, $original)
936
-    {
937
-        return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
938
-    }
939
-
940
-    /**
941
-     * Get the underlying entity object
942
-     *
943
-     * @return mixed
944
-     */
945
-    public function getEntityObject()
946
-    {
947
-        return $this->wrappedEntity->getObject();
948
-    }
949
-
950
-    /**
951
-     * Return the Mapper instance for the current Entity Type
952
-     *
953
-     * @return \Analogue\ORM\System\Mapper
954
-     */
955
-    public function getMapper()
956
-    {
957
-        return $this->mapper;
958
-    }
959
-
960
-    /**
961
-     * Check that the entity already exists in the database, by checking
962
-     * if it has an EntityCache record
963
-     *
964
-     * @return boolean
965
-     */
966
-    public function exists()
967
-    {
968
-        return $this->getCache()->has($this->getEntityId());
969
-    }
970
-
971
-    /**
972
-     * Set the object attribute raw values (hydration)
973
-     *
974
-     * @param array $attributes
975
-     */
976
-    public function setEntityAttributes(array $attributes)
977
-    {
978
-        $this->wrappedEntity->setEntityAttributes($attributes);
979
-    }
980
-
981
-    /**
982
-     * Get the raw object's values.
983
-     *
984
-     * @return array
985
-     */
986
-    public function getEntityAttributes()
987
-    {
988
-        return $this->wrappedEntity->getEntityAttributes();
989
-    }
990
-
991
-    /**
992
-     * Set the raw entity attributes
993
-     * @param string $key
994
-     * @param string $value
995
-     */
996
-    public function setEntityAttribute($key, $value)
997
-    {
998
-        $this->wrappedEntity->setEntityAttribute($key, $value);
999
-    }
1000
-
1001
-    /**
1002
-     * Return the entity's attribute
1003
-     * @param  string $key
1004
-     * @return mixed
1005
-     */
1006
-    public function getEntityAttribute($key)
1007
-    {
1008
-        return $this->wrappedEntity->getEntityAttribute($key);
1009
-    }
1010
-
1011
-    /**
1012
-     * Does the attribute exists on the entity
1013
-     *
1014
-     * @param  string  $key
1015
-     * @return boolean
1016
-     */
1017
-    public function hasAttribute($key)
1018
-    {
1019
-        return $this->wrappedEntity->hasAttribute($key);
1020
-    }
1021
-
1022
-    /**
1023
-     * Set the lazyloading proxies on the wrapped entity
1024
-     */
1025
-    public function setProxies()
1026
-    {
1027
-        $this->wrappedEntity->setProxies();
1028
-    }
582
+		return $attributes;
583
+	}
584
+
585
+	/**
586
+	 * Return's entity raw attributes in the state they were at last
587
+	 * query.
588
+	 *
589
+	 * @param  array|null $columns
590
+	 * @return array
591
+	 */
592
+	protected function getCachedRawAttributes(array $columns = null)
593
+	{
594
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
595
+
596
+		if (is_null($columns)) {
597
+			return $cachedAttributes;
598
+		} else {
599
+			return array_only($cachedAttributes, $columns);
600
+		}
601
+	}
602
+
603
+	/**
604
+	 * Return a single attribute from the cache
605
+	 * @param  string $key
606
+	 * @return mixed
607
+	 */
608
+	protected function getCachedAttribute($key)
609
+	{
610
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
611
+
612
+		if (!array_key_exists($key, $cachedAttributes)) {
613
+			return null;
614
+		} else {
615
+			return $cachedAttributes[$key];
616
+		}
617
+	}
618
+
619
+	/**
620
+	 * Convert related Entity's attributes to foreign keys
621
+	 *
622
+	 * @return array
623
+	 */
624
+	protected function getForeignKeyAttributes()
625
+	{
626
+		$foreignKeys = [];
627
+
628
+		foreach ($this->entityMap->getLocalRelationships() as $relation) {
629
+			// check if relationship has been parsed, meaning it has an actual object
630
+			// in the entity's attributes
631
+			if ($this->isActualRelationships($relation)) {
632
+				$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
633
+			}
634
+		}
635
+
636
+		if (!is_null($this->parent)) {
637
+			$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
638
+		}
639
+
640
+		return $foreignKeys;
641
+	}
642
+
643
+	/**
644
+	 * Return an associative array containing the key-value pair(s) from
645
+	 * the related entity.
646
+	 *
647
+	 * @param  string $relation
648
+	 * @return array
649
+	 */
650
+	protected function getForeignKeyAttributesFromRelation($relation)
651
+	{
652
+		$localRelations = $this->entityMap->getLocalRelationships();
653
+
654
+		if (in_array($relation, $localRelations)) {
655
+			// Call Relationship's method
656
+			$relationship = $this->entityMap->$relation($this->getEntityObject());
657
+
658
+			$relatedAggregate = $this->relationships[$relation][0];
659
+
660
+			return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
661
+		} else {
662
+			return [];
663
+		}
664
+	}
665
+
666
+	/**
667
+	 * Get foreign key attribute(s) from a parent entity in this
668
+	 * aggregate context
669
+	 *
670
+	 * @return array
671
+	 */
672
+	protected function getForeignKeyAttributesFromParent()
673
+	{
674
+		$parentMap = $this->parent->getEntityMap();
675
+
676
+		$parentForeignRelations = $parentMap->getForeignRelationships();
677
+		$parentPivotRelations = $parentMap->getPivotRelationships();
678
+
679
+		$parentRelation = $this->parentRelationship;
680
+
681
+		if (in_array($parentRelation, $parentForeignRelations)
682
+			&& !in_array($parentRelation, $parentPivotRelations)
683
+		) {
684
+			$parentObject = $this->parent->getEntityObject();
685
+
686
+			// Call Relationship's method on parent map
687
+			$relationship = $parentMap->$parentRelation($parentObject);
688
+
689
+			return $relationship->getForeignKeyValuePair();
690
+		} else {
691
+			return [];
692
+		}
693
+	}
694
+
695
+	/**
696
+	 * Update Pivot records on loaded relationships, by comparing the
697
+	 * values from the Entity Cache to the actual relationship inside
698
+	 * the aggregated entity.
699
+	 *
700
+	 * @return void
701
+	 */
702
+	public function updatePivotRecords()
703
+	{
704
+		$pivots = $this->entityMap->getPivotRelationships();
705
+
706
+		foreach ($pivots as $pivot) {
707
+			if (array_key_exists($pivot, $this->relationships)) {
708
+				$this->updatePivotRelation($pivot);
709
+			}
710
+		}
711
+	}
712
+
713
+	/**
714
+	 * Update Single pivot relationship
715
+	 *
716
+	 * @param  string $relation
717
+	 * @return void
718
+	 */
719
+	protected function updatePivotRelation($relation)
720
+	{
721
+		$hashes = $this->getEntityHashesFromRelation($relation);
722
+
723
+		$cachedAttributes = $this->getCachedRawAttributes();
724
+
725
+		if (array_key_exists($relation, $cachedAttributes)) {
726
+			// Compare the two array of hashes to find out existing
727
+			// pivot records, and the ones to be created.
728
+			$new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
729
+			$existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
730
+		} else {
731
+			$existing = [];
732
+			$new = $hashes;
733
+		}
734
+
735
+		if (count($new) > 0) {
736
+			$pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
737
+
738
+			$this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
739
+		}
740
+
741
+		if (count($existing) > 0) {
742
+			foreach ($existing as $pivotHash) {
743
+				$this->updatePivotIfDirty($pivotHash, $relation);
744
+			}
745
+		}
746
+	}
747
+
748
+	/**
749
+	 * Compare existing pivot record in cache and update it
750
+	 * if the pivot attributes are dirty
751
+	 *
752
+	 * @param  string $pivotHash
753
+	 * @param  string $relation
754
+	 * @return void
755
+	 */
756
+	protected function updatePivotIfDirty($pivotHash, $relation)
757
+	{
758
+		$aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
759
+
760
+		if ($aggregate->hasAttribute('pivot')) {
761
+			$pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
762
+
763
+			$cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
764
+
765
+			$actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
766
+
767
+			$dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
768
+
769
+			if (count($dirty) > 0) {
770
+				$id = $aggregate->getEntityId();
771
+
772
+				$this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
773
+			}
774
+		}
775
+	}
776
+
777
+	/**
778
+	 * Compare two attributes array and return dirty attributes
779
+	 *
780
+	 * @param  array $actual
781
+	 * @param  array $cached
782
+	 * @return array
783
+	 */
784
+	protected function getDirtyAttributes(array $actual, array $cached)
785
+	{
786
+		$dirty = [];
787
+
788
+		foreach ($actual as $key => $value) {
789
+			if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
790
+				$dirty[$key] = $actual[$key];
791
+			}
792
+		}
793
+
794
+		return $dirty;
795
+	}
796
+
797
+	/**
798
+	 *
799
+	 * @param  string $pivotHash
800
+	 * @param  string $relation
801
+	 * @return array
802
+	 */
803
+	protected function getPivotAttributesFromCache($pivotHash, $relation)
804
+	{
805
+		$cachedAttributes = $this->getCachedRawAttributes();
806
+
807
+		$cachedRelations = $cachedAttributes[$relation];
808
+
809
+		foreach ($cachedRelations as $cachedRelation) {
810
+			if ($cachedRelation == $pivotHash) {
811
+				return $cachedRelation->getPivotAttributes();
812
+			}
813
+		}
814
+	}
815
+
816
+	/**
817
+	 * Returns an array of related Aggregates from its entity hashes
818
+	 *
819
+	 * @param  array  $hashes
820
+	 * @param  string $relation
821
+	 * @return array
822
+	 */
823
+	protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
824
+	{
825
+		$related = [];
826
+
827
+		foreach ($hashes as $hash) {
828
+			$aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
829
+
830
+			if (!is_null($aggregate)) {
831
+				$related[] = $aggregate;
832
+			}
833
+		}
834
+
835
+		return $related;
836
+	}
837
+
838
+	/**
839
+	 * Get related aggregate from its hash
840
+	 *
841
+	 * @param  string $hash
842
+	 * @param  string $relation
843
+	 * @return \Analogue\ORM\System\Aggregate|null
844
+	 */
845
+	protected function getRelatedAggregateFromHash($hash, $relation)
846
+	{
847
+		foreach ($this->relationships[$relation] as $aggregate) {
848
+			if ($aggregate->getEntityHash() == $hash) {
849
+				return $aggregate;
850
+			}
851
+		}
852
+		return null;
853
+	}
854
+
855
+	/**
856
+	 * Return an array of Entity Hashes from a specific relation
857
+	 *
858
+	 * @param  string $relation
859
+	 * @return array
860
+	 */
861
+	protected function getEntityHashesFromRelation($relation)
862
+	{
863
+		return array_map(function ($aggregate) {
864
+			return $aggregate->getEntityHash();
865
+		}, $this->relationships[$relation]);
866
+	}
867
+
868
+	/**
869
+	 * Check the existence of an actual relationship
870
+	 *
871
+	 * @param  string $relation
872
+	 * @return boolean
873
+	 */
874
+	protected function isActualRelationships($relation)
875
+	{
876
+		return array_key_exists($relation, $this->relationships)
877
+			&& count($this->relationships[$relation]) > 0;
878
+	}
879
+
880
+	/**
881
+	 * Return cache instance for the current entity type
882
+	 *
883
+	 * @return \Analogue\ORM\System\EntityCache
884
+	 */
885
+	protected function getCache()
886
+	{
887
+		return $this->mapper->getEntityCache();
888
+	}
889
+
890
+	/**
891
+	 * Get Only Raw Entiy's attributes which have been modified
892
+	 * since last query
893
+	 *
894
+	 * @return array
895
+	 */
896
+	public function getDirtyRawAttributes()
897
+	{
898
+		$attributes = $this->getRawAttributes();
899
+		$cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
900
+
901
+		$dirty = [];
902
+
903
+		foreach ($attributes as $key => $value) {
904
+			if ($this->isRelation($key) || $key == 'pivot') {
905
+				continue;
906
+			}
907
+
908
+			if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
909
+				$dirty[$key] = $value;
910
+			} elseif ($value !== $cachedAttributes[$key] &&
911
+				!$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
912
+				$dirty[$key] = $value;
913
+			}
914
+		}
915
+
916
+		return $dirty;
917
+	}
918
+
919
+	/**
920
+	 * @param $key
921
+	 * @return bool
922
+	 */
923
+	protected function isRelation($key)
924
+	{
925
+		return in_array($key, $this->entityMap->getRelationships());
926
+	}
927
+
928
+	/**
929
+	 * Determine if the new and old values for a given key are numerically equivalent.
930
+	 *
931
+	 * @param $current
932
+	 * @param $original
933
+	 * @return boolean
934
+	 */
935
+	protected function originalIsNumericallyEquivalent($current, $original)
936
+	{
937
+		return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
938
+	}
939
+
940
+	/**
941
+	 * Get the underlying entity object
942
+	 *
943
+	 * @return mixed
944
+	 */
945
+	public function getEntityObject()
946
+	{
947
+		return $this->wrappedEntity->getObject();
948
+	}
949
+
950
+	/**
951
+	 * Return the Mapper instance for the current Entity Type
952
+	 *
953
+	 * @return \Analogue\ORM\System\Mapper
954
+	 */
955
+	public function getMapper()
956
+	{
957
+		return $this->mapper;
958
+	}
959
+
960
+	/**
961
+	 * Check that the entity already exists in the database, by checking
962
+	 * if it has an EntityCache record
963
+	 *
964
+	 * @return boolean
965
+	 */
966
+	public function exists()
967
+	{
968
+		return $this->getCache()->has($this->getEntityId());
969
+	}
970
+
971
+	/**
972
+	 * Set the object attribute raw values (hydration)
973
+	 *
974
+	 * @param array $attributes
975
+	 */
976
+	public function setEntityAttributes(array $attributes)
977
+	{
978
+		$this->wrappedEntity->setEntityAttributes($attributes);
979
+	}
980
+
981
+	/**
982
+	 * Get the raw object's values.
983
+	 *
984
+	 * @return array
985
+	 */
986
+	public function getEntityAttributes()
987
+	{
988
+		return $this->wrappedEntity->getEntityAttributes();
989
+	}
990
+
991
+	/**
992
+	 * Set the raw entity attributes
993
+	 * @param string $key
994
+	 * @param string $value
995
+	 */
996
+	public function setEntityAttribute($key, $value)
997
+	{
998
+		$this->wrappedEntity->setEntityAttribute($key, $value);
999
+	}
1000
+
1001
+	/**
1002
+	 * Return the entity's attribute
1003
+	 * @param  string $key
1004
+	 * @return mixed
1005
+	 */
1006
+	public function getEntityAttribute($key)
1007
+	{
1008
+		return $this->wrappedEntity->getEntityAttribute($key);
1009
+	}
1010
+
1011
+	/**
1012
+	 * Does the attribute exists on the entity
1013
+	 *
1014
+	 * @param  string  $key
1015
+	 * @return boolean
1016
+	 */
1017
+	public function hasAttribute($key)
1018
+	{
1019
+		return $this->wrappedEntity->hasAttribute($key);
1020
+	}
1021
+
1022
+	/**
1023
+	 * Set the lazyloading proxies on the wrapped entity
1024
+	 */
1025
+	public function setProxies()
1026
+	{
1027
+		$this->wrappedEntity->setProxies();
1028
+	}
1029 1029
 }
Please login to merge, or discard this patch.
src/System/Manager.php 1 patch
Indentation   +455 added lines, -455 removed lines patch added patch discarded remove patch
@@ -15,463 +15,463 @@
 block discarded – undo
15 15
  */
16 16
 class Manager
17 17
 {
18
-    /**
19
-     * Driver Manager
20
-     *
21
-     * @var \Analogue\ORM\Drivers\Manager
22
-     */
23
-    protected $drivers;
24
-
25
-    /**
26
-     * Registered entity classes and corresponding map objects.
27
-     *
28
-     * @var array
29
-     */
30
-    protected $entityClasses = [];
31
-
32
-    /**
33
-     * Key value store of ValueObject Classes and corresponding map classes
34
-     *
35
-     * @var array
36
-     */
37
-    protected $valueClasses = [];
38
-
39
-    /**
40
-     * Morph map
41
-     */
42
-    protected $morphMap = [];
43
-
44
-    /**
45
-     * Loaded Mappers
46
-     *
47
-     * @var array
48
-     */
49
-    protected $mappers = [];
50
-
51
-    /**
52
-     * Loaded Repositories
53
-     *
54
-     * @var array
55
-     */
56
-    protected $repositories = [];
57
-
58
-    /**
59
-     * Event dispatcher instance
60
-     *
61
-     * @var \Illuminate\Contracts\Events\Dispatcher
62
-     */
63
-    protected $eventDispatcher;
64
-
65
-    /**
66
-     * Manager instance
67
-     *
68
-     * @var Manager
69
-     */
70
-    protected static $instance;
71
-
72
-    /**
73
-     * Available Analogue Events
74
-     *
75
-     * @var array
76
-     */
77
-    protected $events = [
78
-        'initializing',
79
-        'initialized',
80
-        'store',
81
-        'stored',
82
-        'creating',
83
-        'created',
84
-        'updating',
85
-        'updated',
86
-        'deleting',
87
-        'deleted',
88
-    ];
89
-
90
-    /**
91
-     * @param \Analogue\ORM\Drivers\Manager $driverManager
92
-     * @param Dispatcher $event
93
-     */
94
-    public function __construct(DriverManager $driverManager, Dispatcher $event)
95
-    {
96
-        $this->drivers = $driverManager;
97
-
98
-        $this->eventDispatcher = $event;
99
-
100
-        static::$instance = $this;
101
-    }
102
-
103
-    /**
104
-     * Return the Driver Manager's instance
105
-     *
106
-     * @return \Analogue\ORM\Drivers\Manager
107
-     */
108
-    public function getDriverManager()
109
-    {
110
-        return $this->drivers;
111
-    }
112
-
113
-    /**
114
-     * Create a mapper for a given entity
115
-     *
116
-     * @param  \Analogue\ORM\Mappable|string $entity
117
-     * @param  mixed                         $entityMap
118
-     * @throws MappingException
119
-     * @return Mapper
120
-     */
121
-    public function mapper($entity, $entityMap = null)
122
-    {
123
-        if ($entity instanceof Wrapper) {
124
-            throw new MappingException('Tried to instantiate mapper on wrapped Entity');
125
-        }
126
-
127
-        if (!is_string($entity)) {
128
-            $entity = get_class($entity);
129
-        }
130
-
131
-        $entity = $this->getInverseMorphMap($entity);
132
-
133
-        // Return existing mapper instance if exists.
134
-        if (array_key_exists($entity, $this->mappers)) {
135
-            return $this->mappers[$entity];
136
-        } else {
137
-            return $this->buildMapper($entity, $entityMap);
138
-        }
139
-    }
140
-
141
-    /**
142
-     * Build a new Mapper instance for a given Entity
143
-     *
144
-     * @param  string $entity
145
-     * @param         $entityMap
146
-     * @throws MappingException
147
-     * @return Mapper
148
-     */
149
-    protected function buildMapper($entity, $entityMap)
150
-    {
151
-        // If an EntityMap hasn't been manually registered by the user
152
-        // register it at runtime.
153
-        if (!$this->isRegisteredEntity($entity)) {
154
-            $this->register($entity, $entityMap);
155
-        }
156
-
157
-        $entityMap = $this->entityClasses[$entity];
158
-
159
-        $factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
160
-
161
-        $mapper = $factory->make($entity, $entityMap);
162
-
163
-        $this->mappers[$entity] = $mapper;
164
-
165
-        // At this point we can safely call the boot() method on the entityMap as
166
-        // the mapper is now instantiated & registered within the manager.
167
-
168
-        $mapper->getEntityMap()->boot();
18
+	/**
19
+	 * Driver Manager
20
+	 *
21
+	 * @var \Analogue\ORM\Drivers\Manager
22
+	 */
23
+	protected $drivers;
24
+
25
+	/**
26
+	 * Registered entity classes and corresponding map objects.
27
+	 *
28
+	 * @var array
29
+	 */
30
+	protected $entityClasses = [];
31
+
32
+	/**
33
+	 * Key value store of ValueObject Classes and corresponding map classes
34
+	 *
35
+	 * @var array
36
+	 */
37
+	protected $valueClasses = [];
38
+
39
+	/**
40
+	 * Morph map
41
+	 */
42
+	protected $morphMap = [];
43
+
44
+	/**
45
+	 * Loaded Mappers
46
+	 *
47
+	 * @var array
48
+	 */
49
+	protected $mappers = [];
50
+
51
+	/**
52
+	 * Loaded Repositories
53
+	 *
54
+	 * @var array
55
+	 */
56
+	protected $repositories = [];
57
+
58
+	/**
59
+	 * Event dispatcher instance
60
+	 *
61
+	 * @var \Illuminate\Contracts\Events\Dispatcher
62
+	 */
63
+	protected $eventDispatcher;
64
+
65
+	/**
66
+	 * Manager instance
67
+	 *
68
+	 * @var Manager
69
+	 */
70
+	protected static $instance;
71
+
72
+	/**
73
+	 * Available Analogue Events
74
+	 *
75
+	 * @var array
76
+	 */
77
+	protected $events = [
78
+		'initializing',
79
+		'initialized',
80
+		'store',
81
+		'stored',
82
+		'creating',
83
+		'created',
84
+		'updating',
85
+		'updated',
86
+		'deleting',
87
+		'deleted',
88
+	];
89
+
90
+	/**
91
+	 * @param \Analogue\ORM\Drivers\Manager $driverManager
92
+	 * @param Dispatcher $event
93
+	 */
94
+	public function __construct(DriverManager $driverManager, Dispatcher $event)
95
+	{
96
+		$this->drivers = $driverManager;
97
+
98
+		$this->eventDispatcher = $event;
99
+
100
+		static::$instance = $this;
101
+	}
102
+
103
+	/**
104
+	 * Return the Driver Manager's instance
105
+	 *
106
+	 * @return \Analogue\ORM\Drivers\Manager
107
+	 */
108
+	public function getDriverManager()
109
+	{
110
+		return $this->drivers;
111
+	}
112
+
113
+	/**
114
+	 * Create a mapper for a given entity
115
+	 *
116
+	 * @param  \Analogue\ORM\Mappable|string $entity
117
+	 * @param  mixed                         $entityMap
118
+	 * @throws MappingException
119
+	 * @return Mapper
120
+	 */
121
+	public function mapper($entity, $entityMap = null)
122
+	{
123
+		if ($entity instanceof Wrapper) {
124
+			throw new MappingException('Tried to instantiate mapper on wrapped Entity');
125
+		}
126
+
127
+		if (!is_string($entity)) {
128
+			$entity = get_class($entity);
129
+		}
130
+
131
+		$entity = $this->getInverseMorphMap($entity);
132
+
133
+		// Return existing mapper instance if exists.
134
+		if (array_key_exists($entity, $this->mappers)) {
135
+			return $this->mappers[$entity];
136
+		} else {
137
+			return $this->buildMapper($entity, $entityMap);
138
+		}
139
+	}
140
+
141
+	/**
142
+	 * Build a new Mapper instance for a given Entity
143
+	 *
144
+	 * @param  string $entity
145
+	 * @param         $entityMap
146
+	 * @throws MappingException
147
+	 * @return Mapper
148
+	 */
149
+	protected function buildMapper($entity, $entityMap)
150
+	{
151
+		// If an EntityMap hasn't been manually registered by the user
152
+		// register it at runtime.
153
+		if (!$this->isRegisteredEntity($entity)) {
154
+			$this->register($entity, $entityMap);
155
+		}
156
+
157
+		$entityMap = $this->entityClasses[$entity];
158
+
159
+		$factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
160
+
161
+		$mapper = $factory->make($entity, $entityMap);
162
+
163
+		$this->mappers[$entity] = $mapper;
164
+
165
+		// At this point we can safely call the boot() method on the entityMap as
166
+		// the mapper is now instantiated & registered within the manager.
167
+
168
+		$mapper->getEntityMap()->boot();
169 169
         
170
-        return $mapper;
171
-    }
172
-
173
-    /**
174
-     * Create a mapper for a given entity (static alias)
175
-     *
176
-     * @param  \Analogue\ORM\Mappable|string $entity
177
-     * @param  null|EntityMap                $entityMap
178
-     * @throws MappingException
179
-     * @return Mapper
180
-     */
181
-    public static function getMapper($entity, $entityMap = null)
182
-    {
183
-        return static::$instance->mapper($entity, $entityMap);
184
-    }
185
-
186
-    /**
187
-     * Get the Repository instance for the given Entity
188
-     *
189
-     * @param  \Analogue\ORM\Mappable|string $entity
190
-     * @throws \InvalidArgumentException
191
-     * @throws MappingException
192
-     * @return \Analogue\ORM\Repository
193
-     */
194
-    public function repository($entity)
195
-    {
196
-        if (!is_string($entity)) {
197
-            $entity = get_class($entity);
198
-        }
199
-
200
-        // First we check if the repository is not already created.
201
-        if (array_key_exists($entity, $this->repositories)) {
202
-            return $this->repositories[$entity];
203
-        }
204
-
205
-        $this->repositories[$entity] = new Repository($this->mapper($entity));
170
+		return $mapper;
171
+	}
172
+
173
+	/**
174
+	 * Create a mapper for a given entity (static alias)
175
+	 *
176
+	 * @param  \Analogue\ORM\Mappable|string $entity
177
+	 * @param  null|EntityMap                $entityMap
178
+	 * @throws MappingException
179
+	 * @return Mapper
180
+	 */
181
+	public static function getMapper($entity, $entityMap = null)
182
+	{
183
+		return static::$instance->mapper($entity, $entityMap);
184
+	}
185
+
186
+	/**
187
+	 * Get the Repository instance for the given Entity
188
+	 *
189
+	 * @param  \Analogue\ORM\Mappable|string $entity
190
+	 * @throws \InvalidArgumentException
191
+	 * @throws MappingException
192
+	 * @return \Analogue\ORM\Repository
193
+	 */
194
+	public function repository($entity)
195
+	{
196
+		if (!is_string($entity)) {
197
+			$entity = get_class($entity);
198
+		}
199
+
200
+		// First we check if the repository is not already created.
201
+		if (array_key_exists($entity, $this->repositories)) {
202
+			return $this->repositories[$entity];
203
+		}
204
+
205
+		$this->repositories[$entity] = new Repository($this->mapper($entity));
206 206
         
207
-        return $this->repositories[$entity];
208
-    }
209
-
210
-    /**
211
-     * Register an entity
212
-     *
213
-     * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
214
-     * @param  string|EntityMap              $entityMap map's class name
215
-     * @throws MappingException
216
-     * @return void
217
-     */
218
-    public function register($entity, $entityMap = null)
219
-    {
220
-        // If an object is provider, get the class name from it
221
-        if (!is_string($entity)) {
222
-            $entity = get_class($entity);
223
-        }
224
-
225
-        if ($this->isRegisteredEntity($entity)) {
226
-            throw new MappingException("Entity $entity is already registered.");
227
-        }
228
-
229
-        if (!class_exists($entity)) {
230
-            throw new MappingException("Class $entity does not exists");
231
-        }
232
-
233
-        if (is_null($entityMap)) {
234
-            $entityMap = $this->getEntityMapInstanceFor($entity);
235
-        }
236
-
237
-        if (is_string($entityMap)) {
238
-            $entityMap = new $entityMap;
239
-        }
240
-
241
-        if (!$entityMap instanceof EntityMap) {
242
-            throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
243
-        }
244
-
245
-        $entityMap->setClass($entity);
246
-
247
-        $entityMap->setManager($this);
248
-
249
-        $this->entityClasses[$entity] = $entityMap;
250
-    }
251
-
252
-    /**
253
-     * Get the entity map instance for a custom entity
254
-     *
255
-     * @param  string   $entity
256
-     * @return \Analogue\ORM\Mappable
257
-     */
258
-    protected function getEntityMapInstanceFor($entity)
259
-    {
260
-        if (class_exists($entity . 'Map')) {
261
-            $map = $entity . 'Map';
262
-            $map = new $map;
263
-        } else {
264
-            // Generate an EntityMap object
265
-            $map = $this->getNewEntityMap();
266
-        }
207
+		return $this->repositories[$entity];
208
+	}
209
+
210
+	/**
211
+	 * Register an entity
212
+	 *
213
+	 * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
214
+	 * @param  string|EntityMap              $entityMap map's class name
215
+	 * @throws MappingException
216
+	 * @return void
217
+	 */
218
+	public function register($entity, $entityMap = null)
219
+	{
220
+		// If an object is provider, get the class name from it
221
+		if (!is_string($entity)) {
222
+			$entity = get_class($entity);
223
+		}
224
+
225
+		if ($this->isRegisteredEntity($entity)) {
226
+			throw new MappingException("Entity $entity is already registered.");
227
+		}
228
+
229
+		if (!class_exists($entity)) {
230
+			throw new MappingException("Class $entity does not exists");
231
+		}
232
+
233
+		if (is_null($entityMap)) {
234
+			$entityMap = $this->getEntityMapInstanceFor($entity);
235
+		}
236
+
237
+		if (is_string($entityMap)) {
238
+			$entityMap = new $entityMap;
239
+		}
240
+
241
+		if (!$entityMap instanceof EntityMap) {
242
+			throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
243
+		}
244
+
245
+		$entityMap->setClass($entity);
246
+
247
+		$entityMap->setManager($this);
248
+
249
+		$this->entityClasses[$entity] = $entityMap;
250
+	}
251
+
252
+	/**
253
+	 * Get the entity map instance for a custom entity
254
+	 *
255
+	 * @param  string   $entity
256
+	 * @return \Analogue\ORM\Mappable
257
+	 */
258
+	protected function getEntityMapInstanceFor($entity)
259
+	{
260
+		if (class_exists($entity . 'Map')) {
261
+			$map = $entity . 'Map';
262
+			$map = new $map;
263
+		} else {
264
+			// Generate an EntityMap object
265
+			$map = $this->getNewEntityMap();
266
+		}
267 267
         
268
-        return $map;
269
-    }
270
-
271
-    /**
272
-     * Dynamically create an entity map for a custom entity class
273
-     *
274
-     * @return EntityMap
275
-     */
276
-    protected function getNewEntityMap()
277
-    {
278
-        return new EntityMap;
279
-    }
280
-
281
-    /**
282
-     * Register a Value Object
283
-     *
284
-     * @param  string $valueObject
285
-     * @param  string $valueMap
286
-     * @throws MappingException
287
-     * @return void
288
-     */
289
-    public function registerValueObject($valueObject, $valueMap = null)
290
-    {
291
-        if (!is_string($valueObject)) {
292
-            $valueObject = get_class($valueObject);
293
-        }
294
-
295
-        if (is_null($valueMap)) {
296
-            $valueMap = $valueObject . 'Map';
297
-        }
298
-
299
-        if (!class_exists($valueMap)) {
300
-            throw new MappingException("$valueMap doesn't exists");
301
-        }
302
-
303
-        $this->valueClasses[$valueObject] = $valueMap;
304
-    }
305
-
306
-    /**
307
-     * Return true is the object is registered as value object
308
-     *
309
-     * @param  mixed $object
310
-     * @return boolean
311
-     */
312
-    public function isValueObject($object)
313
-    {
314
-        if (!is_string($object)) {
315
-            $object = get_class($object);
316
-        }
317
-
318
-        return array_key_exists($object, $this->valueClasses);
319
-    }
320
-
321
-    /**
322
-     * Get the Value Map for a given Value Object Class
323
-     *
324
-     * @param  string $valueObject
325
-     * @throws MappingException
326
-     * @return \Analogue\ORM\ValueMap
327
-     */
328
-    public function getValueMap($valueObject)
329
-    {
330
-        if (!is_string($valueObject)) {
331
-            $valueObject = get_class($valueObject);
332
-        }
333
-
334
-        if (!array_key_exists($valueObject, $this->valueClasses)) {
335
-            $this->registerValueObject($valueObject);
336
-        }
337
-        $valueMap = new $this->valueClasses[$valueObject];
338
-
339
-        $valueMap->setClass($valueObject);
340
-
341
-        return $valueMap;
342
-    }
343
-
344
-    /**
345
-     * Instantiate a new Value Object instance
346
-     *
347
-     * @param  string $valueObject
348
-     * @return \Analogue\ORM\ValueObject
349
-     */
350
-    public function getValueObjectInstance($valueObject)
351
-    {
352
-        $prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
353
-        return $prototype;
354
-    }
355
-
356
-    /**
357
-     * Register Analogue Plugin
358
-     *
359
-     * @param  string $plugin class
360
-     * @return void
361
-     */
362
-    public function registerPlugin($plugin)
363
-    {
364
-        $plugin = new $plugin($this);
365
-
366
-        $this->events = array_merge($this->events, $plugin->getCustomEvents());
367
-
368
-        $plugin->register();
369
-    }
370
-
371
-    /**
372
-     * Check if the entity is already registered
373
-     *
374
-     * @param  string|object $entity
375
-     * @return boolean
376
-     */
377
-    public function isRegisteredEntity($entity)
378
-    {
379
-        if (!is_string($entity)) {
380
-            $entity = get_class($entity);
381
-        }
382
-
383
-        return array_key_exists($entity, $this->entityClasses);
384
-    }
385
-
386
-    /**
387
-     * Register event listeners that will be fired regardless the type
388
-     * of the entity.
389
-     *
390
-     * @param  string $event
391
-     * @param  \Closure $callback
392
-     * @throws \Exception
393
-     * @return void
394
-     */
395
-    public function registerGlobalEvent($event, $callback)
396
-    {
397
-        if (!in_array($event, $this->events)) {
398
-            throw new \Exception("Analogue : Event $event doesn't exist");
399
-        }
400
-        $this->eventDispatcher->listen("analogue.{$event}.*", $callback);
401
-    }
402
-
403
-    /**
404
-     * Shortcut to Mapper store
405
-     *
406
-     * @param  mixed $entity
407
-     * @throws MappingException
408
-     * @return mixed
409
-     */
410
-    public function store($entity)
411
-    {
412
-        return $this->mapper($entity)->store($entity);
413
-    }
414
-
415
-    /**
416
-     * Shortcut to Mapper delete
417
-     *
418
-     * @param  mixed $entity
419
-     * @throws MappingException
420
-     * @return \Illuminate\Support\Collection|null
421
-     */
422
-    public function delete($entity)
423
-    {
424
-        return $this->mapper($entity)->delete($entity);
425
-    }
426
-
427
-    /**
428
-     * Shortcut to Mapper query
429
-     *
430
-     * @param  mixed $entity
431
-     * @throws MappingException
432
-     * @return Query
433
-     */
434
-    public function query($entity)
435
-    {
436
-        return $this->mapper($entity)->query();
437
-    }
438
-
439
-    /**
440
-     * Shortcut to Mapper Global Query
441
-     *
442
-     * @param  mixed $entity
443
-     * @throws MappingException
444
-     * @return Query
445
-     */
446
-    public function globalQuery($entity)
447
-    {
448
-        return $this->mapper($entity)->globalQuery();
449
-    }
450
-
451
-    public function morphMap(array $morphMap)
452
-    {
453
-        $this->morphMap = $morphMap;
454
-        return $this;
455
-    }
456
-
457
-    public function getMorphMap($class)
458
-    {
459
-        $key = array_search($class, $this->morphMap);
460
-        return $key !== false ? $key : $class;
461
-    }
462
-
463
-    public function getInverseMorphMap($key)
464
-    {
465
-        return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
466
-    }
268
+		return $map;
269
+	}
270
+
271
+	/**
272
+	 * Dynamically create an entity map for a custom entity class
273
+	 *
274
+	 * @return EntityMap
275
+	 */
276
+	protected function getNewEntityMap()
277
+	{
278
+		return new EntityMap;
279
+	}
280
+
281
+	/**
282
+	 * Register a Value Object
283
+	 *
284
+	 * @param  string $valueObject
285
+	 * @param  string $valueMap
286
+	 * @throws MappingException
287
+	 * @return void
288
+	 */
289
+	public function registerValueObject($valueObject, $valueMap = null)
290
+	{
291
+		if (!is_string($valueObject)) {
292
+			$valueObject = get_class($valueObject);
293
+		}
294
+
295
+		if (is_null($valueMap)) {
296
+			$valueMap = $valueObject . 'Map';
297
+		}
298
+
299
+		if (!class_exists($valueMap)) {
300
+			throw new MappingException("$valueMap doesn't exists");
301
+		}
302
+
303
+		$this->valueClasses[$valueObject] = $valueMap;
304
+	}
305
+
306
+	/**
307
+	 * Return true is the object is registered as value object
308
+	 *
309
+	 * @param  mixed $object
310
+	 * @return boolean
311
+	 */
312
+	public function isValueObject($object)
313
+	{
314
+		if (!is_string($object)) {
315
+			$object = get_class($object);
316
+		}
317
+
318
+		return array_key_exists($object, $this->valueClasses);
319
+	}
320
+
321
+	/**
322
+	 * Get the Value Map for a given Value Object Class
323
+	 *
324
+	 * @param  string $valueObject
325
+	 * @throws MappingException
326
+	 * @return \Analogue\ORM\ValueMap
327
+	 */
328
+	public function getValueMap($valueObject)
329
+	{
330
+		if (!is_string($valueObject)) {
331
+			$valueObject = get_class($valueObject);
332
+		}
333
+
334
+		if (!array_key_exists($valueObject, $this->valueClasses)) {
335
+			$this->registerValueObject($valueObject);
336
+		}
337
+		$valueMap = new $this->valueClasses[$valueObject];
338
+
339
+		$valueMap->setClass($valueObject);
340
+
341
+		return $valueMap;
342
+	}
343
+
344
+	/**
345
+	 * Instantiate a new Value Object instance
346
+	 *
347
+	 * @param  string $valueObject
348
+	 * @return \Analogue\ORM\ValueObject
349
+	 */
350
+	public function getValueObjectInstance($valueObject)
351
+	{
352
+		$prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
353
+		return $prototype;
354
+	}
355
+
356
+	/**
357
+	 * Register Analogue Plugin
358
+	 *
359
+	 * @param  string $plugin class
360
+	 * @return void
361
+	 */
362
+	public function registerPlugin($plugin)
363
+	{
364
+		$plugin = new $plugin($this);
365
+
366
+		$this->events = array_merge($this->events, $plugin->getCustomEvents());
367
+
368
+		$plugin->register();
369
+	}
370
+
371
+	/**
372
+	 * Check if the entity is already registered
373
+	 *
374
+	 * @param  string|object $entity
375
+	 * @return boolean
376
+	 */
377
+	public function isRegisteredEntity($entity)
378
+	{
379
+		if (!is_string($entity)) {
380
+			$entity = get_class($entity);
381
+		}
382
+
383
+		return array_key_exists($entity, $this->entityClasses);
384
+	}
385
+
386
+	/**
387
+	 * Register event listeners that will be fired regardless the type
388
+	 * of the entity.
389
+	 *
390
+	 * @param  string $event
391
+	 * @param  \Closure $callback
392
+	 * @throws \Exception
393
+	 * @return void
394
+	 */
395
+	public function registerGlobalEvent($event, $callback)
396
+	{
397
+		if (!in_array($event, $this->events)) {
398
+			throw new \Exception("Analogue : Event $event doesn't exist");
399
+		}
400
+		$this->eventDispatcher->listen("analogue.{$event}.*", $callback);
401
+	}
402
+
403
+	/**
404
+	 * Shortcut to Mapper store
405
+	 *
406
+	 * @param  mixed $entity
407
+	 * @throws MappingException
408
+	 * @return mixed
409
+	 */
410
+	public function store($entity)
411
+	{
412
+		return $this->mapper($entity)->store($entity);
413
+	}
414
+
415
+	/**
416
+	 * Shortcut to Mapper delete
417
+	 *
418
+	 * @param  mixed $entity
419
+	 * @throws MappingException
420
+	 * @return \Illuminate\Support\Collection|null
421
+	 */
422
+	public function delete($entity)
423
+	{
424
+		return $this->mapper($entity)->delete($entity);
425
+	}
426
+
427
+	/**
428
+	 * Shortcut to Mapper query
429
+	 *
430
+	 * @param  mixed $entity
431
+	 * @throws MappingException
432
+	 * @return Query
433
+	 */
434
+	public function query($entity)
435
+	{
436
+		return $this->mapper($entity)->query();
437
+	}
438
+
439
+	/**
440
+	 * Shortcut to Mapper Global Query
441
+	 *
442
+	 * @param  mixed $entity
443
+	 * @throws MappingException
444
+	 * @return Query
445
+	 */
446
+	public function globalQuery($entity)
447
+	{
448
+		return $this->mapper($entity)->globalQuery();
449
+	}
450
+
451
+	public function morphMap(array $morphMap)
452
+	{
453
+		$this->morphMap = $morphMap;
454
+		return $this;
455
+	}
456
+
457
+	public function getMorphMap($class)
458
+	{
459
+		$key = array_search($class, $this->morphMap);
460
+		return $key !== false ? $key : $class;
461
+	}
462
+
463
+	public function getInverseMorphMap($key)
464
+	{
465
+		return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
466
+	}
467 467
     
468
-    /**
469
-     * Return the Singleton instance of the manager
470
-     *
471
-     * @return Manager
472
-     */
473
-    public static function getInstance()
474
-    {
475
-        return static::$instance;
476
-    }
468
+	/**
469
+	 * Return the Singleton instance of the manager
470
+	 *
471
+	 * @return Manager
472
+	 */
473
+	public static function getInstance()
474
+	{
475
+		return static::$instance;
476
+	}
477 477
 }
Please login to merge, or discard this patch.
src/System/Mapper.php 2 patches
Indentation   +556 added lines, -556 removed lines patch added patch discarded remove patch
@@ -22,563 +22,563 @@
 block discarded – undo
22 22
  */
23 23
 class Mapper
24 24
 {
25
-    /**
26
-     * The Manager instance
27
-     *
28
-     * @var \Analogue\ORM\System\Manager
29
-     */
30
-    protected $manager;
31
-
32
-    /**
33
-     * Instance of EntityMapper Object
34
-     *
35
-     * @var \Analogue\ORM\EntityMap
36
-     */
37
-    protected $entityMap;
38
-
39
-    /**
40
-     * The instance of db adapter
41
-     *
42
-     * @var \Analogue\ORM\Drivers\DBAdapter
43
-     */
44
-    protected $adapter;
45
-
46
-
47
-    /**
48
-     * Event dispatcher instance
49
-     *
50
-     * @var \Illuminate\Contracts\Events\Dispatcher
51
-     */
52
-    protected $dispatcher;
53
-
54
-    /**
55
-     * Entity Cache
56
-     *
57
-     * @var  \Analogue\ORM\System\EntityCache
58
-     */
59
-    protected $cache;
60
-
61
-    /**
62
-     * Global scopes
63
-     *
64
-     * @var array
65
-     */
66
-    protected $globalScopes = [];
67
-
68
-    /**
69
-     * Custom Commands
70
-     *
71
-     * @var array
72
-     */
73
-    protected $customCommands = [];
74
-
75
-    /**
76
-     * @param EntityMap  $entityMap
77
-     * @param DBAdapter  $adapter
78
-     * @param Dispatcher $dispatcher
79
-     * @param Manager    $manager
80
-     */
81
-    public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher $dispatcher, Manager $manager)
82
-    {
83
-        $this->entityMap = $entityMap;
84
-
85
-        $this->adapter = $adapter;
86
-
87
-        $this->dispatcher = $dispatcher;
88
-
89
-        $this->manager = $manager;
90
-
91
-        $this->cache = new EntityCache($entityMap);
92
-    }
93
-
94
-    /**
95
-     * Persist an entity or an entity collection into the database
96
-     *
97
-     * @param  Mappable|Collection $entity
98
-     * @throws \InvalidArgumentException
99
-     * @throws MappingException
100
-     * @return Mappable|Collection
101
-     */
102
-    public function store($entity)
103
-    {
104
-        if ($this->isArrayOrCollection($entity)) {
105
-            return $this->storeCollection($entity);
106
-        } else {
107
-            return $this->storeEntity($entity);
108
-        }
109
-    }
110
-
111
-    /**
112
-     * Return true if an object is an array or collection
113
-     *
114
-     * @param  mixed $argument
115
-     * @return boolean
116
-     */
117
-    protected function isArrayOrCollection($argument)
118
-    {
119
-        return $argument instanceof Collection || is_array($argument);
120
-    }
121
-
122
-    /**
123
-     * Store a single entity into the database
124
-     *
125
-     * @param  Mappable $entity
126
-     * @throws \InvalidArgumentException
127
-     * @throws MappingException
128
-     * @return \Analogue\ORM\Entity
129
-     */
130
-    protected function storeEntity($entity)
131
-    {
132
-        $this->checkEntityType($entity);
133
-
134
-        $store = new Store($this->aggregate($entity), $this->newQueryBuilder());
135
-
136
-        return $store->execute();
137
-    }
138
-
139
-    /**
140
-     * Convert an entity into an aggregate root
141
-     *
142
-     * @param  mixed $entity
143
-     * @throws MappingException
144
-     * @return \Analogue\ORM\System\Aggregate
145
-     */
146
-    protected function aggregate($entity)
147
-    {
148
-        return new Aggregate($entity);
149
-    }
150
-
151
-    /**
152
-     * Store an entity collection inside a single DB Transaction
153
-     *
154
-     * @param  Collection|array $entities
155
-     * @throws \InvalidArgumentException
156
-     * @throws MappingException
157
-     * @return Collection
158
-     */
159
-    protected function storeCollection($entities)
160
-    {
161
-        $this->adapter->beginTransaction();
162
-
163
-        foreach ($entities as $entity) {
164
-            $this->storeEntity($entity);
165
-        }
166
-
167
-        $this->adapter->commit();
168
-
169
-        return $entities;
170
-    }
171
-
172
-    /**
173
-     * Delete an entity or an entity collection from the database
174
-     *
175
-     * @param  mixed|Collection
176
-     * @throws MappingException
177
-     * @throws \InvalidArgumentException
178
-     * @return Collection|void
179
-     */
180
-    public function delete($entity)
181
-    {
182
-        if ($this->isArrayOrCollection($entity)) {
183
-            return $this->deleteCollection($entity);
184
-        } else {
185
-            $this->deleteEntity($entity);
186
-        }
187
-    }
188
-
189
-    /**
190
-     * Delete a single entity from the database.
191
-     *
192
-     * @param  Mappable $entity
193
-     * @throws \InvalidArgumentException
194
-     * @throws MappingException
195
-     * @return void
196
-     */
197
-    protected function deleteEntity($entity)
198
-    {
199
-        $this->checkEntityType($entity);
200
-
201
-        $delete = new Delete($this->aggregate($entity), $this->newQueryBuilder());
202
-
203
-        $delete->execute();
204
-    }
205
-
206
-    /**
207
-     * Delete an Entity Collection inside a single db transaction
208
-     *
209
-     * @param  Collection|array $entities
210
-     * @throws \InvalidArgumentException
211
-     * @throws MappingException
212
-     * @return Collection
213
-     */
214
-    protected function deleteCollection($entities)
215
-    {
216
-        $this->adapter->beginTransaction();
217
-
218
-        foreach ($entities as $entity) {
219
-            $this->deleteEntity($entity);
220
-        }
221
-
222
-        $this->adapter->commit();
25
+	/**
26
+	 * The Manager instance
27
+	 *
28
+	 * @var \Analogue\ORM\System\Manager
29
+	 */
30
+	protected $manager;
31
+
32
+	/**
33
+	 * Instance of EntityMapper Object
34
+	 *
35
+	 * @var \Analogue\ORM\EntityMap
36
+	 */
37
+	protected $entityMap;
38
+
39
+	/**
40
+	 * The instance of db adapter
41
+	 *
42
+	 * @var \Analogue\ORM\Drivers\DBAdapter
43
+	 */
44
+	protected $adapter;
45
+
46
+
47
+	/**
48
+	 * Event dispatcher instance
49
+	 *
50
+	 * @var \Illuminate\Contracts\Events\Dispatcher
51
+	 */
52
+	protected $dispatcher;
53
+
54
+	/**
55
+	 * Entity Cache
56
+	 *
57
+	 * @var  \Analogue\ORM\System\EntityCache
58
+	 */
59
+	protected $cache;
60
+
61
+	/**
62
+	 * Global scopes
63
+	 *
64
+	 * @var array
65
+	 */
66
+	protected $globalScopes = [];
67
+
68
+	/**
69
+	 * Custom Commands
70
+	 *
71
+	 * @var array
72
+	 */
73
+	protected $customCommands = [];
74
+
75
+	/**
76
+	 * @param EntityMap  $entityMap
77
+	 * @param DBAdapter  $adapter
78
+	 * @param Dispatcher $dispatcher
79
+	 * @param Manager    $manager
80
+	 */
81
+	public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher $dispatcher, Manager $manager)
82
+	{
83
+		$this->entityMap = $entityMap;
84
+
85
+		$this->adapter = $adapter;
86
+
87
+		$this->dispatcher = $dispatcher;
88
+
89
+		$this->manager = $manager;
90
+
91
+		$this->cache = new EntityCache($entityMap);
92
+	}
93
+
94
+	/**
95
+	 * Persist an entity or an entity collection into the database
96
+	 *
97
+	 * @param  Mappable|Collection $entity
98
+	 * @throws \InvalidArgumentException
99
+	 * @throws MappingException
100
+	 * @return Mappable|Collection
101
+	 */
102
+	public function store($entity)
103
+	{
104
+		if ($this->isArrayOrCollection($entity)) {
105
+			return $this->storeCollection($entity);
106
+		} else {
107
+			return $this->storeEntity($entity);
108
+		}
109
+	}
110
+
111
+	/**
112
+	 * Return true if an object is an array or collection
113
+	 *
114
+	 * @param  mixed $argument
115
+	 * @return boolean
116
+	 */
117
+	protected function isArrayOrCollection($argument)
118
+	{
119
+		return $argument instanceof Collection || is_array($argument);
120
+	}
121
+
122
+	/**
123
+	 * Store a single entity into the database
124
+	 *
125
+	 * @param  Mappable $entity
126
+	 * @throws \InvalidArgumentException
127
+	 * @throws MappingException
128
+	 * @return \Analogue\ORM\Entity
129
+	 */
130
+	protected function storeEntity($entity)
131
+	{
132
+		$this->checkEntityType($entity);
133
+
134
+		$store = new Store($this->aggregate($entity), $this->newQueryBuilder());
135
+
136
+		return $store->execute();
137
+	}
138
+
139
+	/**
140
+	 * Convert an entity into an aggregate root
141
+	 *
142
+	 * @param  mixed $entity
143
+	 * @throws MappingException
144
+	 * @return \Analogue\ORM\System\Aggregate
145
+	 */
146
+	protected function aggregate($entity)
147
+	{
148
+		return new Aggregate($entity);
149
+	}
150
+
151
+	/**
152
+	 * Store an entity collection inside a single DB Transaction
153
+	 *
154
+	 * @param  Collection|array $entities
155
+	 * @throws \InvalidArgumentException
156
+	 * @throws MappingException
157
+	 * @return Collection
158
+	 */
159
+	protected function storeCollection($entities)
160
+	{
161
+		$this->adapter->beginTransaction();
162
+
163
+		foreach ($entities as $entity) {
164
+			$this->storeEntity($entity);
165
+		}
166
+
167
+		$this->adapter->commit();
168
+
169
+		return $entities;
170
+	}
171
+
172
+	/**
173
+	 * Delete an entity or an entity collection from the database
174
+	 *
175
+	 * @param  mixed|Collection
176
+	 * @throws MappingException
177
+	 * @throws \InvalidArgumentException
178
+	 * @return Collection|void
179
+	 */
180
+	public function delete($entity)
181
+	{
182
+		if ($this->isArrayOrCollection($entity)) {
183
+			return $this->deleteCollection($entity);
184
+		} else {
185
+			$this->deleteEntity($entity);
186
+		}
187
+	}
188
+
189
+	/**
190
+	 * Delete a single entity from the database.
191
+	 *
192
+	 * @param  Mappable $entity
193
+	 * @throws \InvalidArgumentException
194
+	 * @throws MappingException
195
+	 * @return void
196
+	 */
197
+	protected function deleteEntity($entity)
198
+	{
199
+		$this->checkEntityType($entity);
200
+
201
+		$delete = new Delete($this->aggregate($entity), $this->newQueryBuilder());
202
+
203
+		$delete->execute();
204
+	}
205
+
206
+	/**
207
+	 * Delete an Entity Collection inside a single db transaction
208
+	 *
209
+	 * @param  Collection|array $entities
210
+	 * @throws \InvalidArgumentException
211
+	 * @throws MappingException
212
+	 * @return Collection
213
+	 */
214
+	protected function deleteCollection($entities)
215
+	{
216
+		$this->adapter->beginTransaction();
217
+
218
+		foreach ($entities as $entity) {
219
+			$this->deleteEntity($entity);
220
+		}
221
+
222
+		$this->adapter->commit();
223 223
         
224
-        return $entities;
225
-    }
226
-
227
-    /**
228
-     * Return the entity map for this mapper
229
-     *
230
-     * @return EntityMap
231
-     */
232
-    public function getEntityMap()
233
-    {
234
-        return $this->entityMap;
235
-    }
236
-
237
-    /**
238
-     * Get the entity cache for the current mapper
239
-     *
240
-     * @return EntityCache  $entityCache
241
-     */
242
-    public function getEntityCache()
243
-    {
244
-        return $this->cache;
245
-    }
246
-
247
-    /**
248
-     * Fire the given event for the entity
249
-     *
250
-     * @param  string               $event
251
-     * @param  \Analogue\ORM\Entity $entity
252
-     * @param  bool                 $halt
253
-     * @throws InvalidArgumentException
254
-     * @return mixed
255
-     */
256
-    public function fireEvent($event, $entity, $halt = true)
257
-    {
258
-        if ($entity instanceof Wrapper) {
259
-            throw new InvalidArgumentException('Fired Event with invalid Entity Object');
260
-        }
261
-
262
-        $event = "analogue.{$event}." . $this->entityMap->getClass();
263
-
264
-        $method = $halt ? 'until' : 'fire';
265
-
266
-        return $this->dispatcher->$method($event, $entity);
267
-    }
268
-
269
-    /**
270
-     * Register an entity event with the dispatcher.
271
-     *
272
-     * @param  string   $event
273
-     * @param  \Closure $callback
274
-     * @return void
275
-     */
276
-    public function registerEvent($event, $callback)
277
-    {
278
-        $name = $this->entityMap->getClass();
279
-
280
-        $this->dispatcher->listen("analogue.{$event}.{$name}", $callback);
281
-    }
282
-
283
-    /**
284
-     * Add a global scope to this mapper query builder
285
-     *
286
-     * @param  ScopeInterface $scope
287
-     * @return void
288
-     */
289
-    public function addGlobalScope(ScopeInterface $scope)
290
-    {
291
-        $this->globalScopes[get_class($scope)] = $scope;
292
-    }
293
-
294
-    /**
295
-     * Determine if the mapper has a global scope.
296
-     *
297
-     * @param  \Analogue\ORM\System\ScopeInterface $scope
298
-     * @return bool
299
-     */
300
-    public function hasGlobalScope($scope)
301
-    {
302
-        return !is_null($this->getGlobalScope($scope));
303
-    }
304
-
305
-    /**
306
-     * Get a global scope registered with the modal.
307
-     *
308
-     * @param  \Analogue\ORM\System\ScopeInterface $scope
309
-     * @return \Analogue\ORM\System\ScopeInterface|null
310
-     */
311
-    public function getGlobalScope($scope)
312
-    {
313
-        return array_first($this->globalScopes, function ($key, $value) use ($scope) {
314
-            return $scope instanceof $value;
315
-        });
316
-    }
317
-
318
-    /**
319
-     * Get the global scopes for this class instance.
320
-     *
321
-     * @return \Analogue\ORM\System\ScopeInterface
322
-     */
323
-    public function getGlobalScopes()
324
-    {
325
-        return $this->globalScopes;
326
-    }
327
-
328
-    /**
329
-     * Apply all of the global scopes to an Analogue Query builder.
330
-     *
331
-     * @param Query $query
332
-     * @return \Analogue\ORM\System\Query
333
-     */
334
-    public function applyGlobalScopes($query)
335
-    {
336
-        foreach ($this->getGlobalScopes() as $scope) {
337
-            $scope->apply($query, $this);
338
-        }
339
-
340
-        return $query;
341
-    }
342
-
343
-    /**
344
-     * Remove all of the global scopes from an Analogue Query builder.
345
-     *
346
-     * @param Query $query
347
-     * @return \Analogue\ORM\System\Query
348
-     */
349
-    public function removeGlobalScopes($query)
350
-    {
351
-        foreach ($this->getGlobalScopes() as $scope) {
352
-            $scope->remove($query, $this);
353
-        }
354
-
355
-        return $query;
356
-    }
357
-
358
-    /**
359
-     * Get a new query instance without a given scope.
360
-     *
361
-     * @param  \Analogue\ORM\System\ScopeInterface $scope
362
-     * @return \Analogue\ORM\System\Query
363
-     */
364
-    public function newQueryWithoutScope($scope)
365
-    {
366
-        $this->getGlobalScope($scope)->remove($query = $this->getQuery(), $this);
367
-
368
-        return $query;
369
-    }
370
-
371
-    /**
372
-     * Get a new query builder that doesn't have any global scopes.
373
-     *
374
-     * @return Query
375
-     */
376
-    public function newQueryWithoutScopes()
377
-    {
378
-        return $this->removeGlobalScopes($this->getQuery());
379
-    }
380
-
381
-    /**
382
-     * Add a dynamic method that extends the mapper/repository
383
-     *
384
-     * @param string $command
385
-     */
386
-    public function addCustomCommand($command)
387
-    {
388
-        $name = lcfirst(class_basename($command));
389
-
390
-        $this->customCommands[$name] = $command;
391
-    }
392
-
393
-    /**
394
-     * Execute a custom command on an Entity
395
-     *
396
-     * @param  string                 $command
397
-     * @param  mixed|Collection|array $entity
398
-     * @throws \InvalidArgumentException
399
-     * @throws MappingException
400
-     * @return mixed
401
-     */
402
-    public function executeCustomCommand($command, $entity)
403
-    {
404
-        $commandClass = $this->customCommands[$command];
405
-
406
-        if ($this->isArrayOrCollection($entity)) {
407
-            foreach ($entity as $instance) {
408
-                $this->executeSingleCustomCommand($commandClass, $instance);
409
-            }
410
-        } else {
411
-            return $this->executeSingleCustomCommand($commandClass, $entity);
412
-        }
413
-    }
414
-
415
-    /**
416
-     * Execute a single command instance
417
-     *
418
-     * @param  string $commandClass
419
-     * @param  mixed  $entity
420
-     * @throws \InvalidArgumentException
421
-     * @throws MappingException
422
-     * @return mixed
423
-     */
424
-    protected function executeSingleCustomCommand($commandClass, $entity)
425
-    {
426
-        $this->checkEntityType($entity);
427
-
428
-        $instance = new $commandClass($this->aggregate($entity), $this->newQueryBuilder());
429
-
430
-        return $instance->execute();
431
-    }
432
-
433
-    /**
434
-     * Check that the entity correspond to the current mapper.
435
-     *
436
-     * @param  mixed $entity
437
-     * @throws InvalidArgumentException
438
-     * @return void
439
-     */
440
-    protected function checkEntityType($entity)
441
-    {
442
-        if (get_class($entity) != $this->entityMap->getClass()) {
443
-            $expected = $this->entityMap->getClass();
444
-            $actual = get_class($entity);
445
-            throw new InvalidArgumentException("Expected : $expected, got $actual.");
446
-        }
447
-    }
448
-
449
-    /**
450
-     * Get all the custom commands registered on this mapper
451
-     *
452
-     * @return array
453
-     */
454
-    public function getCustomCommands()
455
-    {
456
-        return array_keys($this->customCommands);
457
-    }
458
-
459
-    /**
460
-     * Check if this mapper supports this command
461
-     * @param  string $command
462
-     * @return boolean
463
-     */
464
-    public function hasCustomCommand($command)
465
-    {
466
-        return in_array($command, $this->getCustomCommands());
467
-    }
468
-
469
-    /**
470
-     * Create a new instance of the mapped entity class
471
-     *
472
-     * @param  array $attributes
473
-     * @return mixed
474
-     */
475
-    public function newInstance($attributes = [])
476
-    {
477
-        $class = $this->entityMap->getClass();
478
-
479
-        if ($this->entityMap->activator() != null) {
480
-            $entity = $this->entityMap->activator();
481
-        } else {
482
-            $entity = $this->customClassInstance($class);
483
-        }
484
-
485
-        // prevent hydrating with an empty array
486
-        if (count($attributes) > 0) {
487
-            $entity->setEntityAttributes($attributes);
488
-        }
489
-
490
-        return $entity;
491
-    }
492
-
493
-    /**
494
-     * Use a trick to generate a class prototype that we
495
-     * can instantiate without calling the constructor.
496
-     *
497
-     * @param string|null $className
498
-     * @throws MappingException
499
-     * @return mixed
500
-     */
501
-    protected function customClassInstance($className)
502
-    {
503
-        if (!class_exists($className)) {
504
-            throw new MappingException("Tried to instantiate a non-existing Entity class : $className");
505
-        }
506
-
507
-        $prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
508
-        return $prototype;
509
-    }
224
+		return $entities;
225
+	}
226
+
227
+	/**
228
+	 * Return the entity map for this mapper
229
+	 *
230
+	 * @return EntityMap
231
+	 */
232
+	public function getEntityMap()
233
+	{
234
+		return $this->entityMap;
235
+	}
236
+
237
+	/**
238
+	 * Get the entity cache for the current mapper
239
+	 *
240
+	 * @return EntityCache  $entityCache
241
+	 */
242
+	public function getEntityCache()
243
+	{
244
+		return $this->cache;
245
+	}
246
+
247
+	/**
248
+	 * Fire the given event for the entity
249
+	 *
250
+	 * @param  string               $event
251
+	 * @param  \Analogue\ORM\Entity $entity
252
+	 * @param  bool                 $halt
253
+	 * @throws InvalidArgumentException
254
+	 * @return mixed
255
+	 */
256
+	public function fireEvent($event, $entity, $halt = true)
257
+	{
258
+		if ($entity instanceof Wrapper) {
259
+			throw new InvalidArgumentException('Fired Event with invalid Entity Object');
260
+		}
261
+
262
+		$event = "analogue.{$event}." . $this->entityMap->getClass();
263
+
264
+		$method = $halt ? 'until' : 'fire';
265
+
266
+		return $this->dispatcher->$method($event, $entity);
267
+	}
268
+
269
+	/**
270
+	 * Register an entity event with the dispatcher.
271
+	 *
272
+	 * @param  string   $event
273
+	 * @param  \Closure $callback
274
+	 * @return void
275
+	 */
276
+	public function registerEvent($event, $callback)
277
+	{
278
+		$name = $this->entityMap->getClass();
279
+
280
+		$this->dispatcher->listen("analogue.{$event}.{$name}", $callback);
281
+	}
282
+
283
+	/**
284
+	 * Add a global scope to this mapper query builder
285
+	 *
286
+	 * @param  ScopeInterface $scope
287
+	 * @return void
288
+	 */
289
+	public function addGlobalScope(ScopeInterface $scope)
290
+	{
291
+		$this->globalScopes[get_class($scope)] = $scope;
292
+	}
293
+
294
+	/**
295
+	 * Determine if the mapper has a global scope.
296
+	 *
297
+	 * @param  \Analogue\ORM\System\ScopeInterface $scope
298
+	 * @return bool
299
+	 */
300
+	public function hasGlobalScope($scope)
301
+	{
302
+		return !is_null($this->getGlobalScope($scope));
303
+	}
304
+
305
+	/**
306
+	 * Get a global scope registered with the modal.
307
+	 *
308
+	 * @param  \Analogue\ORM\System\ScopeInterface $scope
309
+	 * @return \Analogue\ORM\System\ScopeInterface|null
310
+	 */
311
+	public function getGlobalScope($scope)
312
+	{
313
+		return array_first($this->globalScopes, function ($key, $value) use ($scope) {
314
+			return $scope instanceof $value;
315
+		});
316
+	}
317
+
318
+	/**
319
+	 * Get the global scopes for this class instance.
320
+	 *
321
+	 * @return \Analogue\ORM\System\ScopeInterface
322
+	 */
323
+	public function getGlobalScopes()
324
+	{
325
+		return $this->globalScopes;
326
+	}
327
+
328
+	/**
329
+	 * Apply all of the global scopes to an Analogue Query builder.
330
+	 *
331
+	 * @param Query $query
332
+	 * @return \Analogue\ORM\System\Query
333
+	 */
334
+	public function applyGlobalScopes($query)
335
+	{
336
+		foreach ($this->getGlobalScopes() as $scope) {
337
+			$scope->apply($query, $this);
338
+		}
339
+
340
+		return $query;
341
+	}
342
+
343
+	/**
344
+	 * Remove all of the global scopes from an Analogue Query builder.
345
+	 *
346
+	 * @param Query $query
347
+	 * @return \Analogue\ORM\System\Query
348
+	 */
349
+	public function removeGlobalScopes($query)
350
+	{
351
+		foreach ($this->getGlobalScopes() as $scope) {
352
+			$scope->remove($query, $this);
353
+		}
354
+
355
+		return $query;
356
+	}
357
+
358
+	/**
359
+	 * Get a new query instance without a given scope.
360
+	 *
361
+	 * @param  \Analogue\ORM\System\ScopeInterface $scope
362
+	 * @return \Analogue\ORM\System\Query
363
+	 */
364
+	public function newQueryWithoutScope($scope)
365
+	{
366
+		$this->getGlobalScope($scope)->remove($query = $this->getQuery(), $this);
367
+
368
+		return $query;
369
+	}
370
+
371
+	/**
372
+	 * Get a new query builder that doesn't have any global scopes.
373
+	 *
374
+	 * @return Query
375
+	 */
376
+	public function newQueryWithoutScopes()
377
+	{
378
+		return $this->removeGlobalScopes($this->getQuery());
379
+	}
380
+
381
+	/**
382
+	 * Add a dynamic method that extends the mapper/repository
383
+	 *
384
+	 * @param string $command
385
+	 */
386
+	public function addCustomCommand($command)
387
+	{
388
+		$name = lcfirst(class_basename($command));
389
+
390
+		$this->customCommands[$name] = $command;
391
+	}
392
+
393
+	/**
394
+	 * Execute a custom command on an Entity
395
+	 *
396
+	 * @param  string                 $command
397
+	 * @param  mixed|Collection|array $entity
398
+	 * @throws \InvalidArgumentException
399
+	 * @throws MappingException
400
+	 * @return mixed
401
+	 */
402
+	public function executeCustomCommand($command, $entity)
403
+	{
404
+		$commandClass = $this->customCommands[$command];
405
+
406
+		if ($this->isArrayOrCollection($entity)) {
407
+			foreach ($entity as $instance) {
408
+				$this->executeSingleCustomCommand($commandClass, $instance);
409
+			}
410
+		} else {
411
+			return $this->executeSingleCustomCommand($commandClass, $entity);
412
+		}
413
+	}
414
+
415
+	/**
416
+	 * Execute a single command instance
417
+	 *
418
+	 * @param  string $commandClass
419
+	 * @param  mixed  $entity
420
+	 * @throws \InvalidArgumentException
421
+	 * @throws MappingException
422
+	 * @return mixed
423
+	 */
424
+	protected function executeSingleCustomCommand($commandClass, $entity)
425
+	{
426
+		$this->checkEntityType($entity);
427
+
428
+		$instance = new $commandClass($this->aggregate($entity), $this->newQueryBuilder());
429
+
430
+		return $instance->execute();
431
+	}
432
+
433
+	/**
434
+	 * Check that the entity correspond to the current mapper.
435
+	 *
436
+	 * @param  mixed $entity
437
+	 * @throws InvalidArgumentException
438
+	 * @return void
439
+	 */
440
+	protected function checkEntityType($entity)
441
+	{
442
+		if (get_class($entity) != $this->entityMap->getClass()) {
443
+			$expected = $this->entityMap->getClass();
444
+			$actual = get_class($entity);
445
+			throw new InvalidArgumentException("Expected : $expected, got $actual.");
446
+		}
447
+	}
448
+
449
+	/**
450
+	 * Get all the custom commands registered on this mapper
451
+	 *
452
+	 * @return array
453
+	 */
454
+	public function getCustomCommands()
455
+	{
456
+		return array_keys($this->customCommands);
457
+	}
458
+
459
+	/**
460
+	 * Check if this mapper supports this command
461
+	 * @param  string $command
462
+	 * @return boolean
463
+	 */
464
+	public function hasCustomCommand($command)
465
+	{
466
+		return in_array($command, $this->getCustomCommands());
467
+	}
468
+
469
+	/**
470
+	 * Create a new instance of the mapped entity class
471
+	 *
472
+	 * @param  array $attributes
473
+	 * @return mixed
474
+	 */
475
+	public function newInstance($attributes = [])
476
+	{
477
+		$class = $this->entityMap->getClass();
478
+
479
+		if ($this->entityMap->activator() != null) {
480
+			$entity = $this->entityMap->activator();
481
+		} else {
482
+			$entity = $this->customClassInstance($class);
483
+		}
484
+
485
+		// prevent hydrating with an empty array
486
+		if (count($attributes) > 0) {
487
+			$entity->setEntityAttributes($attributes);
488
+		}
489
+
490
+		return $entity;
491
+	}
492
+
493
+	/**
494
+	 * Use a trick to generate a class prototype that we
495
+	 * can instantiate without calling the constructor.
496
+	 *
497
+	 * @param string|null $className
498
+	 * @throws MappingException
499
+	 * @return mixed
500
+	 */
501
+	protected function customClassInstance($className)
502
+	{
503
+		if (!class_exists($className)) {
504
+			throw new MappingException("Tried to instantiate a non-existing Entity class : $className");
505
+		}
506
+
507
+		$prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
508
+		return $prototype;
509
+	}
510 510
     
511
-    /**
512
-     * Get the Analogue Query Builder for this instance
513
-     *
514
-     * @return \Analogue\ORM\System\Query
515
-     */
516
-    public function getQuery()
517
-    {
518
-        $query = new Query($this, $this->adapter);
519
-
520
-        return $this->applyGlobalScopes($query);
521
-    }
511
+	/**
512
+	 * Get the Analogue Query Builder for this instance
513
+	 *
514
+	 * @return \Analogue\ORM\System\Query
515
+	 */
516
+	public function getQuery()
517
+	{
518
+		$query = new Query($this, $this->adapter);
519
+
520
+		return $this->applyGlobalScopes($query);
521
+	}
522 522
     
523
-    /**
524
-     * Get the Analogue Query Builder for this instance
525
-     *
526
-     * @return \Analogue\ORM\System\Query
527
-     */
528
-    public function query()
529
-    {
530
-        return $this->getQuery();
531
-    }
532
-
533
-    /**
534
-     * Get an unscoped Analogue Query Builder for this instance
535
-     *
536
-     * @return \Analogue\ORM\System\Query
537
-     */
538
-    public function globalQuery()
539
-    {
540
-        return $this->newQueryWithoutScopes();
541
-    }
542
-
543
-    /**
544
-     * Get a the Underlying QueryAdapter.
545
-     *
546
-     * @return \Analogue\ORM\Drivers\QueryAdapter
547
-     */
548
-    public function newQueryBuilder()
549
-    {
550
-        return $this->adapter->getQuery();
551
-    }
552
-
553
-    /**
554
-     * Return the manager instance
555
-     *
556
-     * @return \Analogue\ORM\System\Manager
557
-     */
558
-    public function getManager()
559
-    {
560
-        return $this->manager;
561
-    }
562
-
563
-    /**
564
-     * Dynamically handle calls to custom commands, or Redirects to query()
565
-     *
566
-     * @param  string $method
567
-     * @param  array  $parameters
568
-     * @throws \Exception
569
-     * @return mixed
570
-     */
571
-    public function __call($method, $parameters)
572
-    {
573
-        // Check if method is a custom command on the mapper
574
-        if ($this->hasCustomCommand($method)) {
575
-            if (count($parameters) == 0) {
576
-                throw new \Exception("$method must at least have 1 argument");
577
-            }
578
-            return $this->executeCustomCommand($method, $parameters[0]);
579
-        }
580
-
581
-        // Redirect call on a new query instance
582
-        return call_user_func_array([$this->query(), $method], $parameters);
583
-    }
523
+	/**
524
+	 * Get the Analogue Query Builder for this instance
525
+	 *
526
+	 * @return \Analogue\ORM\System\Query
527
+	 */
528
+	public function query()
529
+	{
530
+		return $this->getQuery();
531
+	}
532
+
533
+	/**
534
+	 * Get an unscoped Analogue Query Builder for this instance
535
+	 *
536
+	 * @return \Analogue\ORM\System\Query
537
+	 */
538
+	public function globalQuery()
539
+	{
540
+		return $this->newQueryWithoutScopes();
541
+	}
542
+
543
+	/**
544
+	 * Get a the Underlying QueryAdapter.
545
+	 *
546
+	 * @return \Analogue\ORM\Drivers\QueryAdapter
547
+	 */
548
+	public function newQueryBuilder()
549
+	{
550
+		return $this->adapter->getQuery();
551
+	}
552
+
553
+	/**
554
+	 * Return the manager instance
555
+	 *
556
+	 * @return \Analogue\ORM\System\Manager
557
+	 */
558
+	public function getManager()
559
+	{
560
+		return $this->manager;
561
+	}
562
+
563
+	/**
564
+	 * Dynamically handle calls to custom commands, or Redirects to query()
565
+	 *
566
+	 * @param  string $method
567
+	 * @param  array  $parameters
568
+	 * @throws \Exception
569
+	 * @return mixed
570
+	 */
571
+	public function __call($method, $parameters)
572
+	{
573
+		// Check if method is a custom command on the mapper
574
+		if ($this->hasCustomCommand($method)) {
575
+			if (count($parameters) == 0) {
576
+				throw new \Exception("$method must at least have 1 argument");
577
+			}
578
+			return $this->executeCustomCommand($method, $parameters[0]);
579
+		}
580
+
581
+		// Redirect call on a new query instance
582
+		return call_user_func_array([$this->query(), $method], $parameters);
583
+	}
584 584
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
             throw new InvalidArgumentException('Fired Event with invalid Entity Object');
260 260
         }
261 261
 
262
-        $event = "analogue.{$event}." . $this->entityMap->getClass();
262
+        $event = "analogue.{$event}.".$this->entityMap->getClass();
263 263
 
264 264
         $method = $halt ? 'until' : 'fire';
265 265
 
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
      */
311 311
     public function getGlobalScope($scope)
312 312
     {
313
-        return array_first($this->globalScopes, function ($key, $value) use ($scope) {
313
+        return array_first($this->globalScopes, function($key, $value) use ($scope) {
314 314
             return $scope instanceof $value;
315 315
         });
316 316
     }
Please login to merge, or discard this patch.
src/System/Query.php 2 patches
Indentation   +837 added lines, -837 removed lines patch added patch discarded remove patch
@@ -20,846 +20,846 @@
 block discarded – undo
20 20
  */
21 21
 class Query
22 22
 {
23
-    /**
24
-     * Mapper Instance
25
-     *
26
-     * @var \Analogue\ORM\System\Mapper
27
-     */
28
-    protected $mapper;
29
-
30
-    /**
31
-     * DB Adatper
32
-     *
33
-     * @var \Analogue\ORM\Drivers\DBAdapter
34
-     */
35
-    protected $adapter;
36
-
37
-    /**
38
-     * Query Builder Instance
39
-     *
40
-     * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
41
-     */
42
-    protected $query;
43
-
44
-    /**
45
-     * Entity Map Instance
46
-     *
47
-     * @var \Analogue\ORM\EntityMap
48
-     */
49
-    protected $entityMap;
23
+	/**
24
+	 * Mapper Instance
25
+	 *
26
+	 * @var \Analogue\ORM\System\Mapper
27
+	 */
28
+	protected $mapper;
29
+
30
+	/**
31
+	 * DB Adatper
32
+	 *
33
+	 * @var \Analogue\ORM\Drivers\DBAdapter
34
+	 */
35
+	protected $adapter;
36
+
37
+	/**
38
+	 * Query Builder Instance
39
+	 *
40
+	 * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
41
+	 */
42
+	protected $query;
43
+
44
+	/**
45
+	 * Entity Map Instance
46
+	 *
47
+	 * @var \Analogue\ORM\EntityMap
48
+	 */
49
+	protected $entityMap;
50 50
     
51
-    /**
52
-     * The relationships that should be eager loaded.
53
-     *
54
-     * @var array
55
-     */
56
-    protected $eagerLoad = [];
57
-
58
-    /**
59
-     * All of the registered builder macros.
60
-     *
61
-     * @var array
62
-     */
63
-    protected $macros = [];
64
-
65
-    /**
66
-     * The methods that should be returned from query builder.
67
-     *
68
-     * @var array
69
-     */
70
-    protected $passthru = [
71
-        'toSql',
72
-        'lists',
73
-        'pluck',
74
-        'count',
75
-        'min',
76
-        'max',
77
-        'avg',
78
-        'sum',
79
-        'exists',
80
-        'getBindings',
81
-    ];
82
-
83
-    /**
84
-     * Query Builder Blacklist
85
-     */
86
-    protected $blacklist = [
87
-        'insert',
88
-        'insertGetId',
89
-        'lock',
90
-        'lockForUpdate',
91
-        'sharedLock',
92
-        'update',
93
-        'increment',
94
-        'decrement',
95
-        'delete',
96
-        'truncate',
97
-        'raw',
98
-    ];
99
-
100
-    /**
101
-     * Create a new Analogue Query Builder instance.
102
-     *
103
-     * @param  Mapper    $mapper
104
-     * @param  DBAdapter $adapter
105
-     */
106
-    public function __construct(Mapper $mapper, DBAdapter $adapter)
107
-    {
108
-        $this->mapper = $mapper;
109
-
110
-        $this->adapter = $adapter;
111
-
112
-        $this->entityMap = $mapper->getEntityMap();
113
-
114
-        // Specify the table to work on
115
-        $this->query = $adapter->getQuery()->from($this->entityMap->getTable());
116
-
117
-        $this->with($this->entityMap->getEagerloadedRelationships());
118
-    }
119
-
120
-    /**
121
-     * Run the query and return the result
122
-     *
123
-     * @param  array $columns
124
-     * @return \Analogue\ORM\EntityCollection
125
-     */
126
-    public function get($columns = ['*'])
127
-    {
128
-        $entities = $this->getEntities($columns);
129
-
130
-        // If we actually found models we will also eager load any relationships that
131
-        // have been specified as needing to be eager loaded, which will solve the
132
-        // n+1 query issue for the developers to avoid running a lot of queries.
133
-
134
-        if (count($entities) > 0) {
135
-            $entities = $this->eagerLoadRelations($entities);
136
-        }
137
-
138
-        return $this->entityMap->newCollection($entities);
139
-    }
140
-
141
-    /**
142
-     * Find an entity by its primary key
143
-     *
144
-     * @param  string|integer $id
145
-     * @param  array          $columns
146
-     * @return \Analogue\ORM\Mappable
147
-     */
148
-    public function find($id, $columns = ['*'])
149
-    {
150
-        if (is_array($id)) {
151
-            return $this->findMany($id, $columns);
152
-        }
153
-
154
-        $this->query->where($this->entityMap->getQualifiedKeyName(), '=', $id);
155
-
156
-        return $this->first($columns);
157
-    }
158
-
159
-    /**
160
-     * Find many entities by their primary keys.
161
-     *
162
-     * @param  array $id
163
-     * @param  array $columns
164
-     * @return EntityCollection
165
-     */
166
-    public function findMany($id, $columns = ['*'])
167
-    {
168
-        if (empty($id)) {
169
-            return new EntityCollection;
170
-        }
171
-
172
-        $this->query->whereIn($this->entityMap->getQualifiedKeyName(), $id);
173
-
174
-        return $this->get($columns);
175
-    }
176
-
177
-    /**
178
-     * Find a model by its primary key or throw an exception.
179
-     *
180
-     * @param  mixed $id
181
-     * @param  array $columns
182
-     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
183
-     * @return mixed|self
184
-     */
185
-    public function findOrFail($id, $columns = ['*'])
186
-    {
187
-        if (!is_null($entity = $this->find($id, $columns))) {
188
-            return $entity;
189
-        }
190
-
191
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
192
-    }
193
-
194
-
195
-    /**
196
-     * Execute the query and get the first result.
197
-     *
198
-     * @param  array $columns
199
-     * @return \Analogue\ORM\Entity
200
-     */
201
-    public function first($columns = ['*'])
202
-    {
203
-        return $this->take(1)->get($columns)->first();
204
-    }
205
-
206
-    /**
207
-     * Execute the query and get the first result or throw an exception.
208
-     *
209
-     * @param  array $columns
210
-     * @throws EntityNotFoundException
211
-     * @return \Analogue\ORM\Entity
212
-     */
213
-    public function firstOrFail($columns = ['*'])
214
-    {
215
-        if (!is_null($entity = $this->first($columns))) {
216
-            return $entity;
217
-        }
218
-
219
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
220
-    }
221
-
222
-    /**
223
-     * Pluck a single column from the database.
224
-     *
225
-     * @param  string $column
226
-     * @return mixed
227
-     */
228
-    public function pluck($column)
229
-    {
230
-        $result = $this->first([$column]);
231
-
232
-        if ($result) {
233
-            return $result->{$column};
234
-        }
235
-    }
236
-
237
-    /**
238
-     * Chunk the results of the query.
239
-     *
240
-     * @param  int      $count
241
-     * @param  callable $callback
242
-     * @return void
243
-     */
244
-    public function chunk($count, callable $callback)
245
-    {
246
-        $results = $this->forPage($page = 1, $count)->get();
247
-
248
-        while (count($results) > 0) {
249
-            // On each chunk result set, we will pass them to the callback and then let the
250
-            // developer take care of everything within the callback, which allows us to
251
-            // keep the memory low for spinning through large result sets for working.
252
-            call_user_func($callback, $results);
253
-
254
-            $page++;
255
-
256
-            $results = $this->forPage($page, $count)->get();
257
-        }
258
-    }
51
+	/**
52
+	 * The relationships that should be eager loaded.
53
+	 *
54
+	 * @var array
55
+	 */
56
+	protected $eagerLoad = [];
57
+
58
+	/**
59
+	 * All of the registered builder macros.
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $macros = [];
64
+
65
+	/**
66
+	 * The methods that should be returned from query builder.
67
+	 *
68
+	 * @var array
69
+	 */
70
+	protected $passthru = [
71
+		'toSql',
72
+		'lists',
73
+		'pluck',
74
+		'count',
75
+		'min',
76
+		'max',
77
+		'avg',
78
+		'sum',
79
+		'exists',
80
+		'getBindings',
81
+	];
82
+
83
+	/**
84
+	 * Query Builder Blacklist
85
+	 */
86
+	protected $blacklist = [
87
+		'insert',
88
+		'insertGetId',
89
+		'lock',
90
+		'lockForUpdate',
91
+		'sharedLock',
92
+		'update',
93
+		'increment',
94
+		'decrement',
95
+		'delete',
96
+		'truncate',
97
+		'raw',
98
+	];
99
+
100
+	/**
101
+	 * Create a new Analogue Query Builder instance.
102
+	 *
103
+	 * @param  Mapper    $mapper
104
+	 * @param  DBAdapter $adapter
105
+	 */
106
+	public function __construct(Mapper $mapper, DBAdapter $adapter)
107
+	{
108
+		$this->mapper = $mapper;
109
+
110
+		$this->adapter = $adapter;
111
+
112
+		$this->entityMap = $mapper->getEntityMap();
113
+
114
+		// Specify the table to work on
115
+		$this->query = $adapter->getQuery()->from($this->entityMap->getTable());
116
+
117
+		$this->with($this->entityMap->getEagerloadedRelationships());
118
+	}
119
+
120
+	/**
121
+	 * Run the query and return the result
122
+	 *
123
+	 * @param  array $columns
124
+	 * @return \Analogue\ORM\EntityCollection
125
+	 */
126
+	public function get($columns = ['*'])
127
+	{
128
+		$entities = $this->getEntities($columns);
129
+
130
+		// If we actually found models we will also eager load any relationships that
131
+		// have been specified as needing to be eager loaded, which will solve the
132
+		// n+1 query issue for the developers to avoid running a lot of queries.
133
+
134
+		if (count($entities) > 0) {
135
+			$entities = $this->eagerLoadRelations($entities);
136
+		}
137
+
138
+		return $this->entityMap->newCollection($entities);
139
+	}
140
+
141
+	/**
142
+	 * Find an entity by its primary key
143
+	 *
144
+	 * @param  string|integer $id
145
+	 * @param  array          $columns
146
+	 * @return \Analogue\ORM\Mappable
147
+	 */
148
+	public function find($id, $columns = ['*'])
149
+	{
150
+		if (is_array($id)) {
151
+			return $this->findMany($id, $columns);
152
+		}
153
+
154
+		$this->query->where($this->entityMap->getQualifiedKeyName(), '=', $id);
155
+
156
+		return $this->first($columns);
157
+	}
158
+
159
+	/**
160
+	 * Find many entities by their primary keys.
161
+	 *
162
+	 * @param  array $id
163
+	 * @param  array $columns
164
+	 * @return EntityCollection
165
+	 */
166
+	public function findMany($id, $columns = ['*'])
167
+	{
168
+		if (empty($id)) {
169
+			return new EntityCollection;
170
+		}
171
+
172
+		$this->query->whereIn($this->entityMap->getQualifiedKeyName(), $id);
173
+
174
+		return $this->get($columns);
175
+	}
176
+
177
+	/**
178
+	 * Find a model by its primary key or throw an exception.
179
+	 *
180
+	 * @param  mixed $id
181
+	 * @param  array $columns
182
+	 * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
183
+	 * @return mixed|self
184
+	 */
185
+	public function findOrFail($id, $columns = ['*'])
186
+	{
187
+		if (!is_null($entity = $this->find($id, $columns))) {
188
+			return $entity;
189
+		}
190
+
191
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
192
+	}
193
+
194
+
195
+	/**
196
+	 * Execute the query and get the first result.
197
+	 *
198
+	 * @param  array $columns
199
+	 * @return \Analogue\ORM\Entity
200
+	 */
201
+	public function first($columns = ['*'])
202
+	{
203
+		return $this->take(1)->get($columns)->first();
204
+	}
205
+
206
+	/**
207
+	 * Execute the query and get the first result or throw an exception.
208
+	 *
209
+	 * @param  array $columns
210
+	 * @throws EntityNotFoundException
211
+	 * @return \Analogue\ORM\Entity
212
+	 */
213
+	public function firstOrFail($columns = ['*'])
214
+	{
215
+		if (!is_null($entity = $this->first($columns))) {
216
+			return $entity;
217
+		}
218
+
219
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
220
+	}
221
+
222
+	/**
223
+	 * Pluck a single column from the database.
224
+	 *
225
+	 * @param  string $column
226
+	 * @return mixed
227
+	 */
228
+	public function pluck($column)
229
+	{
230
+		$result = $this->first([$column]);
231
+
232
+		if ($result) {
233
+			return $result->{$column};
234
+		}
235
+	}
236
+
237
+	/**
238
+	 * Chunk the results of the query.
239
+	 *
240
+	 * @param  int      $count
241
+	 * @param  callable $callback
242
+	 * @return void
243
+	 */
244
+	public function chunk($count, callable $callback)
245
+	{
246
+		$results = $this->forPage($page = 1, $count)->get();
247
+
248
+		while (count($results) > 0) {
249
+			// On each chunk result set, we will pass them to the callback and then let the
250
+			// developer take care of everything within the callback, which allows us to
251
+			// keep the memory low for spinning through large result sets for working.
252
+			call_user_func($callback, $results);
253
+
254
+			$page++;
255
+
256
+			$results = $this->forPage($page, $count)->get();
257
+		}
258
+	}
259 259
     
260
-    /**
261
-     * Get an array with the values of a given column.
262
-     *
263
-     * @param  string $column
264
-     * @param  string $key
265
-     * @return array
266
-     */
267
-    public function lists($column, $key = null)
268
-    {
269
-        return $this->query->lists($column, $key);
270
-    }
271
-
272
-    /**
273
-     * Get a paginator for the "select" statement.
274
-     *
275
-     * @param  int   $perPage
276
-     * @param  array $columns
277
-     * @return LengthAwarePaginator
278
-     */
279
-    public function paginate($perPage = null, $columns = ['*'])
280
-    {
281
-        $total = $this->query->getCountForPagination();
282
-
283
-        $this->query->forPage(
284
-            $page = Paginator::resolveCurrentPage(),
285
-            $perPage = $perPage ?: $this->entityMap->getPerPage()
286
-        );
287
-
288
-        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
289
-            'path' => Paginator::resolveCurrentPath()
290
-        ]);
291
-    }
292
-
293
-    /**
294
-     * Get a paginator for a grouped statement.
295
-     *
296
-     * @param  \Illuminate\Pagination\Factory $paginator
297
-     * @param  int                            $perPage
298
-     * @param  array                          $columns
299
-     * @return \Illuminate\Pagination\Paginator
300
-     */
301
-    protected function groupedPaginate($paginator, $perPage, $columns)
302
-    {
303
-        $results = $this->get($columns)->all();
304
-
305
-        return $this->query->buildRawPaginator($paginator, $results, $perPage);
306
-    }
307
-
308
-    /**
309
-     * Get a paginator for an ungrouped statement.
310
-     *
311
-     * @param  \Illuminate\Pagination\Factory $paginator
312
-     * @param  int                            $perPage
313
-     * @param  array                          $columns
314
-     * @return \Illuminate\Pagination\Paginator
315
-     */
316
-    protected function ungroupedPaginate($paginator, $perPage, $columns)
317
-    {
318
-        $total = $this->query->getPaginationCount();
319
-
320
-        // Once we have the paginator we need to set the limit and offset values for
321
-        // the query so we can get the properly paginated items. Once we have an
322
-        // array of items we can create the paginator instances for the items.
323
-        $page = $paginator->getCurrentPage($total);
324
-
325
-        $this->query->forPage($page, $perPage);
326
-
327
-        return $paginator->make($this->get($columns)->all(), $total, $perPage);
328
-    }
329
-
330
-    /**
331
-     * Paginate the given query into a simple paginator.
332
-     *
333
-     * @param  int   $perPage
334
-     * @param  array $columns
335
-     * @return \Illuminate\Contracts\Pagination\Paginator
336
-     */
337
-    public function simplePaginate($perPage = null, $columns = ['*'])
338
-    {
339
-        $page = Paginator::resolveCurrentPage();
340
-
341
-        $perPage = $perPage ?: $this->entityMap->getPerPage();
342
-
343
-        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
344
-
345
-        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
346
-    }
347
-
348
-    /**
349
-     * Add a basic where clause to the query.
350
-     *
351
-     * @param  string $column
352
-     * @param  string $operator
353
-     * @param  mixed  $value
354
-     * @param  string $boolean
355
-     * @return $this
356
-     */
357
-    public function where($column, $operator = null, $value = null, $boolean = 'and')
358
-    {
359
-        if ($column instanceof Closure) {
360
-            $query = $this->newQueryWithoutScopes();
361
-
362
-            call_user_func($column, $query);
363
-
364
-            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
365
-        } else {
366
-            call_user_func_array([$this->query, 'where'], func_get_args());
367
-        }
368
-
369
-        return $this;
370
-    }
371
-
372
-    /**
373
-     * Add an "or where" clause to the query.
374
-     *
375
-     * @param  string $column
376
-     * @param  string $operator
377
-     * @param  mixed  $value
378
-     * @return \Analogue\ORM\System\Query
379
-     */
380
-    public function orWhere($column, $operator = null, $value = null)
381
-    {
382
-        return $this->where($column, $operator, $value, 'or');
383
-    }
384
-
385
-    /**
386
-     * Add a relationship count condition to the query.
387
-     *
388
-     * @param  string   $relation
389
-     * @param  string   $operator
390
-     * @param  int      $count
391
-     * @param  string   $boolean
392
-     * @param  \Closure $callback
393
-     * @return \Analogue\ORM\System\Query
394
-     */
395
-    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
396
-    {
397
-        $entity = $this->mapper->newInstance();
398
-
399
-        $relation = $this->getHasRelationQuery($relation, $entity);
400
-
401
-        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
402
-
403
-        if ($callback) {
404
-            call_user_func($callback, $query);
405
-        }
406
-
407
-        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
408
-    }
409
-
410
-    /**
411
-     * Add a relationship count condition to the query with where clauses.
412
-     *
413
-     * @param  string   $relation
414
-     * @param  \Closure $callback
415
-     * @param  string   $operator
416
-     * @param  int      $count
417
-     * @return \Analogue\ORM\System\Query
418
-     */
419
-    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
420
-    {
421
-        return $this->has($relation, $operator, $count, 'and', $callback);
422
-    }
423
-
424
-    /**
425
-     * Add a relationship count condition to the query with an "or".
426
-     *
427
-     * @param  string $relation
428
-     * @param  string $operator
429
-     * @param  int    $count
430
-     * @return \Analogue\ORM\System\Query
431
-     */
432
-    public function orHas($relation, $operator = '>=', $count = 1)
433
-    {
434
-        return $this->has($relation, $operator, $count, 'or');
435
-    }
436
-
437
-    /**
438
-     * Add a relationship count condition to the query with where clauses and an "or".
439
-     *
440
-     * @param  string   $relation
441
-     * @param  \Closure $callback
442
-     * @param  string   $operator
443
-     * @param  int      $count
444
-     * @return \Analogue\ORM\System\Query
445
-     */
446
-    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
447
-    {
448
-        return $this->has($relation, $operator, $count, 'or', $callback);
449
-    }
450
-
451
-    /**
452
-     * Add the "has" condition where clause to the query.
453
-     *
454
-     * @param  \Analogue\ORM\System\Query               $hasQuery
455
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
456
-     * @param  string                                   $operator
457
-     * @param  int                                      $count
458
-     * @param  string                                   $boolean
459
-     * @return \Analogue\ORM\System\Query
460
-     */
461
-    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
462
-    {
463
-        $this->mergeWheresToHas($hasQuery, $relation);
464
-
465
-        if (is_numeric($count)) {
466
-            $count = new Expression($count);
467
-        }
468
-
469
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
470
-    }
471
-
472
-    /**
473
-     * Merge the "wheres" from a relation query to a has query.
474
-     *
475
-     * @param  \Analogue\ORM\System\Query               $hasQuery
476
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
477
-     * @return void
478
-     */
479
-    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
480
-    {
481
-        // Here we have the "has" query and the original relation. We need to copy over any
482
-        // where clauses the developer may have put in the relationship function over to
483
-        // the has query, and then copy the bindings from the "has" query to the main.
484
-        $relationQuery = $relation->getBaseQuery();
485
-
486
-        $hasQuery->mergeWheres(
487
-            $relationQuery->wheres, $relationQuery->getBindings()
488
-        );
489
-
490
-        $this->query->mergeBindings($hasQuery->getQuery());
491
-    }
492
-
493
-    /**
494
-     * Get the "has relation" base query instance.
495
-     *
496
-     * @param  string $relation
497
-     * @param         $entity
498
-     * @return \Analogue\ORM\System\Query
499
-     */
500
-    protected function getHasRelationQuery($relation, $entity)
501
-    {
502
-        return Relationship::noConstraints(function () use ($relation, $entity) {
503
-            return $this->entityMap->$relation($entity);
504
-        });
505
-    }
506
-
507
-    /**
508
-     * Get the table for the current query object
509
-     *
510
-     * @return string
511
-     */
512
-    public function getTable()
513
-    {
514
-        return $this->entityMap->getTable();
515
-    }
516
-
517
-    /**
518
-     * Set the relationships that should be eager loaded.
519
-     *
520
-     * @param  mixed $relations
521
-     * @return $this
522
-     */
523
-    public function with($relations)
524
-    {
525
-        if (is_string($relations)) {
526
-            $relations = func_get_args();
527
-        }
528
-
529
-        $eagers = $this->parseRelations($relations);
530
-
531
-        $this->eagerLoad = array_merge($this->eagerLoad, $eagers);
532
-
533
-        return $this;
534
-    }
535
-
536
-    /**
537
-     * Parse a list of relations into individuals.
538
-     *
539
-     * @param  array $relations
540
-     * @return array
541
-     */
542
-    protected function parseRelations(array $relations)
543
-    {
544
-        $results = [];
545
-
546
-        foreach ($relations as $name => $constraints) {
547
-            // If the "relation" value is actually a numeric key, we can assume that no
548
-            // constraints have been specified for the eager load and we'll just put
549
-            // an empty Closure with the loader so that we can treat all the same.
550
-            if (is_numeric($name)) {
551
-                $f = function () {};
552
-
553
-                list($name, $constraints) = [$constraints, $f];
554
-            }
555
-
556
-            // We need to separate out any nested includes. Which allows the developers
557
-            // to load deep relationships using "dots" without stating each level of
558
-            // the relationship with its own key in the array of eager load names.
559
-            $results = $this->parseNested($name, $results);
560
-
561
-            $results[$name] = $constraints;
562
-        }
563
-
564
-        return $results;
565
-    }
566
-
567
-
568
-    /**
569
-     * Parse the nested relationships in a relation.
570
-     *
571
-     * @param  string $name
572
-     * @param  array  $results
573
-     * @return array
574
-     */
575
-    protected function parseNested($name, $results)
576
-    {
577
-        $progress = [];
578
-
579
-        // If the relation has already been set on the result array, we will not set it
580
-        // again, since that would override any constraints that were already placed
581
-        // on the relationships. We will only set the ones that are not specified.
582
-        foreach (explode('.', $name) as $segment) {
583
-            $progress[] = $segment;
584
-
585
-            if (!isset($results[$last = implode('.', $progress)])) {
586
-                $results[$last] = function () {};
587
-            }
588
-        }
589
-
590
-        return $results;
591
-    }
592
-
593
-    /**
594
-     * Get the relationships being eagerly loaded.
595
-     *
596
-     * @return array
597
-     */
598
-    public function getEagerLoads()
599
-    {
600
-        return $this->eagerLoad;
601
-    }
602
-
603
-    /**
604
-     * Set the relationships being eagerly loaded.
605
-     *
606
-     * @param  array $eagerLoad
607
-     * @return void
608
-     */
609
-    public function setEagerLoads(array $eagerLoad)
610
-    {
611
-        $this->eagerLoad = $eagerLoad;
612
-    }
613
-
614
-    /**
615
-     * Eager load the relationships for the entities.
616
-     *
617
-     * @param  array $entities
618
-     * @return array
619
-     */
620
-    public function eagerLoadRelations($entities)
621
-    {
622
-        foreach ($this->eagerLoad as $name => $constraints) {
623
-            // For nested eager loads we'll skip loading them here and they will be set as an
624
-            // eager load on the query to retrieve the relation so that they will be eager
625
-            // loaded on that query, because that is where they get hydrated as models.
626
-            if (strpos($name, '.') === false) {
627
-                $entities = $this->loadRelation($entities, $name, $constraints);
628
-            }
629
-        }
630
-
631
-        return $entities;
632
-    }
633
-
634
-    /**
635
-     * Eagerly load the relationship on a set of entities.
636
-     *
637
-     * @param  array    $entities
638
-     * @param  string   $name
639
-     * @param  \Closure $constraints
640
-     * @return array
641
-     */
642
-    protected function loadRelation(array $entities, $name, Closure $constraints)
643
-    {
644
-        // First we will "back up" the existing where conditions on the query so we can
645
-        // add our eager constraints. Then we will merge the wheres that were on the
646
-        // query back to it in order that any where conditions might be specified.
647
-        $relation = $this->getRelation($name);
648
-
649
-        $relation->addEagerConstraints($entities);
650
-
651
-        call_user_func($constraints, $relation);
652
-
653
-        $entities = $relation->initRelation($entities, $name);
654
-
655
-        // Once we have the results, we just match those back up to their parent models
656
-        // using the relationship instance. Then we just return the finished arrays
657
-        // of models which have been eagerly hydrated and are readied for return.
658
-
659
-        $results = $relation->getEager();
660
-
661
-        return $relation->match($entities, $results, $name);
662
-    }
663
-
664
-    /**
665
-     * Get the relation instance for the given relation name.
666
-     *
667
-     * @param  string $relation
668
-     * @return \Analogue\ORM\Relationships\Relationship
669
-     */
670
-    public function getRelation($relation)
671
-    {
672
-        // We want to run a relationship query without any constrains so that we will
673
-        // not have to remove these where clauses manually which gets really hacky
674
-        // and is error prone while we remove the developer's own where clauses.
675
-        $query = Relationship::noConstraints(function () use ($relation) {
676
-            return $this->entityMap->$relation($this->getEntityInstance());
677
-        });
678
-
679
-        $nested = $this->nestedRelations($relation);
680
-
681
-        // If there are nested relationships set on the query, we will put those onto
682
-        // the query instances so that they can be handled after this relationship
683
-        // is loaded. In this way they will all trickle down as they are loaded.
684
-        if (count($nested) > 0) {
685
-            $query->getQuery()->with($nested);
686
-        }
687
-
688
-        return $query;
689
-    }
690
-
691
-    /**
692
-     * Get the deeply nested relations for a given top-level relation.
693
-     *
694
-     * @param  string $relation
695
-     * @return array
696
-     */
697
-    protected function nestedRelations($relation)
698
-    {
699
-        $nested = [];
700
-
701
-        // We are basically looking for any relationships that are nested deeper than
702
-        // the given top-level relationship. We will just check for any relations
703
-        // that start with the given top relations and adds them to our arrays.
704
-        foreach ($this->eagerLoad as $name => $constraints) {
705
-            if ($this->isNested($name, $relation)) {
706
-                $nested[substr($name, strlen($relation . '.'))] = $constraints;
707
-            }
708
-        }
709
-
710
-        return $nested;
711
-    }
712
-
713
-    /**
714
-     * Determine if the relationship is nested.
715
-     *
716
-     * @param  string $name
717
-     * @param  string $relation
718
-     * @return bool
719
-     */
720
-    protected function isNested($name, $relation)
721
-    {
722
-        $dots = str_contains($name, '.');
723
-
724
-        return $dots && starts_with($name, $relation . '.');
725
-    }
726
-
727
-    /**
728
-     * Add the Entity primary key if not in requested columns
729
-     *
730
-     * @param  array $columns
731
-     * @return array
732
-     */
733
-    protected function enforceIdColumn($columns)
734
-    {
735
-        if (!in_array($this->entityMap->getKeyName(), $columns)) {
736
-            $columns[] = $this->entityMap->getKeyName();
737
-        }
738
-        return $columns;
739
-    }
740
-
741
-    /**
742
-     * Get the hydrated models without eager loading.
743
-     *
744
-     * @param  array  $columns
745
-     * @return \Analogue\ORM\EntityCollection
746
-     */
747
-    public function getEntities($columns = ['*'])
748
-    {
749
-        // As we need the primary key to feed the
750
-        // entity cache, we need it loaded on each
751
-        // request
752
-        $columns = $this->enforceIdColumn($columns);
753
-
754
-        // Run the query
755
-        $results = $this->query->get($columns);
756
-
757
-        $builder = new EntityBuilder($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
-     * @param  array  $attributes
766
-     * @return \Analogue\ORM\Entity
767
-     */
768
-    public function getEntityInstance(array $attributes = [])
769
-    {
770
-        return $this->mapper->newInstance($attributes);
771
-    }
772
-
773
-    /**
774
-     * Extend the builder with a given callback.
775
-     *
776
-     * @param  string   $name
777
-     * @param  \Closure $callback
778
-     * @return void
779
-     */
780
-    public function macro($name, Closure $callback)
781
-    {
782
-        $this->macros[$name] = $callback;
783
-    }
784
-
785
-    /**
786
-     * Get the given macro by name.
787
-     *
788
-     * @param  string $name
789
-     * @return \Closure
790
-     */
791
-    public function getMacro($name)
792
-    {
793
-        return array_get($this->macros, $name);
794
-    }
795
-
796
-    /**
797
-     * Get a new query builder for the model's table.
798
-     *
799
-     * @return \Analogue\ORM\System\Query
800
-     */
801
-    public function newQuery()
802
-    {
803
-        $builder = new Query($this->mapper, $this->adapter);
804
-
805
-        return $this->applyGlobalScopes($builder);
806
-    }
807
-
808
-    /**
809
-     * Get a new query builder without any scope applied.
810
-     *
811
-     * @return \Analogue\ORM\System\Query
812
-     */
813
-    public function newQueryWithoutScopes()
814
-    {
815
-        return new Query($this->mapper, $this->adapter);
816
-    }
817
-
818
-    /**
819
-     * Get the Mapper instance for this Query Builder
820
-     *
821
-     * @return \Analogue\ORM\System\Mapper
822
-     */
823
-    public function getMapper()
824
-    {
825
-        return $this->mapper;
826
-    }
827
-
828
-    /**
829
-     * Get the underlying query adapter
830
-     *
831
-     * (REFACTOR: this method should move out, we need to provide the client classes
832
-     * with the adapter instead.)
833
-     *
834
-     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
835
-     */
836
-    public function getQuery()
837
-    {
838
-        return $this->query;
839
-    }
840
-
841
-    /**
842
-     * Dynamically handle calls into the query instance.
843
-     *
844
-     * @param  string $method
845
-     * @param  array  $parameters
846
-     * @throws Exception
847
-     * @return mixed
848
-     */
849
-    public function __call($method, $parameters)
850
-    {
851
-        if (isset($this->macros[$method])) {
852
-            array_unshift($parameters, $this);
853
-
854
-            return call_user_func_array($this->macros[$method], $parameters);
855
-        }
260
+	/**
261
+	 * Get an array with the values of a given column.
262
+	 *
263
+	 * @param  string $column
264
+	 * @param  string $key
265
+	 * @return array
266
+	 */
267
+	public function lists($column, $key = null)
268
+	{
269
+		return $this->query->lists($column, $key);
270
+	}
271
+
272
+	/**
273
+	 * Get a paginator for the "select" statement.
274
+	 *
275
+	 * @param  int   $perPage
276
+	 * @param  array $columns
277
+	 * @return LengthAwarePaginator
278
+	 */
279
+	public function paginate($perPage = null, $columns = ['*'])
280
+	{
281
+		$total = $this->query->getCountForPagination();
282
+
283
+		$this->query->forPage(
284
+			$page = Paginator::resolveCurrentPage(),
285
+			$perPage = $perPage ?: $this->entityMap->getPerPage()
286
+		);
287
+
288
+		return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
289
+			'path' => Paginator::resolveCurrentPath()
290
+		]);
291
+	}
292
+
293
+	/**
294
+	 * Get a paginator for a grouped statement.
295
+	 *
296
+	 * @param  \Illuminate\Pagination\Factory $paginator
297
+	 * @param  int                            $perPage
298
+	 * @param  array                          $columns
299
+	 * @return \Illuminate\Pagination\Paginator
300
+	 */
301
+	protected function groupedPaginate($paginator, $perPage, $columns)
302
+	{
303
+		$results = $this->get($columns)->all();
304
+
305
+		return $this->query->buildRawPaginator($paginator, $results, $perPage);
306
+	}
307
+
308
+	/**
309
+	 * Get a paginator for an ungrouped statement.
310
+	 *
311
+	 * @param  \Illuminate\Pagination\Factory $paginator
312
+	 * @param  int                            $perPage
313
+	 * @param  array                          $columns
314
+	 * @return \Illuminate\Pagination\Paginator
315
+	 */
316
+	protected function ungroupedPaginate($paginator, $perPage, $columns)
317
+	{
318
+		$total = $this->query->getPaginationCount();
319
+
320
+		// Once we have the paginator we need to set the limit and offset values for
321
+		// the query so we can get the properly paginated items. Once we have an
322
+		// array of items we can create the paginator instances for the items.
323
+		$page = $paginator->getCurrentPage($total);
324
+
325
+		$this->query->forPage($page, $perPage);
326
+
327
+		return $paginator->make($this->get($columns)->all(), $total, $perPage);
328
+	}
329
+
330
+	/**
331
+	 * Paginate the given query into a simple paginator.
332
+	 *
333
+	 * @param  int   $perPage
334
+	 * @param  array $columns
335
+	 * @return \Illuminate\Contracts\Pagination\Paginator
336
+	 */
337
+	public function simplePaginate($perPage = null, $columns = ['*'])
338
+	{
339
+		$page = Paginator::resolveCurrentPage();
340
+
341
+		$perPage = $perPage ?: $this->entityMap->getPerPage();
342
+
343
+		$this->skip(($page - 1) * $perPage)->take($perPage + 1);
344
+
345
+		return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
346
+	}
347
+
348
+	/**
349
+	 * Add a basic where clause to the query.
350
+	 *
351
+	 * @param  string $column
352
+	 * @param  string $operator
353
+	 * @param  mixed  $value
354
+	 * @param  string $boolean
355
+	 * @return $this
356
+	 */
357
+	public function where($column, $operator = null, $value = null, $boolean = 'and')
358
+	{
359
+		if ($column instanceof Closure) {
360
+			$query = $this->newQueryWithoutScopes();
361
+
362
+			call_user_func($column, $query);
363
+
364
+			$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
365
+		} else {
366
+			call_user_func_array([$this->query, 'where'], func_get_args());
367
+		}
368
+
369
+		return $this;
370
+	}
371
+
372
+	/**
373
+	 * Add an "or where" clause to the query.
374
+	 *
375
+	 * @param  string $column
376
+	 * @param  string $operator
377
+	 * @param  mixed  $value
378
+	 * @return \Analogue\ORM\System\Query
379
+	 */
380
+	public function orWhere($column, $operator = null, $value = null)
381
+	{
382
+		return $this->where($column, $operator, $value, 'or');
383
+	}
384
+
385
+	/**
386
+	 * Add a relationship count condition to the query.
387
+	 *
388
+	 * @param  string   $relation
389
+	 * @param  string   $operator
390
+	 * @param  int      $count
391
+	 * @param  string   $boolean
392
+	 * @param  \Closure $callback
393
+	 * @return \Analogue\ORM\System\Query
394
+	 */
395
+	public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
396
+	{
397
+		$entity = $this->mapper->newInstance();
398
+
399
+		$relation = $this->getHasRelationQuery($relation, $entity);
400
+
401
+		$query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
402
+
403
+		if ($callback) {
404
+			call_user_func($callback, $query);
405
+		}
406
+
407
+		return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
408
+	}
409
+
410
+	/**
411
+	 * Add a relationship count condition to the query with where clauses.
412
+	 *
413
+	 * @param  string   $relation
414
+	 * @param  \Closure $callback
415
+	 * @param  string   $operator
416
+	 * @param  int      $count
417
+	 * @return \Analogue\ORM\System\Query
418
+	 */
419
+	public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
420
+	{
421
+		return $this->has($relation, $operator, $count, 'and', $callback);
422
+	}
423
+
424
+	/**
425
+	 * Add a relationship count condition to the query with an "or".
426
+	 *
427
+	 * @param  string $relation
428
+	 * @param  string $operator
429
+	 * @param  int    $count
430
+	 * @return \Analogue\ORM\System\Query
431
+	 */
432
+	public function orHas($relation, $operator = '>=', $count = 1)
433
+	{
434
+		return $this->has($relation, $operator, $count, 'or');
435
+	}
436
+
437
+	/**
438
+	 * Add a relationship count condition to the query with where clauses and an "or".
439
+	 *
440
+	 * @param  string   $relation
441
+	 * @param  \Closure $callback
442
+	 * @param  string   $operator
443
+	 * @param  int      $count
444
+	 * @return \Analogue\ORM\System\Query
445
+	 */
446
+	public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
447
+	{
448
+		return $this->has($relation, $operator, $count, 'or', $callback);
449
+	}
450
+
451
+	/**
452
+	 * Add the "has" condition where clause to the query.
453
+	 *
454
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
455
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
456
+	 * @param  string                                   $operator
457
+	 * @param  int                                      $count
458
+	 * @param  string                                   $boolean
459
+	 * @return \Analogue\ORM\System\Query
460
+	 */
461
+	protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
462
+	{
463
+		$this->mergeWheresToHas($hasQuery, $relation);
464
+
465
+		if (is_numeric($count)) {
466
+			$count = new Expression($count);
467
+		}
468
+
469
+		return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
470
+	}
471
+
472
+	/**
473
+	 * Merge the "wheres" from a relation query to a has query.
474
+	 *
475
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
476
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
477
+	 * @return void
478
+	 */
479
+	protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
480
+	{
481
+		// Here we have the "has" query and the original relation. We need to copy over any
482
+		// where clauses the developer may have put in the relationship function over to
483
+		// the has query, and then copy the bindings from the "has" query to the main.
484
+		$relationQuery = $relation->getBaseQuery();
485
+
486
+		$hasQuery->mergeWheres(
487
+			$relationQuery->wheres, $relationQuery->getBindings()
488
+		);
489
+
490
+		$this->query->mergeBindings($hasQuery->getQuery());
491
+	}
492
+
493
+	/**
494
+	 * Get the "has relation" base query instance.
495
+	 *
496
+	 * @param  string $relation
497
+	 * @param         $entity
498
+	 * @return \Analogue\ORM\System\Query
499
+	 */
500
+	protected function getHasRelationQuery($relation, $entity)
501
+	{
502
+		return Relationship::noConstraints(function () use ($relation, $entity) {
503
+			return $this->entityMap->$relation($entity);
504
+		});
505
+	}
506
+
507
+	/**
508
+	 * Get the table for the current query object
509
+	 *
510
+	 * @return string
511
+	 */
512
+	public function getTable()
513
+	{
514
+		return $this->entityMap->getTable();
515
+	}
516
+
517
+	/**
518
+	 * Set the relationships that should be eager loaded.
519
+	 *
520
+	 * @param  mixed $relations
521
+	 * @return $this
522
+	 */
523
+	public function with($relations)
524
+	{
525
+		if (is_string($relations)) {
526
+			$relations = func_get_args();
527
+		}
528
+
529
+		$eagers = $this->parseRelations($relations);
530
+
531
+		$this->eagerLoad = array_merge($this->eagerLoad, $eagers);
532
+
533
+		return $this;
534
+	}
535
+
536
+	/**
537
+	 * Parse a list of relations into individuals.
538
+	 *
539
+	 * @param  array $relations
540
+	 * @return array
541
+	 */
542
+	protected function parseRelations(array $relations)
543
+	{
544
+		$results = [];
545
+
546
+		foreach ($relations as $name => $constraints) {
547
+			// If the "relation" value is actually a numeric key, we can assume that no
548
+			// constraints have been specified for the eager load and we'll just put
549
+			// an empty Closure with the loader so that we can treat all the same.
550
+			if (is_numeric($name)) {
551
+				$f = function () {};
552
+
553
+				list($name, $constraints) = [$constraints, $f];
554
+			}
555
+
556
+			// We need to separate out any nested includes. Which allows the developers
557
+			// to load deep relationships using "dots" without stating each level of
558
+			// the relationship with its own key in the array of eager load names.
559
+			$results = $this->parseNested($name, $results);
560
+
561
+			$results[$name] = $constraints;
562
+		}
563
+
564
+		return $results;
565
+	}
566
+
567
+
568
+	/**
569
+	 * Parse the nested relationships in a relation.
570
+	 *
571
+	 * @param  string $name
572
+	 * @param  array  $results
573
+	 * @return array
574
+	 */
575
+	protected function parseNested($name, $results)
576
+	{
577
+		$progress = [];
578
+
579
+		// If the relation has already been set on the result array, we will not set it
580
+		// again, since that would override any constraints that were already placed
581
+		// on the relationships. We will only set the ones that are not specified.
582
+		foreach (explode('.', $name) as $segment) {
583
+			$progress[] = $segment;
584
+
585
+			if (!isset($results[$last = implode('.', $progress)])) {
586
+				$results[$last] = function () {};
587
+			}
588
+		}
589
+
590
+		return $results;
591
+	}
592
+
593
+	/**
594
+	 * Get the relationships being eagerly loaded.
595
+	 *
596
+	 * @return array
597
+	 */
598
+	public function getEagerLoads()
599
+	{
600
+		return $this->eagerLoad;
601
+	}
602
+
603
+	/**
604
+	 * Set the relationships being eagerly loaded.
605
+	 *
606
+	 * @param  array $eagerLoad
607
+	 * @return void
608
+	 */
609
+	public function setEagerLoads(array $eagerLoad)
610
+	{
611
+		$this->eagerLoad = $eagerLoad;
612
+	}
613
+
614
+	/**
615
+	 * Eager load the relationships for the entities.
616
+	 *
617
+	 * @param  array $entities
618
+	 * @return array
619
+	 */
620
+	public function eagerLoadRelations($entities)
621
+	{
622
+		foreach ($this->eagerLoad as $name => $constraints) {
623
+			// For nested eager loads we'll skip loading them here and they will be set as an
624
+			// eager load on the query to retrieve the relation so that they will be eager
625
+			// loaded on that query, because that is where they get hydrated as models.
626
+			if (strpos($name, '.') === false) {
627
+				$entities = $this->loadRelation($entities, $name, $constraints);
628
+			}
629
+		}
630
+
631
+		return $entities;
632
+	}
633
+
634
+	/**
635
+	 * Eagerly load the relationship on a set of entities.
636
+	 *
637
+	 * @param  array    $entities
638
+	 * @param  string   $name
639
+	 * @param  \Closure $constraints
640
+	 * @return array
641
+	 */
642
+	protected function loadRelation(array $entities, $name, Closure $constraints)
643
+	{
644
+		// First we will "back up" the existing where conditions on the query so we can
645
+		// add our eager constraints. Then we will merge the wheres that were on the
646
+		// query back to it in order that any where conditions might be specified.
647
+		$relation = $this->getRelation($name);
648
+
649
+		$relation->addEagerConstraints($entities);
650
+
651
+		call_user_func($constraints, $relation);
652
+
653
+		$entities = $relation->initRelation($entities, $name);
654
+
655
+		// Once we have the results, we just match those back up to their parent models
656
+		// using the relationship instance. Then we just return the finished arrays
657
+		// of models which have been eagerly hydrated and are readied for return.
658
+
659
+		$results = $relation->getEager();
660
+
661
+		return $relation->match($entities, $results, $name);
662
+	}
663
+
664
+	/**
665
+	 * Get the relation instance for the given relation name.
666
+	 *
667
+	 * @param  string $relation
668
+	 * @return \Analogue\ORM\Relationships\Relationship
669
+	 */
670
+	public function getRelation($relation)
671
+	{
672
+		// We want to run a relationship query without any constrains so that we will
673
+		// not have to remove these where clauses manually which gets really hacky
674
+		// and is error prone while we remove the developer's own where clauses.
675
+		$query = Relationship::noConstraints(function () use ($relation) {
676
+			return $this->entityMap->$relation($this->getEntityInstance());
677
+		});
678
+
679
+		$nested = $this->nestedRelations($relation);
680
+
681
+		// If there are nested relationships set on the query, we will put those onto
682
+		// the query instances so that they can be handled after this relationship
683
+		// is loaded. In this way they will all trickle down as they are loaded.
684
+		if (count($nested) > 0) {
685
+			$query->getQuery()->with($nested);
686
+		}
687
+
688
+		return $query;
689
+	}
690
+
691
+	/**
692
+	 * Get the deeply nested relations for a given top-level relation.
693
+	 *
694
+	 * @param  string $relation
695
+	 * @return array
696
+	 */
697
+	protected function nestedRelations($relation)
698
+	{
699
+		$nested = [];
700
+
701
+		// We are basically looking for any relationships that are nested deeper than
702
+		// the given top-level relationship. We will just check for any relations
703
+		// that start with the given top relations and adds them to our arrays.
704
+		foreach ($this->eagerLoad as $name => $constraints) {
705
+			if ($this->isNested($name, $relation)) {
706
+				$nested[substr($name, strlen($relation . '.'))] = $constraints;
707
+			}
708
+		}
709
+
710
+		return $nested;
711
+	}
712
+
713
+	/**
714
+	 * Determine if the relationship is nested.
715
+	 *
716
+	 * @param  string $name
717
+	 * @param  string $relation
718
+	 * @return bool
719
+	 */
720
+	protected function isNested($name, $relation)
721
+	{
722
+		$dots = str_contains($name, '.');
723
+
724
+		return $dots && starts_with($name, $relation . '.');
725
+	}
726
+
727
+	/**
728
+	 * Add the Entity primary key if not in requested columns
729
+	 *
730
+	 * @param  array $columns
731
+	 * @return array
732
+	 */
733
+	protected function enforceIdColumn($columns)
734
+	{
735
+		if (!in_array($this->entityMap->getKeyName(), $columns)) {
736
+			$columns[] = $this->entityMap->getKeyName();
737
+		}
738
+		return $columns;
739
+	}
740
+
741
+	/**
742
+	 * Get the hydrated models without eager loading.
743
+	 *
744
+	 * @param  array  $columns
745
+	 * @return \Analogue\ORM\EntityCollection
746
+	 */
747
+	public function getEntities($columns = ['*'])
748
+	{
749
+		// As we need the primary key to feed the
750
+		// entity cache, we need it loaded on each
751
+		// request
752
+		$columns = $this->enforceIdColumn($columns);
753
+
754
+		// Run the query
755
+		$results = $this->query->get($columns);
756
+
757
+		$builder = new EntityBuilder($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
+	 * @param  array  $attributes
766
+	 * @return \Analogue\ORM\Entity
767
+	 */
768
+	public function getEntityInstance(array $attributes = [])
769
+	{
770
+		return $this->mapper->newInstance($attributes);
771
+	}
772
+
773
+	/**
774
+	 * Extend the builder with a given callback.
775
+	 *
776
+	 * @param  string   $name
777
+	 * @param  \Closure $callback
778
+	 * @return void
779
+	 */
780
+	public function macro($name, Closure $callback)
781
+	{
782
+		$this->macros[$name] = $callback;
783
+	}
784
+
785
+	/**
786
+	 * Get the given macro by name.
787
+	 *
788
+	 * @param  string $name
789
+	 * @return \Closure
790
+	 */
791
+	public function getMacro($name)
792
+	{
793
+		return array_get($this->macros, $name);
794
+	}
795
+
796
+	/**
797
+	 * Get a new query builder for the model's table.
798
+	 *
799
+	 * @return \Analogue\ORM\System\Query
800
+	 */
801
+	public function newQuery()
802
+	{
803
+		$builder = new Query($this->mapper, $this->adapter);
804
+
805
+		return $this->applyGlobalScopes($builder);
806
+	}
807
+
808
+	/**
809
+	 * Get a new query builder without any scope applied.
810
+	 *
811
+	 * @return \Analogue\ORM\System\Query
812
+	 */
813
+	public function newQueryWithoutScopes()
814
+	{
815
+		return new Query($this->mapper, $this->adapter);
816
+	}
817
+
818
+	/**
819
+	 * Get the Mapper instance for this Query Builder
820
+	 *
821
+	 * @return \Analogue\ORM\System\Mapper
822
+	 */
823
+	public function getMapper()
824
+	{
825
+		return $this->mapper;
826
+	}
827
+
828
+	/**
829
+	 * Get the underlying query adapter
830
+	 *
831
+	 * (REFACTOR: this method should move out, we need to provide the client classes
832
+	 * with the adapter instead.)
833
+	 *
834
+	 * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
835
+	 */
836
+	public function getQuery()
837
+	{
838
+		return $this->query;
839
+	}
840
+
841
+	/**
842
+	 * Dynamically handle calls into the query instance.
843
+	 *
844
+	 * @param  string $method
845
+	 * @param  array  $parameters
846
+	 * @throws Exception
847
+	 * @return mixed
848
+	 */
849
+	public function __call($method, $parameters)
850
+	{
851
+		if (isset($this->macros[$method])) {
852
+			array_unshift($parameters, $this);
853
+
854
+			return call_user_func_array($this->macros[$method], $parameters);
855
+		}
856 856
         
857
-        if (in_array($method, $this->blacklist)) {
858
-            throw new Exception("Method $method doesn't exist");
859
-        }
857
+		if (in_array($method, $this->blacklist)) {
858
+			throw new Exception("Method $method doesn't exist");
859
+		}
860 860
 
861
-        $result = call_user_func_array([$this->query, $method], $parameters);
861
+		$result = call_user_func_array([$this->query, $method], $parameters);
862 862
 
863
-        return in_array($method, $this->passthru) ? $result : $this;
864
-    }
863
+		return in_array($method, $this->passthru) ? $result : $this;
864
+	}
865 865
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -466,7 +466,7 @@  discard block
 block discarded – undo
466 466
             $count = new Expression($count);
467 467
         }
468 468
 
469
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
469
+        return $this->where(new Expression('('.$hasQuery->toSql().')'), $operator, $count, $boolean);
470 470
     }
471 471
 
472 472
     /**
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
      */
500 500
     protected function getHasRelationQuery($relation, $entity)
501 501
     {
502
-        return Relationship::noConstraints(function () use ($relation, $entity) {
502
+        return Relationship::noConstraints(function() use ($relation, $entity) {
503 503
             return $this->entityMap->$relation($entity);
504 504
         });
505 505
     }
@@ -548,7 +548,7 @@  discard block
 block discarded – undo
548 548
             // constraints have been specified for the eager load and we'll just put
549 549
             // an empty Closure with the loader so that we can treat all the same.
550 550
             if (is_numeric($name)) {
551
-                $f = function () {};
551
+                $f = function() {};
552 552
 
553 553
                 list($name, $constraints) = [$constraints, $f];
554 554
             }
@@ -583,7 +583,7 @@  discard block
 block discarded – undo
583 583
             $progress[] = $segment;
584 584
 
585 585
             if (!isset($results[$last = implode('.', $progress)])) {
586
-                $results[$last] = function () {};
586
+                $results[$last] = function() {};
587 587
             }
588 588
         }
589 589
 
@@ -672,7 +672,7 @@  discard block
 block discarded – undo
672 672
         // We want to run a relationship query without any constrains so that we will
673 673
         // not have to remove these where clauses manually which gets really hacky
674 674
         // and is error prone while we remove the developer's own where clauses.
675
-        $query = Relationship::noConstraints(function () use ($relation) {
675
+        $query = Relationship::noConstraints(function() use ($relation) {
676 676
             return $this->entityMap->$relation($this->getEntityInstance());
677 677
         });
678 678
 
@@ -703,7 +703,7 @@  discard block
 block discarded – undo
703 703
         // that start with the given top relations and adds them to our arrays.
704 704
         foreach ($this->eagerLoad as $name => $constraints) {
705 705
             if ($this->isNested($name, $relation)) {
706
-                $nested[substr($name, strlen($relation . '.'))] = $constraints;
706
+                $nested[substr($name, strlen($relation.'.'))] = $constraints;
707 707
             }
708 708
         }
709 709
 
@@ -721,7 +721,7 @@  discard block
 block discarded – undo
721 721
     {
722 722
         $dots = str_contains($name, '.');
723 723
 
724
-        return $dots && starts_with($name, $relation . '.');
724
+        return $dots && starts_with($name, $relation.'.');
725 725
     }
726 726
 
727 727
     /**
Please login to merge, or discard this patch.