Completed
Push — 5.1 ( 78924a...4f568f )
by Rémi
04:05
created
src/Commands/Store.php 1 patch
Indentation   +225 added lines, -225 removed lines patch added patch discarded remove patch
@@ -14,240 +14,240 @@
 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
-        // Update any dirty relationship. This include relationships that already exists, have
136
-        // dirty attributes / newly created related entities / dirty related entities.
137
-        $dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
138
-
139
-        foreach ($dirtyRelatedAggregates as $related) {
140
-            $this->createStoreCommand($related)->execute();
141
-        }
142
-
143
-        // Now we can sync the related collections
144
-        if ($this->aggregate->exists()) {
145
-            $this->aggregate->syncRelationships($foreignRelationships);
146
-        }
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
+		// Update any dirty relationship. This include relationships that already exists, have
136
+		// dirty attributes / newly created related entities / dirty related entities.
137
+		$dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
138
+
139
+		foreach ($dirtyRelatedAggregates as $related) {
140
+			$this->createStoreCommand($related)->execute();
141
+		}
142
+
143
+		// Now we can sync the related collections
144
+		if ($this->aggregate->exists()) {
145
+			$this->aggregate->syncRelationships($foreignRelationships);
146
+		}
147 147
         
148
-        // TODO be move it to the wrapper class
149
-        // so it's the same code for the entity builder
150
-        $aggregate->setProxies();
151
-
152
-        // Update Entity Cache
153
-        $aggregate->getMapper()->getEntityCache()->refresh($aggregate);
154
-    }
155
-
156
-    /**
157
-     * Update Related Entities which attributes have
158
-     * been modified.
159
-     *
160
-     * @return void
161
-     */
162
-    protected function updateDirtyRelated()
163
-    {
164
-        $relations = $this->entityMap->getRelationships();
165
-        $attributes = $this->getAttributes();
166
-
167
-        foreach ($relations as $relation) {
168
-            if (!array_key_exists($relation, $attributes)) {
169
-                continue;
170
-            }
171
-
172
-            $value = $attributes[$relation];
173
-
174
-            if ($value == null) {
175
-                continue;
176
-            }
177
-
178
-            if ($value instanceof EntityProxy) {
179
-                continue;
180
-            }
181
-
182
-            if ($value instanceof CollectionProxy && $value->isLoaded()) {
183
-                $value = $value->getUnderlyingCollection();
184
-            }
185
-            if ($value instanceof CollectionProxy && !$value->isLoaded()) {
186
-                foreach ($value->getAddedItems() as $entity) {
187
-                    $this->updateEntityIfDirty($entity);
188
-                }
189
-                continue;
190
-            }
191
-
192
-            if ($value instanceof EntityCollection) {
193
-                foreach ($value as $entity) {
194
-                    if (!$this->createEntityIfNotExists($entity)) {
195
-                        $this->updateEntityIfDirty($entity);
196
-                    }
197
-                }
198
-                continue;
199
-            }
200
-            if ($value instanceof Mappable) {
201
-                $this->updateEntityIfDirty($value);
202
-                continue;
203
-            }
204
-        }
205
-    }
206
-
207
-    /**
208
-     * Execute an insert statement on the database
209
-     *
210
-     * @return void
211
-     */
212
-    protected function insert()
213
-    {
214
-        $aggregate = $this->aggregate;
215
-
216
-        $attributes = $aggregate->getRawAttributes();
148
+		// TODO be move it to the wrapper class
149
+		// so it's the same code for the entity builder
150
+		$aggregate->setProxies();
151
+
152
+		// Update Entity Cache
153
+		$aggregate->getMapper()->getEntityCache()->refresh($aggregate);
154
+	}
155
+
156
+	/**
157
+	 * Update Related Entities which attributes have
158
+	 * been modified.
159
+	 *
160
+	 * @return void
161
+	 */
162
+	protected function updateDirtyRelated()
163
+	{
164
+		$relations = $this->entityMap->getRelationships();
165
+		$attributes = $this->getAttributes();
166
+
167
+		foreach ($relations as $relation) {
168
+			if (!array_key_exists($relation, $attributes)) {
169
+				continue;
170
+			}
171
+
172
+			$value = $attributes[$relation];
173
+
174
+			if ($value == null) {
175
+				continue;
176
+			}
177
+
178
+			if ($value instanceof EntityProxy) {
179
+				continue;
180
+			}
181
+
182
+			if ($value instanceof CollectionProxy && $value->isLoaded()) {
183
+				$value = $value->getUnderlyingCollection();
184
+			}
185
+			if ($value instanceof CollectionProxy && !$value->isLoaded()) {
186
+				foreach ($value->getAddedItems() as $entity) {
187
+					$this->updateEntityIfDirty($entity);
188
+				}
189
+				continue;
190
+			}
191
+
192
+			if ($value instanceof EntityCollection) {
193
+				foreach ($value as $entity) {
194
+					if (!$this->createEntityIfNotExists($entity)) {
195
+						$this->updateEntityIfDirty($entity);
196
+					}
197
+				}
198
+				continue;
199
+			}
200
+			if ($value instanceof Mappable) {
201
+				$this->updateEntityIfDirty($value);
202
+				continue;
203
+			}
204
+		}
205
+	}
206
+
207
+	/**
208
+	 * Execute an insert statement on the database
209
+	 *
210
+	 * @return void
211
+	 */
212
+	protected function insert()
213
+	{
214
+		$aggregate = $this->aggregate;
215
+
216
+		$attributes = $aggregate->getRawAttributes();
217 217
         
218
-        $keyName = $aggregate->getEntityMap()->getKeyName();
218
+		$keyName = $aggregate->getEntityMap()->getKeyName();
219 219
 
220
-        // Check if the primary key is defined in the attributes
221
-        if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
222
-            $this->query->insert($attributes);
223
-        } else {
224
-            $sequence = $aggregate->getEntityMap()->getSequence();
220
+		// Check if the primary key is defined in the attributes
221
+		if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
222
+			$this->query->insert($attributes);
223
+		} else {
224
+			$sequence = $aggregate->getEntityMap()->getSequence();
225 225
 
226
-            $id = $this->query->insertGetId($attributes, $sequence);
226
+			$id = $this->query->insertGetId($attributes, $sequence);
227 227
 
228
-            $aggregate->setEntityAttribute($keyName, $id);
229
-        }
230
-    }
228
+			$aggregate->setEntityAttribute($keyName, $id);
229
+		}
230
+	}
231 231
 
232
-    /**
233
-     * Run an update statement on the entity
234
-     *
235
-     * @throws \InvalidArgumentException
236
-     *
237
-     * @return void
238
-     */
239
-    protected function update()
240
-    {
241
-        $query = $this->query;
232
+	/**
233
+	 * Run an update statement on the entity
234
+	 *
235
+	 * @throws \InvalidArgumentException
236
+	 *
237
+	 * @return void
238
+	 */
239
+	protected function update()
240
+	{
241
+		$query = $this->query;
242 242
 
243
-        $keyName = $this->aggregate->getEntityKey();
243
+		$keyName = $this->aggregate->getEntityKey();
244 244
 
245
-        $query = $query->where($keyName, '=', $this->aggregate->getEntityId());
245
+		$query = $query->where($keyName, '=', $this->aggregate->getEntityId());
246 246
 
247
-        $dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
247
+		$dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
248 248
 
249
-        if (count($dirtyAttributes) > 0) {
250
-            $query->update($dirtyAttributes);
251
-        }
252
-    }
249
+		if (count($dirtyAttributes) > 0) {
250
+			$query->update($dirtyAttributes);
251
+		}
252
+	}
253 253
 }
Please login to merge, or discard this patch.
src/Relationships/BelongsToMany.php 1 patch
Indentation   +928 added lines, -928 removed lines patch added patch discarded remove patch
@@ -12,937 +12,937 @@
 block discarded – undo
12 12
 
13 13
 class BelongsToMany extends Relationship
14 14
 {
15
-    /**
16
-     * The intermediate table for the relation.
17
-     *
18
-     * @var string
19
-     */
20
-    protected $table;
21
-
22
-    /**
23
-     * The foreign key of the parent model.
24
-     *
25
-     * @var string
26
-     */
27
-    protected $foreignKey;
28
-
29
-    /**
30
-     * The associated key of the relation.
31
-     *
32
-     * @var string
33
-     */
34
-    protected $otherKey;
35
-
36
-    /**
37
-     * The "name" of the relationship.
38
-     *
39
-     * @var string
40
-     */
41
-    protected $relationName;
42
-
43
-    /**
44
-     * The pivot table columns to retrieve.
45
-     *
46
-     * @var array
47
-     */
48
-    protected $pivotColumns = [];
49
-
50
-    /**
51
-     * This relationship has pivot attributes
52
-     *
53
-     * @var boolean
54
-     */
55
-    protected static $hasPivot = true;
56
-
57
-    /**
58
-     * Create a new has many relationship instance.
59
-     *
60
-     * @param Mapper   $mapper
61
-     * @param Mappable $parent
62
-     * @param string   $table
63
-     * @param string   $foreignKey
64
-     * @param string   $otherKey
65
-     * @param string   $relationName
66
-     */
67
-    public function __construct(Mapper $mapper, $parent, $table, $foreignKey, $otherKey, $relationName = null)
68
-    {
69
-        $this->table = $table;
70
-        $this->otherKey = $otherKey;
71
-        $this->foreignKey = $foreignKey;
72
-        $this->relationName = $relationName;
73
-
74
-        parent::__construct($mapper, $parent);
75
-    }
76
-
77
-    /**
78
-     * @param  $related
79
-     * @return mixed
80
-     */
81
-    public function attachTo($related)
82
-    {
83
-    }
84
-
85
-    /**
86
-     * @param  $related
87
-     * @return mixed
88
-     */
89
-    public function detachFrom($related)
90
-    {
91
-        $ids = $this->getIdsFromHashes([$related]);
92
-
93
-        $this->detach($ids);
94
-    }
95
-
96
-    /**
97
-     * @param $related
98
-     */
99
-    public function detachMany($related)
100
-    {
101
-        $ids = $this->getIdsFromHashes($related);
102
-
103
-        $this->detach($ids);
104
-    }
105
-
106
-    /**
107
-     * @param array $hashes
108
-     * @return array
109
-     */
110
-    protected function getIdsFromHashes(array $hashes)
111
-    {
112
-        $ids = [];
113
-
114
-        foreach ($hashes as $hash) {
115
-            $split = explode('.', $hash);
116
-            $ids[] = $split[1];
117
-        }
118
-        return $ids;
119
-    }
120
-
121
-    /**
122
-     * Get the results of the relationship.
123
-     *
124
-     * @param $relation
125
-     *
126
-     * @return EntityCollection
127
-     */
128
-    public function getResults($relation)
129
-    {
130
-        $results = $this->get();
131
-
132
-        $this->cacheRelation($results, $relation);
133
-
134
-        return $results;
135
-    }
136
-
137
-    /**
138
-     * Set a where clause for a pivot table column.
139
-     *
140
-     * @param  string $column
141
-     * @param  string $operator
142
-     * @param  mixed  $value
143
-     * @param  string $boolean
144
-     * @return self
145
-     */
146
-    public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
147
-    {
148
-        return $this->where($this->table . '.' . $column, $operator, $value, $boolean);
149
-    }
150
-
151
-    /**
152
-     * Set an or where clause for a pivot table column.
153
-     *
154
-     * @param  string $column
155
-     * @param  string $operator
156
-     * @param  mixed  $value
157
-     * @return self
158
-     */
159
-    public function orWherePivot($column, $operator = null, $value = null)
160
-    {
161
-        return $this->wherePivot($column, $operator, $value, 'or');
162
-    }
163
-
164
-    /**
165
-     * Return Pivot attributes when available on a relationship
166
-     *
167
-     * @return array
168
-     */
169
-    public function getPivotAttributes()
170
-    {
171
-        return $this->pivotColumns;
172
-    }
173
-
174
-    /**
175
-     * Execute the query and get the first result.
176
-     *
177
-     * @param  array $columns
178
-     * @return mixed
179
-     */
180
-    public function first($columns = ['*'])
181
-    {
182
-        $results = $this->take(1)->get($columns);
183
-
184
-        return count($results) > 0 ? $results->first() : null;
185
-    }
186
-
187
-    /**
188
-     * Execute the query and get the first result or throw an exception.
189
-     *
190
-     * @param  array $columns
191
-     *
192
-     * @throws EntityNotFoundException
193
-     *
194
-     * @return Mappable|self
195
-     */
196
-    public function firstOrFail($columns = ['*'])
197
-    {
198
-        if (!is_null($entity = $this->first($columns))) {
199
-            return $entity;
200
-        }
201
-
202
-        throw new EntityNotFoundException;
203
-    }
204
-
205
-    /**
206
-     * Execute the query as a "select" statement.
207
-     *
208
-     * @param  array $columns
209
-     * @return \Analogue\ORM\EntityCollection
210
-     */
211
-    public function get($columns = ['*'])
212
-    {
213
-        // First we'll add the proper select columns onto the query so it is run with
214
-        // the proper columns. Then, we will get the results and hydrate out pivot
215
-        // models with the result of those columns as a separate model relation.
216
-        $columns = $this->query->getQuery()->columns ? [] : $columns;
217
-
218
-        $select = $this->getSelectColumns($columns);
219
-
220
-        $entities = $this->query->addSelect($select)->getEntities();
221
-
222
-        $this->hydratePivotRelation($entities);
223
-
224
-        // If we actually found models we will also eager load any relationships that
225
-        // have been specified as needing to be eager loaded. This will solve the
226
-        // n + 1 query problem for the developer and also increase performance.
227
-        if (count($entities) > 0) {
228
-            $entities = $this->query->eagerLoadRelations($entities);
229
-        }
230
-
231
-        return $this->relatedMap->newCollection($entities);
232
-    }
233
-
234
-    /**
235
-     * Hydrate the pivot table relationship on the models.
236
-     *
237
-     * @param  array $entities
238
-     * @return void
239
-     */
240
-    protected function hydratePivotRelation(array $entities)
241
-    {
242
-        // To hydrate the pivot relationship, we will just gather the pivot attributes
243
-        // and create a new Pivot model, which is basically a dynamic model that we
244
-        // will set the attributes, table, and connections on so it they be used.
245
-
246
-        foreach ($entities as $entity) {
247
-            $entityWrapper = $this->factory->make($entity);
248
-
249
-            $pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
250
-
251
-            $entityWrapper->setEntityAttribute('pivot', $pivot);
252
-        }
253
-    }
254
-
255
-    /**
256
-     * Get the pivot attributes from a model.
257
-     *
258
-     * @param  $entity
259
-     * @return array
260
-     */
261
-    protected function cleanPivotAttributes(InternallyMappable $entity)
262
-    {
263
-        $values = [];
264
-
265
-        $attributes = $entity->getEntityAttributes();
266
-
267
-        foreach ($attributes as $key => $value) {
268
-            // To get the pivots attributes we will just take any of the attributes which
269
-            // begin with "pivot_" and add those to this arrays, as well as unsetting
270
-            // them from the parent's models since they exist in a different table.
271
-            if (strpos($key, 'pivot_') === 0) {
272
-                $values[substr($key, 6)] = $value;
273
-
274
-                unset($attributes[$key]);
275
-            }
276
-        }
277
-
278
-        // Rehydrate Entity with cleaned array.
279
-        $entity->setEntityAttributes($attributes);
280
-
281
-        return $values;
282
-    }
283
-
284
-    /**
285
-     * Set the base constraints on the relation query.
286
-     *
287
-     * @return void
288
-     */
289
-    public function addConstraints()
290
-    {
291
-        $this->setJoin();
292
-
293
-        if (static::$constraints) {
294
-            $this->setWhere();
295
-        }
296
-    }
297
-
298
-    /**
299
-     * Add the constraints for a relationship count query.
300
-     *
301
-     * @param  Query $query
302
-     * @param  Query $parent
303
-     * @return Query
304
-     */
305
-    public function getRelationCountQuery(Query $query, Query $parent)
306
-    {
307
-        if ($parent->getQuery()->from == $query->getQuery()->from) {
308
-            return $this->getRelationCountQueryForSelfJoin($query, $parent);
309
-        }
310
-
311
-        $this->setJoin($query);
312
-
313
-        return parent::getRelationCountQuery($query, $parent);
314
-    }
315
-
316
-    /**
317
-     * Add the constraints for a relationship count query on the same table.
318
-     *
319
-     * @param  Query $query
320
-     * @param  Query $parent
321
-     * @return Query
322
-     */
323
-    public function getRelationCountQueryForSelfJoin(Query $query, Query $parent)
324
-    {
325
-        $query->select(new Expression('count(*)'));
326
-
327
-        $tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
328
-
329
-        $query->from($this->table . ' as ' . $tablePrefix . $hash = $this->getRelationCountHash());
330
-
331
-        $key = $this->wrap($this->getQualifiedParentKeyName());
332
-
333
-        return $query->where($hash . '.' . $this->foreignKey, '=', new Expression($key));
334
-    }
335
-
336
-    /**
337
-     * Get a relationship join table hash.
338
-     *
339
-     * @return string
340
-     */
341
-    public function getRelationCountHash()
342
-    {
343
-        return 'self_' . md5(microtime(true));
344
-    }
345
-
346
-    /**
347
-     * Set the select clause for the relation query.
348
-     *
349
-     * @param  array $columns
350
-     * @return \Analogue\ORM\Relationships\BelongsToMany
351
-     */
352
-    protected function getSelectColumns(array $columns = ['*'])
353
-    {
354
-        if ($columns == ['*']) {
355
-            $columns = [$this->relatedMap->getTable() . '.*'];
356
-        }
357
-
358
-        return array_merge($columns, $this->getAliasedPivotColumns());
359
-    }
360
-
361
-    /**
362
-     * Get the pivot columns for the relation.
363
-     *
364
-     * @return array
365
-     */
366
-    protected function getAliasedPivotColumns()
367
-    {
368
-        $defaults = [$this->foreignKey, $this->otherKey];
369
-
370
-        // We need to alias all of the pivot columns with the "pivot_" prefix so we
371
-        // can easily extract them out of the models and put them into the pivot
372
-        // relationships when they are retrieved and hydrated into the models.
373
-        $columns = [];
374
-
375
-        foreach (array_merge($defaults, $this->pivotColumns) as $column) {
376
-            $columns[] = $this->table . '.' . $column . ' as pivot_' . $column;
377
-        }
378
-
379
-        return array_unique($columns);
380
-    }
381
-
382
-    /**
383
-     * Set the join clause for the relation query.
384
-     *
385
-     * @param  \Analogue\ORM\Query|null
386
-     * @return $this
387
-     */
388
-    protected function setJoin($query = null)
389
-    {
390
-        $query = $query ?: $this->query;
391
-
392
-        // We need to join to the intermediate table on the related model's primary
393
-        // key column with the intermediate table's foreign key for the related
394
-        // model instance. Then we can set the "where" for the parent models.
395
-        $baseTable = $this->relatedMap->getTable();
396
-
397
-        $key = $baseTable . '.' . $this->relatedMap->getKeyName();
398
-
399
-        $query->join($this->table, $key, '=', $this->getOtherKey());
400
-
401
-        return $this;
402
-    }
403
-
404
-    /**
405
-     * Set the where clause for the relation query.
406
-     *
407
-     * @return $this
408
-     */
409
-    protected function setWhere()
410
-    {
411
-        $foreign = $this->getForeignKey();
412
-
413
-        $parentKey = $this->parentMap->getKeyName();
414
-
415
-        $this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey));
416
-
417
-        return $this;
418
-    }
419
-
420
-    /**
421
-     * Set the constraints for an eager load of the relation.
422
-     *
423
-     * @param  array $entities
424
-     * @return void
425
-     */
426
-    public function addEagerConstraints(array $entities)
427
-    {
428
-        $this->query->whereIn($this->getForeignKey(), $this->getKeys($entities));
429
-    }
430
-
431
-    /**
432
-     * Initialize the relation on a set of eneities.
433
-     *
434
-     * @param  array  $entities
435
-     * @param  string $relation
436
-     * @return array
437
-     */
438
-    public function initRelation(array $entities, $relation)
439
-    {
440
-        foreach ($entities as $entity) {
441
-            $entity = $this->factory->make($entity);
442
-
443
-            $entity->setEntityAttribute($relation, $this->relatedMap->newCollection());
444
-        }
445
-
446
-        return $entities;
447
-    }
448
-
449
-    /**
450
-     * Match the eagerly loaded results to their parents.
451
-     *
452
-     * @param  array            $entities
453
-     * @param  EntityCollection $results
454
-     * @param  string           $relation
455
-     * @return array
456
-     */
457
-    public function match(array $entities, EntityCollection $results, $relation)
458
-    {
459
-        $dictionary = $this->buildDictionary($results);
460
-
461
-        $keyName = $this->relatedMap->getKeyName();
462
-
463
-        $cache = $this->parentMapper->getEntityCache();
464
-
465
-        // Once we have an array dictionary of child objects we can easily match the
466
-        // children back to their parent using the dictionary and the keys on the
467
-        // the parent models. Then we will return the hydrated models back out.
468
-        foreach ($entities as $entity) {
469
-            $wrapper = $this->factory->make($entity);
470
-
471
-            if (isset($dictionary[$key = $wrapper->getEntityAttribute($keyName)])) {
472
-                $collection = $this->relatedMap->newCollection($dictionary[$key]);
473
-
474
-                $wrapper->setEntityAttribute($relation, $collection);
475
-
476
-                $cache->cacheLoadedRelationResult($entity, $relation, $collection, $this);
477
-            }
478
-        }
479
-
480
-        return $entities;
481
-    }
482
-
483
-    /**
484
-     * Build model dictionary keyed by the relation's foreign key.
485
-     *
486
-     * @param  EntityCollection $results
487
-     * @return array
488
-     */
489
-    protected function buildDictionary(EntityCollection $results)
490
-    {
491
-        $foreign = $this->foreignKey;
492
-
493
-        // First we will build a dictionary of child models keyed by the foreign key
494
-        // of the relation so that we will easily and quickly match them to their
495
-        // parents without having a possibly slow inner loops for every models.
496
-        $dictionary = [];
497
-
498
-        foreach ($results as $entity) {
499
-            $wrapper = $this->factory->make($entity);
500
-
501
-            $dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity;
502
-        }
503
-
504
-        return $dictionary;
505
-    }
506
-
507
-    /**
508
-     * Get all of the IDs for the related models.
509
-     *
510
-     * @return array
511
-     */
512
-    public function getRelatedIds()
513
-    {
514
-        $fullKey = $this->relatedMap->getQualifiedKeyName();
515
-
516
-        return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName());
517
-    }
518
-
519
-    /**
520
-     * Update Pivot
521
-     *
522
-     * @param  \Analogue\ORM\Entity $entity
523
-     * @return void
524
-     */
525
-    public function updatePivot($entity)
526
-    {
527
-        $keyName = $this->relatedMap->getKeyName();
528
-
529
-        $this->updateExistingPivot(
530
-            $entity->getEntityAttribute($keyName),
531
-            $entity->getEntityAttribute('pivot')->getEntityAttributes()
532
-        );
533
-    }
534
-
535
-    /**
536
-     * Update Multiple pivot
537
-     *
538
-     * @param  $relatedEntities
539
-     * @return void
540
-     */
541
-    public function updatePivots($relatedEntities)
542
-    {
543
-        foreach ($relatedEntities as $entity) {
544
-            $this->updatePivot($entity);
545
-        }
546
-    }
547
-
548
-    /**
549
-     * Create Pivot Records
550
-     *
551
-     * @param \Analogue\ORM\Entity[] $relatedEntities
552
-     * @return void
553
-     */
554
-    public function createPivots($relatedEntities)
555
-    {
556
-        $keys = [];
557
-        $attributes = [];
558
-
559
-        $keyName = $this->relatedMap->getKeyName();
560
-
561
-        foreach ($relatedEntities as $entity) {
562
-            $keys[] = $entity->getEntityAttribute($keyName);
563
-        }
564
-
565
-        $records = $this->createAttachRecords($keys, $attributes);
566
-
567
-        $this->query->getQuery()->from($this->table)->insert($records);
568
-    }
569
-
570
-    /**
571
-     * Update an existing pivot record on the table.
572
-     *
573
-     * @param  mixed $id
574
-     * @param  array $attributes
575
-     * @throws \InvalidArgumentException
576
-     * @return integer
577
-     */
578
-    public function updateExistingPivot($id, array $attributes)
579
-    {
580
-        if (in_array($this->updatedAt(), $this->pivotColumns)) {
581
-            $attributes = $this->setTimestampsOnAttach($attributes, true);
582
-        }
583
-
584
-        return $this->newPivotStatementForId($id)->update($attributes);
585
-    }
586
-
587
-    /**
588
-     * Attach a model to the parent.
589
-     *
590
-     * @param  mixed $id
591
-     * @param  array $attributes
592
-     * @return void
593
-     */
594
-    public function attach($id, array $attributes = [])
595
-    {
596
-        $query = $this->newPivotStatement();
597
-
598
-        $query->insert($this->createAttachRecords((array) $id, $attributes));
599
-    }
600
-
601
-    /**
602
-     * @param  array $entities
603
-     *
604
-     * @throws \InvalidArgumentException
605
-     */
606
-    public function sync(array $entities)
607
-    {
608
-        $this->detachExcept($entities);
609
-    }
610
-
611
-    /**
612
-     * Detach related entities that are not in $id
613
-     *
614
-     * @param  array $entities
615
-     *
616
-     * @throws \InvalidArgumentException
617
-     *
618
-     * @return void
619
-     */
620
-    protected function detachExcept(array $entities = [])
621
-    {
622
-        tdd($this->relationName);
623
-        $query = $this->newPivotQuery();
624
-
625
-        if (count($entities) > 0) {
626
-            $keys = $this->getKeys($entities);
627
-
628
-            $query->whereNotIn($this->otherKey, $keys);
629
-        }
630
-        $parentKey = $this->parentMap->getKeyName();
631
-
632
-        $query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey));
15
+	/**
16
+	 * The intermediate table for the relation.
17
+	 *
18
+	 * @var string
19
+	 */
20
+	protected $table;
21
+
22
+	/**
23
+	 * The foreign key of the parent model.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	protected $foreignKey;
28
+
29
+	/**
30
+	 * The associated key of the relation.
31
+	 *
32
+	 * @var string
33
+	 */
34
+	protected $otherKey;
35
+
36
+	/**
37
+	 * The "name" of the relationship.
38
+	 *
39
+	 * @var string
40
+	 */
41
+	protected $relationName;
42
+
43
+	/**
44
+	 * The pivot table columns to retrieve.
45
+	 *
46
+	 * @var array
47
+	 */
48
+	protected $pivotColumns = [];
49
+
50
+	/**
51
+	 * This relationship has pivot attributes
52
+	 *
53
+	 * @var boolean
54
+	 */
55
+	protected static $hasPivot = true;
56
+
57
+	/**
58
+	 * Create a new has many relationship instance.
59
+	 *
60
+	 * @param Mapper   $mapper
61
+	 * @param Mappable $parent
62
+	 * @param string   $table
63
+	 * @param string   $foreignKey
64
+	 * @param string   $otherKey
65
+	 * @param string   $relationName
66
+	 */
67
+	public function __construct(Mapper $mapper, $parent, $table, $foreignKey, $otherKey, $relationName = null)
68
+	{
69
+		$this->table = $table;
70
+		$this->otherKey = $otherKey;
71
+		$this->foreignKey = $foreignKey;
72
+		$this->relationName = $relationName;
73
+
74
+		parent::__construct($mapper, $parent);
75
+	}
76
+
77
+	/**
78
+	 * @param  $related
79
+	 * @return mixed
80
+	 */
81
+	public function attachTo($related)
82
+	{
83
+	}
84
+
85
+	/**
86
+	 * @param  $related
87
+	 * @return mixed
88
+	 */
89
+	public function detachFrom($related)
90
+	{
91
+		$ids = $this->getIdsFromHashes([$related]);
92
+
93
+		$this->detach($ids);
94
+	}
95
+
96
+	/**
97
+	 * @param $related
98
+	 */
99
+	public function detachMany($related)
100
+	{
101
+		$ids = $this->getIdsFromHashes($related);
102
+
103
+		$this->detach($ids);
104
+	}
105
+
106
+	/**
107
+	 * @param array $hashes
108
+	 * @return array
109
+	 */
110
+	protected function getIdsFromHashes(array $hashes)
111
+	{
112
+		$ids = [];
113
+
114
+		foreach ($hashes as $hash) {
115
+			$split = explode('.', $hash);
116
+			$ids[] = $split[1];
117
+		}
118
+		return $ids;
119
+	}
120
+
121
+	/**
122
+	 * Get the results of the relationship.
123
+	 *
124
+	 * @param $relation
125
+	 *
126
+	 * @return EntityCollection
127
+	 */
128
+	public function getResults($relation)
129
+	{
130
+		$results = $this->get();
131
+
132
+		$this->cacheRelation($results, $relation);
133
+
134
+		return $results;
135
+	}
136
+
137
+	/**
138
+	 * Set a where clause for a pivot table column.
139
+	 *
140
+	 * @param  string $column
141
+	 * @param  string $operator
142
+	 * @param  mixed  $value
143
+	 * @param  string $boolean
144
+	 * @return self
145
+	 */
146
+	public function wherePivot($column, $operator = null, $value = null, $boolean = 'and')
147
+	{
148
+		return $this->where($this->table . '.' . $column, $operator, $value, $boolean);
149
+	}
150
+
151
+	/**
152
+	 * Set an or where clause for a pivot table column.
153
+	 *
154
+	 * @param  string $column
155
+	 * @param  string $operator
156
+	 * @param  mixed  $value
157
+	 * @return self
158
+	 */
159
+	public function orWherePivot($column, $operator = null, $value = null)
160
+	{
161
+		return $this->wherePivot($column, $operator, $value, 'or');
162
+	}
163
+
164
+	/**
165
+	 * Return Pivot attributes when available on a relationship
166
+	 *
167
+	 * @return array
168
+	 */
169
+	public function getPivotAttributes()
170
+	{
171
+		return $this->pivotColumns;
172
+	}
173
+
174
+	/**
175
+	 * Execute the query and get the first result.
176
+	 *
177
+	 * @param  array $columns
178
+	 * @return mixed
179
+	 */
180
+	public function first($columns = ['*'])
181
+	{
182
+		$results = $this->take(1)->get($columns);
183
+
184
+		return count($results) > 0 ? $results->first() : null;
185
+	}
186
+
187
+	/**
188
+	 * Execute the query and get the first result or throw an exception.
189
+	 *
190
+	 * @param  array $columns
191
+	 *
192
+	 * @throws EntityNotFoundException
193
+	 *
194
+	 * @return Mappable|self
195
+	 */
196
+	public function firstOrFail($columns = ['*'])
197
+	{
198
+		if (!is_null($entity = $this->first($columns))) {
199
+			return $entity;
200
+		}
201
+
202
+		throw new EntityNotFoundException;
203
+	}
204
+
205
+	/**
206
+	 * Execute the query as a "select" statement.
207
+	 *
208
+	 * @param  array $columns
209
+	 * @return \Analogue\ORM\EntityCollection
210
+	 */
211
+	public function get($columns = ['*'])
212
+	{
213
+		// First we'll add the proper select columns onto the query so it is run with
214
+		// the proper columns. Then, we will get the results and hydrate out pivot
215
+		// models with the result of those columns as a separate model relation.
216
+		$columns = $this->query->getQuery()->columns ? [] : $columns;
217
+
218
+		$select = $this->getSelectColumns($columns);
219
+
220
+		$entities = $this->query->addSelect($select)->getEntities();
221
+
222
+		$this->hydratePivotRelation($entities);
223
+
224
+		// If we actually found models we will also eager load any relationships that
225
+		// have been specified as needing to be eager loaded. This will solve the
226
+		// n + 1 query problem for the developer and also increase performance.
227
+		if (count($entities) > 0) {
228
+			$entities = $this->query->eagerLoadRelations($entities);
229
+		}
230
+
231
+		return $this->relatedMap->newCollection($entities);
232
+	}
233
+
234
+	/**
235
+	 * Hydrate the pivot table relationship on the models.
236
+	 *
237
+	 * @param  array $entities
238
+	 * @return void
239
+	 */
240
+	protected function hydratePivotRelation(array $entities)
241
+	{
242
+		// To hydrate the pivot relationship, we will just gather the pivot attributes
243
+		// and create a new Pivot model, which is basically a dynamic model that we
244
+		// will set the attributes, table, and connections on so it they be used.
245
+
246
+		foreach ($entities as $entity) {
247
+			$entityWrapper = $this->factory->make($entity);
248
+
249
+			$pivot = $this->newExistingPivot($this->cleanPivotAttributes($entityWrapper));
250
+
251
+			$entityWrapper->setEntityAttribute('pivot', $pivot);
252
+		}
253
+	}
254
+
255
+	/**
256
+	 * Get the pivot attributes from a model.
257
+	 *
258
+	 * @param  $entity
259
+	 * @return array
260
+	 */
261
+	protected function cleanPivotAttributes(InternallyMappable $entity)
262
+	{
263
+		$values = [];
264
+
265
+		$attributes = $entity->getEntityAttributes();
266
+
267
+		foreach ($attributes as $key => $value) {
268
+			// To get the pivots attributes we will just take any of the attributes which
269
+			// begin with "pivot_" and add those to this arrays, as well as unsetting
270
+			// them from the parent's models since they exist in a different table.
271
+			if (strpos($key, 'pivot_') === 0) {
272
+				$values[substr($key, 6)] = $value;
273
+
274
+				unset($attributes[$key]);
275
+			}
276
+		}
277
+
278
+		// Rehydrate Entity with cleaned array.
279
+		$entity->setEntityAttributes($attributes);
280
+
281
+		return $values;
282
+	}
283
+
284
+	/**
285
+	 * Set the base constraints on the relation query.
286
+	 *
287
+	 * @return void
288
+	 */
289
+	public function addConstraints()
290
+	{
291
+		$this->setJoin();
292
+
293
+		if (static::$constraints) {
294
+			$this->setWhere();
295
+		}
296
+	}
297
+
298
+	/**
299
+	 * Add the constraints for a relationship count query.
300
+	 *
301
+	 * @param  Query $query
302
+	 * @param  Query $parent
303
+	 * @return Query
304
+	 */
305
+	public function getRelationCountQuery(Query $query, Query $parent)
306
+	{
307
+		if ($parent->getQuery()->from == $query->getQuery()->from) {
308
+			return $this->getRelationCountQueryForSelfJoin($query, $parent);
309
+		}
310
+
311
+		$this->setJoin($query);
312
+
313
+		return parent::getRelationCountQuery($query, $parent);
314
+	}
315
+
316
+	/**
317
+	 * Add the constraints for a relationship count query on the same table.
318
+	 *
319
+	 * @param  Query $query
320
+	 * @param  Query $parent
321
+	 * @return Query
322
+	 */
323
+	public function getRelationCountQueryForSelfJoin(Query $query, Query $parent)
324
+	{
325
+		$query->select(new Expression('count(*)'));
326
+
327
+		$tablePrefix = $this->query->getQuery()->getConnection()->getTablePrefix();
328
+
329
+		$query->from($this->table . ' as ' . $tablePrefix . $hash = $this->getRelationCountHash());
330
+
331
+		$key = $this->wrap($this->getQualifiedParentKeyName());
332
+
333
+		return $query->where($hash . '.' . $this->foreignKey, '=', new Expression($key));
334
+	}
335
+
336
+	/**
337
+	 * Get a relationship join table hash.
338
+	 *
339
+	 * @return string
340
+	 */
341
+	public function getRelationCountHash()
342
+	{
343
+		return 'self_' . md5(microtime(true));
344
+	}
345
+
346
+	/**
347
+	 * Set the select clause for the relation query.
348
+	 *
349
+	 * @param  array $columns
350
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
351
+	 */
352
+	protected function getSelectColumns(array $columns = ['*'])
353
+	{
354
+		if ($columns == ['*']) {
355
+			$columns = [$this->relatedMap->getTable() . '.*'];
356
+		}
357
+
358
+		return array_merge($columns, $this->getAliasedPivotColumns());
359
+	}
360
+
361
+	/**
362
+	 * Get the pivot columns for the relation.
363
+	 *
364
+	 * @return array
365
+	 */
366
+	protected function getAliasedPivotColumns()
367
+	{
368
+		$defaults = [$this->foreignKey, $this->otherKey];
369
+
370
+		// We need to alias all of the pivot columns with the "pivot_" prefix so we
371
+		// can easily extract them out of the models and put them into the pivot
372
+		// relationships when they are retrieved and hydrated into the models.
373
+		$columns = [];
374
+
375
+		foreach (array_merge($defaults, $this->pivotColumns) as $column) {
376
+			$columns[] = $this->table . '.' . $column . ' as pivot_' . $column;
377
+		}
378
+
379
+		return array_unique($columns);
380
+	}
381
+
382
+	/**
383
+	 * Set the join clause for the relation query.
384
+	 *
385
+	 * @param  \Analogue\ORM\Query|null
386
+	 * @return $this
387
+	 */
388
+	protected function setJoin($query = null)
389
+	{
390
+		$query = $query ?: $this->query;
391
+
392
+		// We need to join to the intermediate table on the related model's primary
393
+		// key column with the intermediate table's foreign key for the related
394
+		// model instance. Then we can set the "where" for the parent models.
395
+		$baseTable = $this->relatedMap->getTable();
396
+
397
+		$key = $baseTable . '.' . $this->relatedMap->getKeyName();
398
+
399
+		$query->join($this->table, $key, '=', $this->getOtherKey());
400
+
401
+		return $this;
402
+	}
403
+
404
+	/**
405
+	 * Set the where clause for the relation query.
406
+	 *
407
+	 * @return $this
408
+	 */
409
+	protected function setWhere()
410
+	{
411
+		$foreign = $this->getForeignKey();
412
+
413
+		$parentKey = $this->parentMap->getKeyName();
414
+
415
+		$this->query->where($foreign, '=', $this->parent->getEntityAttribute($parentKey));
416
+
417
+		return $this;
418
+	}
419
+
420
+	/**
421
+	 * Set the constraints for an eager load of the relation.
422
+	 *
423
+	 * @param  array $entities
424
+	 * @return void
425
+	 */
426
+	public function addEagerConstraints(array $entities)
427
+	{
428
+		$this->query->whereIn($this->getForeignKey(), $this->getKeys($entities));
429
+	}
430
+
431
+	/**
432
+	 * Initialize the relation on a set of eneities.
433
+	 *
434
+	 * @param  array  $entities
435
+	 * @param  string $relation
436
+	 * @return array
437
+	 */
438
+	public function initRelation(array $entities, $relation)
439
+	{
440
+		foreach ($entities as $entity) {
441
+			$entity = $this->factory->make($entity);
442
+
443
+			$entity->setEntityAttribute($relation, $this->relatedMap->newCollection());
444
+		}
445
+
446
+		return $entities;
447
+	}
448
+
449
+	/**
450
+	 * Match the eagerly loaded results to their parents.
451
+	 *
452
+	 * @param  array            $entities
453
+	 * @param  EntityCollection $results
454
+	 * @param  string           $relation
455
+	 * @return array
456
+	 */
457
+	public function match(array $entities, EntityCollection $results, $relation)
458
+	{
459
+		$dictionary = $this->buildDictionary($results);
460
+
461
+		$keyName = $this->relatedMap->getKeyName();
462
+
463
+		$cache = $this->parentMapper->getEntityCache();
464
+
465
+		// Once we have an array dictionary of child objects we can easily match the
466
+		// children back to their parent using the dictionary and the keys on the
467
+		// the parent models. Then we will return the hydrated models back out.
468
+		foreach ($entities as $entity) {
469
+			$wrapper = $this->factory->make($entity);
470
+
471
+			if (isset($dictionary[$key = $wrapper->getEntityAttribute($keyName)])) {
472
+				$collection = $this->relatedMap->newCollection($dictionary[$key]);
473
+
474
+				$wrapper->setEntityAttribute($relation, $collection);
475
+
476
+				$cache->cacheLoadedRelationResult($entity, $relation, $collection, $this);
477
+			}
478
+		}
479
+
480
+		return $entities;
481
+	}
482
+
483
+	/**
484
+	 * Build model dictionary keyed by the relation's foreign key.
485
+	 *
486
+	 * @param  EntityCollection $results
487
+	 * @return array
488
+	 */
489
+	protected function buildDictionary(EntityCollection $results)
490
+	{
491
+		$foreign = $this->foreignKey;
492
+
493
+		// First we will build a dictionary of child models keyed by the foreign key
494
+		// of the relation so that we will easily and quickly match them to their
495
+		// parents without having a possibly slow inner loops for every models.
496
+		$dictionary = [];
497
+
498
+		foreach ($results as $entity) {
499
+			$wrapper = $this->factory->make($entity);
500
+
501
+			$dictionary[$wrapper->getEntityAttribute('pivot')->$foreign][] = $entity;
502
+		}
503
+
504
+		return $dictionary;
505
+	}
506
+
507
+	/**
508
+	 * Get all of the IDs for the related models.
509
+	 *
510
+	 * @return array
511
+	 */
512
+	public function getRelatedIds()
513
+	{
514
+		$fullKey = $this->relatedMap->getQualifiedKeyName();
515
+
516
+		return $this->getQuery()->select($fullKey)->lists($this->relatedMap->getKeyName());
517
+	}
518
+
519
+	/**
520
+	 * Update Pivot
521
+	 *
522
+	 * @param  \Analogue\ORM\Entity $entity
523
+	 * @return void
524
+	 */
525
+	public function updatePivot($entity)
526
+	{
527
+		$keyName = $this->relatedMap->getKeyName();
528
+
529
+		$this->updateExistingPivot(
530
+			$entity->getEntityAttribute($keyName),
531
+			$entity->getEntityAttribute('pivot')->getEntityAttributes()
532
+		);
533
+	}
534
+
535
+	/**
536
+	 * Update Multiple pivot
537
+	 *
538
+	 * @param  $relatedEntities
539
+	 * @return void
540
+	 */
541
+	public function updatePivots($relatedEntities)
542
+	{
543
+		foreach ($relatedEntities as $entity) {
544
+			$this->updatePivot($entity);
545
+		}
546
+	}
547
+
548
+	/**
549
+	 * Create Pivot Records
550
+	 *
551
+	 * @param \Analogue\ORM\Entity[] $relatedEntities
552
+	 * @return void
553
+	 */
554
+	public function createPivots($relatedEntities)
555
+	{
556
+		$keys = [];
557
+		$attributes = [];
558
+
559
+		$keyName = $this->relatedMap->getKeyName();
560
+
561
+		foreach ($relatedEntities as $entity) {
562
+			$keys[] = $entity->getEntityAttribute($keyName);
563
+		}
564
+
565
+		$records = $this->createAttachRecords($keys, $attributes);
566
+
567
+		$this->query->getQuery()->from($this->table)->insert($records);
568
+	}
569
+
570
+	/**
571
+	 * Update an existing pivot record on the table.
572
+	 *
573
+	 * @param  mixed $id
574
+	 * @param  array $attributes
575
+	 * @throws \InvalidArgumentException
576
+	 * @return integer
577
+	 */
578
+	public function updateExistingPivot($id, array $attributes)
579
+	{
580
+		if (in_array($this->updatedAt(), $this->pivotColumns)) {
581
+			$attributes = $this->setTimestampsOnAttach($attributes, true);
582
+		}
583
+
584
+		return $this->newPivotStatementForId($id)->update($attributes);
585
+	}
586
+
587
+	/**
588
+	 * Attach a model to the parent.
589
+	 *
590
+	 * @param  mixed $id
591
+	 * @param  array $attributes
592
+	 * @return void
593
+	 */
594
+	public function attach($id, array $attributes = [])
595
+	{
596
+		$query = $this->newPivotStatement();
597
+
598
+		$query->insert($this->createAttachRecords((array) $id, $attributes));
599
+	}
600
+
601
+	/**
602
+	 * @param  array $entities
603
+	 *
604
+	 * @throws \InvalidArgumentException
605
+	 */
606
+	public function sync(array $entities)
607
+	{
608
+		$this->detachExcept($entities);
609
+	}
610
+
611
+	/**
612
+	 * Detach related entities that are not in $id
613
+	 *
614
+	 * @param  array $entities
615
+	 *
616
+	 * @throws \InvalidArgumentException
617
+	 *
618
+	 * @return void
619
+	 */
620
+	protected function detachExcept(array $entities = [])
621
+	{
622
+		tdd($this->relationName);
623
+		$query = $this->newPivotQuery();
624
+
625
+		if (count($entities) > 0) {
626
+			$keys = $this->getKeys($entities);
627
+
628
+			$query->whereNotIn($this->otherKey, $keys);
629
+		}
630
+		$parentKey = $this->parentMap->getKeyName();
631
+
632
+		$query->where($this->foreignKey, '=', $this->parent->getEntityAttribute($parentKey));
633 633
         
634
-        $query->delete();
635
-
636
-        $query = $this->newPivotQuery();
637
-    }
638
-
639
-
640
-    /**
641
-     * Create an array of records to insert into the pivot table.
642
-     *
643
-     * @param  array $ids
644
-     * @param  array $attributes
645
-     * @return array
646
-     */
647
-    protected function createAttachRecords($ids, array $attributes)
648
-    {
649
-        $records = [];
650
-
651
-        $timed = in_array($this->createdAt(), $this->pivotColumns);
652
-
653
-        // To create the attachment records, we will simply spin through the IDs given
654
-        // and create a new record to insert for each ID. Each ID may actually be a
655
-        // key in the array, with extra attributes to be placed in other columns.
656
-        foreach ($ids as $key => $value) {
657
-            $records[] = $this->attacher($key, $value, $attributes, $timed);
658
-        }
634
+		$query->delete();
635
+
636
+		$query = $this->newPivotQuery();
637
+	}
638
+
639
+
640
+	/**
641
+	 * Create an array of records to insert into the pivot table.
642
+	 *
643
+	 * @param  array $ids
644
+	 * @param  array $attributes
645
+	 * @return array
646
+	 */
647
+	protected function createAttachRecords($ids, array $attributes)
648
+	{
649
+		$records = [];
650
+
651
+		$timed = in_array($this->createdAt(), $this->pivotColumns);
652
+
653
+		// To create the attachment records, we will simply spin through the IDs given
654
+		// and create a new record to insert for each ID. Each ID may actually be a
655
+		// key in the array, with extra attributes to be placed in other columns.
656
+		foreach ($ids as $key => $value) {
657
+			$records[] = $this->attacher($key, $value, $attributes, $timed);
658
+		}
659 659
         
660
-        return $records;
661
-    }
662
-
663
-    /**
664
-     * Create a full attachment record payload.
665
-     *
666
-     * @param  int   $key
667
-     * @param  mixed $value
668
-     * @param  array $attributes
669
-     * @param  bool  $timed
670
-     * @return array
671
-     */
672
-    protected function attacher($key, $value, $attributes, $timed)
673
-    {
674
-        list($id, $extra) = $this->getAttachId($key, $value, $attributes);
660
+		return $records;
661
+	}
662
+
663
+	/**
664
+	 * Create a full attachment record payload.
665
+	 *
666
+	 * @param  int   $key
667
+	 * @param  mixed $value
668
+	 * @param  array $attributes
669
+	 * @param  bool  $timed
670
+	 * @return array
671
+	 */
672
+	protected function attacher($key, $value, $attributes, $timed)
673
+	{
674
+		list($id, $extra) = $this->getAttachId($key, $value, $attributes);
675 675
         
676
-        // To create the attachment records, we will simply spin through the IDs given
677
-        // and create a new record to insert for each ID. Each ID may actually be a
678
-        // key in the array, with extra attributes to be placed in other columns.
679
-        $record = $this->createAttachRecord($id, $timed);
680
-
681
-        return array_merge($record, $extra);
682
-    }
683
-
684
-    /**
685
-     * Get the attach record ID and extra attributes.
686
-     *
687
-     * @param  int   $key
688
-     * @param  mixed $value
689
-     * @param  array $attributes
690
-     * @return array
691
-     */
692
-    protected function getAttachId($key, $value, array $attributes)
693
-    {
694
-        if (is_array($value)) {
695
-            return [$key, array_merge($value, $attributes)];
696
-        }
697
-
698
-        return [$value, $attributes];
699
-    }
700
-
701
-    /**
702
-     * Create a new pivot attachment record.
703
-     *
704
-     * @param  int  $id
705
-     * @param  bool $timed
706
-     * @return array
707
-     */
708
-    protected function createAttachRecord($id, $timed)
709
-    {
710
-        $parentKey = $this->parentMap->getKeyName();
711
-
712
-        $record = [];
713
-
714
-        $record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey);
715
-
716
-        $record[$this->otherKey] = $id;
717
-
718
-        // If the record needs to have creation and update timestamps, we will make
719
-        // them by calling the parent model's "freshTimestamp" method which will
720
-        // provide us with a fresh timestamp in this model's preferred format.
721
-        if ($timed) {
722
-            $record = $this->setTimestampsOnAttach($record);
723
-        }
724
-
725
-        return $record;
726
-    }
727
-
728
-    /**
729
-     * Set the creation and update timestamps on an attach record.
730
-     *
731
-     * @param  array $record
732
-     * @param  bool  $exists
733
-     * @return array
734
-     */
735
-    protected function setTimestampsOnAttach(array $record, $exists = false)
736
-    {
737
-        $fresh = $this->freshTimestamp();
738
-
739
-        if (!$exists) {
740
-            $record[$this->createdAt()] = $fresh;
741
-        }
742
-
743
-        $record[$this->updatedAt()] = $fresh;
744
-
745
-        return $record;
746
-    }
747
-
748
-    /**
749
-     * @param EntityCollection $entities
750
-     * @return array
751
-     */
752
-    protected function getModelKeysFromCollection(EntityCollection $entities)
753
-    {
754
-        $keyName = $this->relatedMap->getKeyName();
755
-
756
-        return array_map(function ($m) use ($keyName) {
757
-            return $m->$keyName;
758
-        }, $entities);
759
-    }
760
-
761
-    /**
762
-     * Detach models from the relationship.
763
-     *
764
-     * @param  int|array $ids
765
-     * @throws \InvalidArgumentException
766
-     * @return int
767
-     */
768
-    public function detach($ids = [])
769
-    {
770
-        if ($ids instanceof EntityCollection) {
771
-            $ids = (array) $ids->modelKeys();
772
-        }
773
-
774
-        $query = $this->newPivotQuery();
775
-
776
-        // If associated IDs were passed to the method we will only delete those
777
-        // associations, otherwise all of the association ties will be broken.
778
-        // We'll return the numbers of affected rows when we do the deletes.
779
-        $ids = (array) $ids;
780
-
781
-        if (count($ids) > 0) {
782
-            $query->whereIn($this->otherKey, (array) $ids);
783
-        }
784
-
785
-        // Once we have all of the conditions set on the statement, we are ready
786
-        // to run the delete on the pivot table. Then, if the touch parameter
787
-        // is true, we will go ahead and touch all related models to sync.
788
-        return $query->delete();
789
-    }
676
+		// To create the attachment records, we will simply spin through the IDs given
677
+		// and create a new record to insert for each ID. Each ID may actually be a
678
+		// key in the array, with extra attributes to be placed in other columns.
679
+		$record = $this->createAttachRecord($id, $timed);
680
+
681
+		return array_merge($record, $extra);
682
+	}
683
+
684
+	/**
685
+	 * Get the attach record ID and extra attributes.
686
+	 *
687
+	 * @param  int   $key
688
+	 * @param  mixed $value
689
+	 * @param  array $attributes
690
+	 * @return array
691
+	 */
692
+	protected function getAttachId($key, $value, array $attributes)
693
+	{
694
+		if (is_array($value)) {
695
+			return [$key, array_merge($value, $attributes)];
696
+		}
697
+
698
+		return [$value, $attributes];
699
+	}
700
+
701
+	/**
702
+	 * Create a new pivot attachment record.
703
+	 *
704
+	 * @param  int  $id
705
+	 * @param  bool $timed
706
+	 * @return array
707
+	 */
708
+	protected function createAttachRecord($id, $timed)
709
+	{
710
+		$parentKey = $this->parentMap->getKeyName();
711
+
712
+		$record = [];
713
+
714
+		$record[$this->foreignKey] = $this->parent->getEntityAttribute($parentKey);
715
+
716
+		$record[$this->otherKey] = $id;
717
+
718
+		// If the record needs to have creation and update timestamps, we will make
719
+		// them by calling the parent model's "freshTimestamp" method which will
720
+		// provide us with a fresh timestamp in this model's preferred format.
721
+		if ($timed) {
722
+			$record = $this->setTimestampsOnAttach($record);
723
+		}
724
+
725
+		return $record;
726
+	}
727
+
728
+	/**
729
+	 * Set the creation and update timestamps on an attach record.
730
+	 *
731
+	 * @param  array $record
732
+	 * @param  bool  $exists
733
+	 * @return array
734
+	 */
735
+	protected function setTimestampsOnAttach(array $record, $exists = false)
736
+	{
737
+		$fresh = $this->freshTimestamp();
738
+
739
+		if (!$exists) {
740
+			$record[$this->createdAt()] = $fresh;
741
+		}
742
+
743
+		$record[$this->updatedAt()] = $fresh;
744
+
745
+		return $record;
746
+	}
747
+
748
+	/**
749
+	 * @param EntityCollection $entities
750
+	 * @return array
751
+	 */
752
+	protected function getModelKeysFromCollection(EntityCollection $entities)
753
+	{
754
+		$keyName = $this->relatedMap->getKeyName();
755
+
756
+		return array_map(function ($m) use ($keyName) {
757
+			return $m->$keyName;
758
+		}, $entities);
759
+	}
760
+
761
+	/**
762
+	 * Detach models from the relationship.
763
+	 *
764
+	 * @param  int|array $ids
765
+	 * @throws \InvalidArgumentException
766
+	 * @return int
767
+	 */
768
+	public function detach($ids = [])
769
+	{
770
+		if ($ids instanceof EntityCollection) {
771
+			$ids = (array) $ids->modelKeys();
772
+		}
773
+
774
+		$query = $this->newPivotQuery();
775
+
776
+		// If associated IDs were passed to the method we will only delete those
777
+		// associations, otherwise all of the association ties will be broken.
778
+		// We'll return the numbers of affected rows when we do the deletes.
779
+		$ids = (array) $ids;
780
+
781
+		if (count($ids) > 0) {
782
+			$query->whereIn($this->otherKey, (array) $ids);
783
+		}
784
+
785
+		// Once we have all of the conditions set on the statement, we are ready
786
+		// to run the delete on the pivot table. Then, if the touch parameter
787
+		// is true, we will go ahead and touch all related models to sync.
788
+		return $query->delete();
789
+	}
790 790
     
791
-    /**
792
-     * Create a new query builder for the pivot table.
793
-     *
794
-     * @throws \InvalidArgumentException
795
-     *
796
-     * @return \Illuminate\Database\Query\Builder
797
-     */
798
-    protected function newPivotQuery()
799
-    {
800
-        $query = $this->newPivotStatement();
801
-
802
-        $parentKey = $this->parentMap->getKeyName();
803
-
804
-        return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey));
805
-    }
806
-
807
-    /**
808
-     * Get a new plain query builder for the pivot table.
809
-     *
810
-     * @return \Illuminate\Database\Query\Builder
811
-     */
812
-    public function newPivotStatement()
813
-    {
814
-        return $this->query->getQuery()->newQuery()->from($this->table);
815
-    }
816
-
817
-    /**
818
-     * Get a new pivot statement for a given "other" ID.
819
-     *
820
-     * @param  mixed $id
821
-     *
822
-     * @throws \InvalidArgumentException
823
-     *
824
-     * @return \Illuminate\Database\Query\Builder
825
-     */
826
-    public function newPivotStatementForId($id)
827
-    {
828
-        $pivot = $this->newPivotStatement();
829
-
830
-        $parentKeyName = $this->parentMap->getKeyName();
831
-
832
-        $key = $this->parent->getEntityAttribute($parentKeyName);
833
-
834
-        return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id);
835
-    }
836
-
837
-    /**
838
-     * Create a new pivot model instance.
839
-     *
840
-     * @param  array $attributes
841
-     * @param  bool  $exists
842
-     * @return \Analogue\ORM\Relationships\Pivot
843
-     */
844
-    public function newPivot(array $attributes = [], $exists = false)
845
-    {
846
-        $pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
791
+	/**
792
+	 * Create a new query builder for the pivot table.
793
+	 *
794
+	 * @throws \InvalidArgumentException
795
+	 *
796
+	 * @return \Illuminate\Database\Query\Builder
797
+	 */
798
+	protected function newPivotQuery()
799
+	{
800
+		$query = $this->newPivotStatement();
801
+
802
+		$parentKey = $this->parentMap->getKeyName();
803
+
804
+		return $query->where($this->foreignKey, $this->parent->getEntityAttribute($parentKey));
805
+	}
806
+
807
+	/**
808
+	 * Get a new plain query builder for the pivot table.
809
+	 *
810
+	 * @return \Illuminate\Database\Query\Builder
811
+	 */
812
+	public function newPivotStatement()
813
+	{
814
+		return $this->query->getQuery()->newQuery()->from($this->table);
815
+	}
816
+
817
+	/**
818
+	 * Get a new pivot statement for a given "other" ID.
819
+	 *
820
+	 * @param  mixed $id
821
+	 *
822
+	 * @throws \InvalidArgumentException
823
+	 *
824
+	 * @return \Illuminate\Database\Query\Builder
825
+	 */
826
+	public function newPivotStatementForId($id)
827
+	{
828
+		$pivot = $this->newPivotStatement();
829
+
830
+		$parentKeyName = $this->parentMap->getKeyName();
831
+
832
+		$key = $this->parent->getEntityAttribute($parentKeyName);
833
+
834
+		return $pivot->where($this->foreignKey, $key)->where($this->otherKey, $id);
835
+	}
836
+
837
+	/**
838
+	 * Create a new pivot model instance.
839
+	 *
840
+	 * @param  array $attributes
841
+	 * @param  bool  $exists
842
+	 * @return \Analogue\ORM\Relationships\Pivot
843
+	 */
844
+	public function newPivot(array $attributes = [], $exists = false)
845
+	{
846
+		$pivot = new Pivot($this->parent, $this->parentMap, $attributes, $this->table, $exists);
847 847
         
848
-        return $pivot->setPivotKeys($this->foreignKey, $this->otherKey);
849
-    }
850
-
851
-    /**
852
-     * Create a new existing pivot model instance.
853
-     *
854
-     * @param  array $attributes
855
-     * @return \Analogue\ORM\Relationships\Pivot
856
-     */
857
-    public function newExistingPivot(array $attributes = [])
858
-    {
859
-        return $this->newPivot($attributes, true);
860
-    }
861
-
862
-    /**
863
-     * Set the columns on the pivot table to retrieve.
864
-     *
865
-     * @param  array $columns
866
-     * @return $this
867
-     */
868
-    public function withPivot($columns)
869
-    {
870
-        $columns = is_array($columns) ? $columns : func_get_args();
871
-
872
-        $this->pivotColumns = array_merge($this->pivotColumns, $columns);
873
-
874
-        return $this;
875
-    }
876
-
877
-    /**
878
-     * Specify that the pivot table has creation and update timestamps.
879
-     *
880
-     * @param  mixed $createdAt
881
-     * @param  mixed $updatedAt
882
-     * @return \Analogue\ORM\Relationships\BelongsToMany
883
-     */
884
-    public function withTimestamps($createdAt = null, $updatedAt = null)
885
-    {
886
-        return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt());
887
-    }
888
-
889
-    /**
890
-     * Get the key for comparing against the parent key in "has" query.
891
-     *
892
-     * @return string
893
-     */
894
-    public function getHasCompareKey()
895
-    {
896
-        return $this->getForeignKey();
897
-    }
898
-
899
-    /**
900
-     * Get the fully qualified foreign key for the relation.
901
-     *
902
-     * @return string
903
-     */
904
-    public function getForeignKey()
905
-    {
906
-        return $this->table . '.' . $this->foreignKey;
907
-    }
908
-
909
-    /**
910
-     * Get the fully qualified "other key" for the relation.
911
-     *
912
-     * @return string
913
-     */
914
-    public function getOtherKey()
915
-    {
916
-        return $this->table . '.' . $this->otherKey;
917
-    }
918
-
919
-    /**
920
-     * Get the fully qualified parent key name.
921
-     *
922
-     * @return string
923
-     */
924
-    protected function getQualifiedParentKeyName()
925
-    {
926
-        return $this->parentMap->getQualifiedKeyName();
927
-    }
928
-
929
-    /**
930
-     * Get the intermediate table for the relationship.
931
-     *
932
-     * @return string
933
-     */
934
-    public function getTable()
935
-    {
936
-        return $this->table;
937
-    }
938
-
939
-    /**
940
-     * Get the relationship name for the relationship.
941
-     *
942
-     * @return string
943
-     */
944
-    public function getRelationName()
945
-    {
946
-        return $this->relationName;
947
-    }
848
+		return $pivot->setPivotKeys($this->foreignKey, $this->otherKey);
849
+	}
850
+
851
+	/**
852
+	 * Create a new existing pivot model instance.
853
+	 *
854
+	 * @param  array $attributes
855
+	 * @return \Analogue\ORM\Relationships\Pivot
856
+	 */
857
+	public function newExistingPivot(array $attributes = [])
858
+	{
859
+		return $this->newPivot($attributes, true);
860
+	}
861
+
862
+	/**
863
+	 * Set the columns on the pivot table to retrieve.
864
+	 *
865
+	 * @param  array $columns
866
+	 * @return $this
867
+	 */
868
+	public function withPivot($columns)
869
+	{
870
+		$columns = is_array($columns) ? $columns : func_get_args();
871
+
872
+		$this->pivotColumns = array_merge($this->pivotColumns, $columns);
873
+
874
+		return $this;
875
+	}
876
+
877
+	/**
878
+	 * Specify that the pivot table has creation and update timestamps.
879
+	 *
880
+	 * @param  mixed $createdAt
881
+	 * @param  mixed $updatedAt
882
+	 * @return \Analogue\ORM\Relationships\BelongsToMany
883
+	 */
884
+	public function withTimestamps($createdAt = null, $updatedAt = null)
885
+	{
886
+		return $this->withPivot($createdAt ?: $this->createdAt(), $updatedAt ?: $this->updatedAt());
887
+	}
888
+
889
+	/**
890
+	 * Get the key for comparing against the parent key in "has" query.
891
+	 *
892
+	 * @return string
893
+	 */
894
+	public function getHasCompareKey()
895
+	{
896
+		return $this->getForeignKey();
897
+	}
898
+
899
+	/**
900
+	 * Get the fully qualified foreign key for the relation.
901
+	 *
902
+	 * @return string
903
+	 */
904
+	public function getForeignKey()
905
+	{
906
+		return $this->table . '.' . $this->foreignKey;
907
+	}
908
+
909
+	/**
910
+	 * Get the fully qualified "other key" for the relation.
911
+	 *
912
+	 * @return string
913
+	 */
914
+	public function getOtherKey()
915
+	{
916
+		return $this->table . '.' . $this->otherKey;
917
+	}
918
+
919
+	/**
920
+	 * Get the fully qualified parent key name.
921
+	 *
922
+	 * @return string
923
+	 */
924
+	protected function getQualifiedParentKeyName()
925
+	{
926
+		return $this->parentMap->getQualifiedKeyName();
927
+	}
928
+
929
+	/**
930
+	 * Get the intermediate table for the relationship.
931
+	 *
932
+	 * @return string
933
+	 */
934
+	public function getTable()
935
+	{
936
+		return $this->table;
937
+	}
938
+
939
+	/**
940
+	 * Get the relationship name for the relationship.
941
+	 *
942
+	 * @return string
943
+	 */
944
+	public function getRelationName()
945
+	{
946
+		return $this->relationName;
947
+	}
948 948
 }
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,1016 +15,1016 @@
 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
-        }
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 237
         
238
-        if (is_array($value) || $value instanceof Collection) {
239
-            $this->needSync[] = $relation;
240
-        }
241
-
242
-        // If the relation is a proxy, we test is the relation
243
-        // has been lazy loaded, otherwise we'll just treat
244
-        // the subset of newly added items.
245
-        if ($value instanceof CollectionProxy && $value->isLoaded()) {
246
-            $this->needSync[] = $relation;
247
-            $value = $value->getUnderlyingCollection();
248
-        }
249
-
250
-        if ($value instanceof CollectionProxy && !$value->isLoaded()) {
251
-            $value = $value->getAddedItems();
252
-        }
253
-
254
-        // At this point $value should be either an array or an instance
255
-        // of a collection class.
256
-        if (!is_array($value) && !$value instanceof Collection) {
257
-            throw new MappingException("'$relation' attribute should be array() or Collection");
258
-        }
259
-
260
-        $this->relationships[$relation] = $this->createSubAggregates($value, $relation);
238
+		if (is_array($value) || $value instanceof Collection) {
239
+			$this->needSync[] = $relation;
240
+		}
241
+
242
+		// If the relation is a proxy, we test is the relation
243
+		// has been lazy loaded, otherwise we'll just treat
244
+		// the subset of newly added items.
245
+		if ($value instanceof CollectionProxy && $value->isLoaded()) {
246
+			$this->needSync[] = $relation;
247
+			$value = $value->getUnderlyingCollection();
248
+		}
249
+
250
+		if ($value instanceof CollectionProxy && !$value->isLoaded()) {
251
+			$value = $value->getAddedItems();
252
+		}
253
+
254
+		// At this point $value should be either an array or an instance
255
+		// of a collection class.
256
+		if (!is_array($value) && !$value instanceof Collection) {
257
+			throw new MappingException("'$relation' attribute should be array() or Collection");
258
+		}
259
+
260
+		$this->relationships[$relation] = $this->createSubAggregates($value, $relation);
261 261
         
262
-        return true;
263
-    }
264
-
265
-    /**
266
-     * Return Entity's relationship attribute
267
-     *
268
-     * @param  string $relation
269
-     * @throws MappingException
270
-     * @return mixed
271
-     */
272
-    protected function getRelationshipValue($relation)
273
-    {
274
-        $value = $this->getEntityAttribute($relation);
275
-        //if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
276
-        if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
277
-            throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
278
-        }
279
-
280
-        return $value;
281
-    }
282
-
283
-    /**
284
-     * Create a child, aggregated entity
285
-     *
286
-     * @param  mixed $entities
287
-     * @param string $relation
288
-     * @return array
289
-     */
290
-    protected function createSubAggregates($entities, $relation)
291
-    {
292
-        $aggregates = [];
293
-
294
-        foreach ($entities as $entity) {
295
-            $aggregates[] = $this->createSubAggregate($entity, $relation);
296
-        }
297
-
298
-        return $aggregates;
299
-    }
300
-
301
-    /**
302
-     * Create a related subAggregate
303
-     *
304
-     * @param  mixed $entity
305
-     * @param  string $relation
306
-     * @throws MappingException
307
-     * @return self
308
-     */
309
-    protected function createSubAggregate($entity, $relation)
310
-    {
311
-        // If root isn't defined, then this is the Aggregate Root
312
-        if (is_null($this->root)) {
313
-            $root = $this;
314
-        } else {
315
-            $root = $this->root;
316
-        }
317
-
318
-        return new self($entity, $this, $relation, $root);
319
-    }
320
-
321
-    /**
322
-     * Get the Entity's primary key attribute
323
-     *
324
-     * @return string|integer
325
-     */
326
-    public function getEntityId()
327
-    {
328
-        return $this->wrappedEntity->getEntityAttribute($this->entityMap->getKeyName());
329
-    }
330
-
331
-    /**
332
-     * Get the name of the primary key
333
-     *
334
-     * @return string
335
-     */
336
-    public function getEntityKey()
337
-    {
338
-        return $this->entityMap->getKeyName();
339
-    }
340
-
341
-    /**
342
-     * Return the entity map for the current entity
343
-     *
344
-     * @return \Analogue\ORM\EntityMap
345
-     */
346
-    public function getEntityMap()
347
-    {
348
-        return $this->entityMap;
349
-    }
350
-
351
-    /**
352
-     * Return the Entity's hash $class.$id
353
-     *
354
-     * @return string
355
-     */
356
-    public function getEntityHash()
357
-    {
358
-        return $this->getEntityClass() . '.' . $this->getEntityId();
359
-    }
360
-
361
-    /**
362
-     * Get wrapped entity class
363
-     *
364
-     * @return string
365
-     */
366
-    public function getEntityClass()
367
-    {
368
-        return $this->entityMap->getClass();
369
-    }
370
-
371
-    /**
372
-     * Return the Mapper's entity cache
373
-     *
374
-     * @return \Analogue\ORM\System\EntityCache
375
-     */
376
-    protected function getEntityCache()
377
-    {
378
-        return $this->mapper->getEntityCache();
379
-    }
380
-
381
-    /**
382
-     * Get a relationship as an aggregated entities' array
383
-     *
384
-     * @param  string $name
385
-     * @return array
386
-     */
387
-    public function getRelationship($name)
388
-    {
389
-        if (array_key_exists($name, $this->relationships)) {
390
-            return $this->relationships[$name];
391
-        } else {
392
-            return [];
393
-        }
394
-    }
395
-
396
-    /**
397
-     * [TO IMPLEMENT]
398
-     *
399
-     * @return array
400
-     */
401
-    public function getPivotAttributes()
402
-    {
403
-        return [];
404
-    }
405
-
406
-    /**
407
-     * Get Non existing related entities from several relationships
408
-     *
409
-     * @param  array $relationships
410
-     * @return array
411
-     */
412
-    public function getNonExistingRelated(array $relationships)
413
-    {
414
-        $nonExisting = [];
415
-
416
-        foreach ($relationships as $relation) {
417
-            if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
418
-                $nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
419
-            }
420
-        }
421
-
422
-        return $nonExisting;
423
-    }
424
-
425
-    /**
426
-     * Get non-existing related entities from a single relation
427
-     *
428
-     * @param  string $relation
429
-     * @return array
430
-     */
431
-    protected function getNonExistingFromRelation($relation)
432
-    {
433
-        $nonExisting = [];
434
-
435
-        foreach ($this->relationships[$relation] as $aggregate) {
436
-            if (!$aggregate->exists()) {
437
-                $nonExisting[] = $aggregate;
438
-            }
439
-        }
440
-
441
-        return $nonExisting;
442
-    }
443
-
444
-    /**
445
-     * Synchronize relationships if needed
446
-     */
447
-    public function syncRelationships(array $relationships)
448
-    {
449
-        if ($this->exists()) {
450
-            foreach ($relationships as $relation) {
451
-                if (in_array($relation, $this->needSync)) {
452
-                    $this->synchronize($relation);
453
-                }
454
-            }
455
-        }
456
-    }
457
-
458
-    /**
459
-     * Synchronize a relationship attribute
460
-     *
461
-     * @param $relation
462
-     */
463
-    protected function synchronize($relation)
464
-    {
465
-        $actualContent = $this->relationships[$relation];
466
-
467
-        $this->entityMap->$relation($this->getEntityObject())->sync($actualContent);
468
-    }
469
-
470
-    /**
471
-     * Returns an array of Missing related Entities for the
472
-     * given $relation
473
-     *
474
-     * @param  string $relation
475
-     * @return array
476
-     */
477
-    public function getMissingEntities($relation)
478
-    {
479
-        $cachedRelations = $this->getCachedAttribute($relation);
480
-
481
-        if (!is_null($cachedRelations)) {
482
-            $missing = [];
483
-
484
-            foreach ($cachedRelations as $hash) {
485
-                if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
486
-                    $missing[] = $hash;
487
-                }
488
-            }
489
-
490
-            return $missing;
491
-        } else {
492
-            return [];
493
-        }
494
-    }
262
+		return true;
263
+	}
264
+
265
+	/**
266
+	 * Return Entity's relationship attribute
267
+	 *
268
+	 * @param  string $relation
269
+	 * @throws MappingException
270
+	 * @return mixed
271
+	 */
272
+	protected function getRelationshipValue($relation)
273
+	{
274
+		$value = $this->getEntityAttribute($relation);
275
+		//if($relation == "role") tdd($this->wrappedEntity->getEntityAttributes());
276
+		if (is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
277
+			throw new MappingException("Entity's attribute $relation should be array, object, collection or null");
278
+		}
279
+
280
+		return $value;
281
+	}
282
+
283
+	/**
284
+	 * Create a child, aggregated entity
285
+	 *
286
+	 * @param  mixed $entities
287
+	 * @param string $relation
288
+	 * @return array
289
+	 */
290
+	protected function createSubAggregates($entities, $relation)
291
+	{
292
+		$aggregates = [];
293
+
294
+		foreach ($entities as $entity) {
295
+			$aggregates[] = $this->createSubAggregate($entity, $relation);
296
+		}
297
+
298
+		return $aggregates;
299
+	}
300
+
301
+	/**
302
+	 * Create a related subAggregate
303
+	 *
304
+	 * @param  mixed $entity
305
+	 * @param  string $relation
306
+	 * @throws MappingException
307
+	 * @return self
308
+	 */
309
+	protected function createSubAggregate($entity, $relation)
310
+	{
311
+		// If root isn't defined, then this is the Aggregate Root
312
+		if (is_null($this->root)) {
313
+			$root = $this;
314
+		} else {
315
+			$root = $this->root;
316
+		}
317
+
318
+		return new self($entity, $this, $relation, $root);
319
+	}
320
+
321
+	/**
322
+	 * Get the Entity's primary key attribute
323
+	 *
324
+	 * @return string|integer
325
+	 */
326
+	public function getEntityId()
327
+	{
328
+		return $this->wrappedEntity->getEntityAttribute($this->entityMap->getKeyName());
329
+	}
330
+
331
+	/**
332
+	 * Get the name of the primary key
333
+	 *
334
+	 * @return string
335
+	 */
336
+	public function getEntityKey()
337
+	{
338
+		return $this->entityMap->getKeyName();
339
+	}
340
+
341
+	/**
342
+	 * Return the entity map for the current entity
343
+	 *
344
+	 * @return \Analogue\ORM\EntityMap
345
+	 */
346
+	public function getEntityMap()
347
+	{
348
+		return $this->entityMap;
349
+	}
350
+
351
+	/**
352
+	 * Return the Entity's hash $class.$id
353
+	 *
354
+	 * @return string
355
+	 */
356
+	public function getEntityHash()
357
+	{
358
+		return $this->getEntityClass() . '.' . $this->getEntityId();
359
+	}
360
+
361
+	/**
362
+	 * Get wrapped entity class
363
+	 *
364
+	 * @return string
365
+	 */
366
+	public function getEntityClass()
367
+	{
368
+		return $this->entityMap->getClass();
369
+	}
370
+
371
+	/**
372
+	 * Return the Mapper's entity cache
373
+	 *
374
+	 * @return \Analogue\ORM\System\EntityCache
375
+	 */
376
+	protected function getEntityCache()
377
+	{
378
+		return $this->mapper->getEntityCache();
379
+	}
380
+
381
+	/**
382
+	 * Get a relationship as an aggregated entities' array
383
+	 *
384
+	 * @param  string $name
385
+	 * @return array
386
+	 */
387
+	public function getRelationship($name)
388
+	{
389
+		if (array_key_exists($name, $this->relationships)) {
390
+			return $this->relationships[$name];
391
+		} else {
392
+			return [];
393
+		}
394
+	}
395
+
396
+	/**
397
+	 * [TO IMPLEMENT]
398
+	 *
399
+	 * @return array
400
+	 */
401
+	public function getPivotAttributes()
402
+	{
403
+		return [];
404
+	}
405
+
406
+	/**
407
+	 * Get Non existing related entities from several relationships
408
+	 *
409
+	 * @param  array $relationships
410
+	 * @return array
411
+	 */
412
+	public function getNonExistingRelated(array $relationships)
413
+	{
414
+		$nonExisting = [];
415
+
416
+		foreach ($relationships as $relation) {
417
+			if ($this->hasAttribute($relation) && array_key_exists($relation, $this->relationships)) {
418
+				$nonExisting = array_merge($nonExisting, $this->getNonExistingFromRelation($relation));
419
+			}
420
+		}
421
+
422
+		return $nonExisting;
423
+	}
424
+
425
+	/**
426
+	 * Get non-existing related entities from a single relation
427
+	 *
428
+	 * @param  string $relation
429
+	 * @return array
430
+	 */
431
+	protected function getNonExistingFromRelation($relation)
432
+	{
433
+		$nonExisting = [];
434
+
435
+		foreach ($this->relationships[$relation] as $aggregate) {
436
+			if (!$aggregate->exists()) {
437
+				$nonExisting[] = $aggregate;
438
+			}
439
+		}
440
+
441
+		return $nonExisting;
442
+	}
443
+
444
+	/**
445
+	 * Synchronize relationships if needed
446
+	 */
447
+	public function syncRelationships(array $relationships)
448
+	{
449
+		if ($this->exists()) {
450
+			foreach ($relationships as $relation) {
451
+				if (in_array($relation, $this->needSync)) {
452
+					$this->synchronize($relation);
453
+				}
454
+			}
455
+		}
456
+	}
457
+
458
+	/**
459
+	 * Synchronize a relationship attribute
460
+	 *
461
+	 * @param $relation
462
+	 */
463
+	protected function synchronize($relation)
464
+	{
465
+		$actualContent = $this->relationships[$relation];
466
+
467
+		$this->entityMap->$relation($this->getEntityObject())->sync($actualContent);
468
+	}
469
+
470
+	/**
471
+	 * Returns an array of Missing related Entities for the
472
+	 * given $relation
473
+	 *
474
+	 * @param  string $relation
475
+	 * @return array
476
+	 */
477
+	public function getMissingEntities($relation)
478
+	{
479
+		$cachedRelations = $this->getCachedAttribute($relation);
480
+
481
+		if (!is_null($cachedRelations)) {
482
+			$missing = [];
483
+
484
+			foreach ($cachedRelations as $hash) {
485
+				if (!$this->getRelatedAggregateFromHash($hash, $relation)) {
486
+					$missing[] = $hash;
487
+				}
488
+			}
489
+
490
+			return $missing;
491
+		} else {
492
+			return [];
493
+		}
494
+	}
495 495
        
496
-    /**
497
-     * Get Relationships who have dirty attributes / dirty relationships
498
-     *
499
-     * @return array
500
-     */
501
-    public function getDirtyRelationships()
502
-    {
503
-        $dirtyAggregates = [];
504
-
505
-        foreach ($this->relationships as $relation) {
506
-            foreach ($relation as $aggregate) {
507
-                if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships() > 0)) {
508
-                    $dirtyAggregates[] = $aggregate;
509
-                }
510
-            }
511
-        }
512
-
513
-        return $dirtyAggregates;
514
-    }
496
+	/**
497
+	 * Get Relationships who have dirty attributes / dirty relationships
498
+	 *
499
+	 * @return array
500
+	 */
501
+	public function getDirtyRelationships()
502
+	{
503
+		$dirtyAggregates = [];
504
+
505
+		foreach ($this->relationships as $relation) {
506
+			foreach ($relation as $aggregate) {
507
+				if (!$aggregate->exists() || $aggregate->isDirty() || count($aggregate->getDirtyRelationships() > 0)) {
508
+					$dirtyAggregates[] = $aggregate;
509
+				}
510
+			}
511
+		}
512
+
513
+		return $dirtyAggregates;
514
+	}
515 515
     
516
-    /**
517
-     * Compare the object's raw attributes with the record in cache
518
-     *
519
-     * @return boolean
520
-     */
521
-    public function isDirty()
522
-    {
523
-        if (count($this->getDirtyRawAttributes()) > 0) {
524
-            return true;
525
-        } else {
526
-            return false;
527
-        }
528
-    }
529
-
530
-    /**
531
-     * Get Raw Entity's attributes, as they are represented
532
-     * in the database, including value objects & foreign keys
533
-     *
534
-     * @return array
535
-     */
536
-    public function getRawAttributes()
537
-    {
538
-        $attributes = $this->wrappedEntity->getEntityAttributes();
539
-
540
-        foreach ($this->entityMap->getRelationships() as $relation) {
541
-            unset($attributes[$relation]);
542
-        }
543
-
544
-        $attributes = $this->flattenEmbeddables($attributes);
545
-
546
-        $foreignKeys = $this->getForeignKeyAttributes();
547
-
548
-        return $attributes + $foreignKeys;
549
-    }
550
-
551
-    /**
552
-     * Convert Value Objects to raw db attributes
553
-     *
554
-     * @param  array $attributes
555
-     * @return array
556
-     */
557
-    protected function flattenEmbeddables($attributes)
558
-    {
559
-        $embeddables = $this->entityMap->getEmbeddables();
516
+	/**
517
+	 * Compare the object's raw attributes with the record in cache
518
+	 *
519
+	 * @return boolean
520
+	 */
521
+	public function isDirty()
522
+	{
523
+		if (count($this->getDirtyRawAttributes()) > 0) {
524
+			return true;
525
+		} else {
526
+			return false;
527
+		}
528
+	}
529
+
530
+	/**
531
+	 * Get Raw Entity's attributes, as they are represented
532
+	 * in the database, including value objects & foreign keys
533
+	 *
534
+	 * @return array
535
+	 */
536
+	public function getRawAttributes()
537
+	{
538
+		$attributes = $this->wrappedEntity->getEntityAttributes();
539
+
540
+		foreach ($this->entityMap->getRelationships() as $relation) {
541
+			unset($attributes[$relation]);
542
+		}
543
+
544
+		$attributes = $this->flattenEmbeddables($attributes);
545
+
546
+		$foreignKeys = $this->getForeignKeyAttributes();
547
+
548
+		return $attributes + $foreignKeys;
549
+	}
550
+
551
+	/**
552
+	 * Convert Value Objects to raw db attributes
553
+	 *
554
+	 * @param  array $attributes
555
+	 * @return array
556
+	 */
557
+	protected function flattenEmbeddables($attributes)
558
+	{
559
+		$embeddables = $this->entityMap->getEmbeddables();
560 560
         
561
-        foreach ($embeddables as $localKey => $embed) {
562
-            // Retrieve the value object from the entity's attributes
563
-            $valueObject = $attributes[$localKey];
561
+		foreach ($embeddables as $localKey => $embed) {
562
+			// Retrieve the value object from the entity's attributes
563
+			$valueObject = $attributes[$localKey];
564 564
 
565
-            // Unset the corresponding key
566
-            unset($attributes[$localKey]);
565
+			// Unset the corresponding key
566
+			unset($attributes[$localKey]);
567 567
 
568
-            // TODO Make wrapper object compatible with value objects
569
-            $valueObjectAttributes = $valueObject->getEntityAttributes();
568
+			// TODO Make wrapper object compatible with value objects
569
+			$valueObjectAttributes = $valueObject->getEntityAttributes();
570 570
 
571
-            // Now (if setup in the entity map) we prefix the value object's
572
-            // attributes with the snake_case name of the embedded class.
573
-            $prefix = snake_case(class_basename($embed));
571
+			// Now (if setup in the entity map) we prefix the value object's
572
+			// attributes with the snake_case name of the embedded class.
573
+			$prefix = snake_case(class_basename($embed));
574 574
 
575
-            foreach ($valueObjectAttributes as $key=>$value) {
576
-                $valueObjectAttributes[$prefix . '_' . $key] = $value;
577
-                unset($valueObjectAttributes[$key]);
578
-            }
575
+			foreach ($valueObjectAttributes as $key=>$value) {
576
+				$valueObjectAttributes[$prefix . '_' . $key] = $value;
577
+				unset($valueObjectAttributes[$key]);
578
+			}
579 579
 
580
-            $attributes = array_merge($attributes, $valueObjectAttributes);
581
-        }
580
+			$attributes = array_merge($attributes, $valueObjectAttributes);
581
+		}
582 582
         
583
-        return $attributes;
584
-    }
585
-
586
-    /**
587
-     * Return's entity raw attributes in the state they were at last
588
-     * query.
589
-     *
590
-     * @param  array|null $columns
591
-     * @return array
592
-     */
593
-    protected function getCachedRawAttributes(array $columns = null)
594
-    {
595
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
596
-
597
-        if (is_null($columns)) {
598
-            return $cachedAttributes;
599
-        } else {
600
-            return array_only($cachedAttributes, $columns);
601
-        }
602
-    }
603
-
604
-    /**
605
-     * Return a single attribute from the cache
606
-     * @param  string $key
607
-     * @return mixed
608
-     */
609
-    protected function getCachedAttribute($key)
610
-    {
611
-        $cachedAttributes = $this->getCache()->get($this->getEntityId());
612
-
613
-        if (!array_key_exists($key, $cachedAttributes)) {
614
-            return null;
615
-        } else {
616
-            return $cachedAttributes[$key];
617
-        }
618
-    }
619
-
620
-    /**
621
-     * Convert related Entity's attributes to foreign keys
622
-     *
623
-     * @return array
624
-     */
625
-    protected function getForeignKeyAttributes()
626
-    {
627
-        $foreignKeys = [];
628
-
629
-        foreach ($this->entityMap->getLocalRelationships() as $relation) {
630
-            // check if relationship has been parsed, meaning it has an actual object
631
-            // in the entity's attributes
632
-            if ($this->isActualRelationships($relation)) {
633
-                $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
634
-            }
635
-        }
636
-
637
-        if (!is_null($this->parent)) {
638
-            $foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
639
-        }
640
-
641
-        return $foreignKeys;
642
-    }
643
-
644
-    /**
645
-     * Return an associative array containing the key-value pair(s) from
646
-     * the related entity.
647
-     *
648
-     * @param  string $relation
649
-     * @return array
650
-     */
651
-    protected function getForeignKeyAttributesFromRelation($relation)
652
-    {
653
-        $localRelations = $this->entityMap->getLocalRelationships();
654
-
655
-        if (in_array($relation, $localRelations)) {
656
-            // Call Relationship's method
657
-            $relationship = $this->entityMap->$relation($this->getEntityObject());
658
-
659
-            $relatedAggregate = $this->relationships[$relation][0];
660
-
661
-            return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
662
-        } else {
663
-            return [];
664
-        }
665
-    }
666
-
667
-    /**
668
-     * Get foreign key attribute(s) from a parent entity in this
669
-     * aggregate context
670
-     *
671
-     * @return array
672
-     */
673
-    protected function getForeignKeyAttributesFromParent()
674
-    {
675
-        $parentMap = $this->parent->getEntityMap();
676
-
677
-        $parentForeignRelations = $parentMap->getForeignRelationships();
678
-        $parentPivotRelations = $parentMap->getPivotRelationships();
679
-
680
-        $parentRelation = $this->parentRelationship;
681
-
682
-        if (in_array($parentRelation, $parentForeignRelations)
683
-            && !in_array($parentRelation, $parentPivotRelations)
684
-        ) {
685
-            $parentObject = $this->parent->getEntityObject();
686
-
687
-            // Call Relationship's method on parent map
688
-            $relationship = $parentMap->$parentRelation($parentObject);
689
-
690
-            return $relationship->getForeignKeyValuePair();
691
-        } else {
692
-            return [];
693
-        }
694
-    }
695
-
696
-    /**
697
-     * Update Pivot records on loaded relationships, by comparing the
698
-     * values from the Entity Cache to the actual relationship inside
699
-     * the aggregated entity.
700
-     *
701
-     * @return void
702
-     */
703
-    public function updatePivotRecords()
704
-    {
705
-        $pivots = $this->entityMap->getPivotRelationships();
706
-
707
-        foreach ($pivots as $pivot) {
708
-            if (array_key_exists($pivot, $this->relationships)) {
709
-                $this->updatePivotRelation($pivot);
710
-            }
711
-        }
712
-    }
713
-
714
-    /**
715
-     * Update Single pivot relationship
716
-     *
717
-     * @param  string $relation
718
-     * @return void
719
-     */
720
-    protected function updatePivotRelation($relation)
721
-    {
722
-        $hashes = $this->getEntityHashesFromRelation($relation);
723
-
724
-        $cachedAttributes = $this->getCachedRawAttributes();
725
-
726
-        if (array_key_exists($relation, $cachedAttributes)) {
727
-            // Compare the two array of hashes to find out existing
728
-            // pivot records, and the ones to be created.
729
-            $new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
730
-            $existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
731
-        } else {
732
-            $existing = [];
733
-            $new = $hashes;
734
-        }
735
-
736
-        if (count($new) > 0) {
737
-            $pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
738
-
739
-            $this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
740
-        }
741
-
742
-        if (count($existing) > 0) {
743
-            foreach ($existing as $pivotHash) {
744
-                $this->updatePivotIfDirty($pivotHash, $relation);
745
-            }
746
-        }
747
-    }
748
-
749
-    /**
750
-     * Compare existing pivot record in cache and update it
751
-     * if the pivot attributes are dirty
752
-     *
753
-     * @param  string $pivotHash
754
-     * @param  string $relation
755
-     * @return void
756
-     */
757
-    protected function updatePivotIfDirty($pivotHash, $relation)
758
-    {
759
-        $aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
760
-
761
-        if ($aggregate->hasAttribute('pivot')) {
762
-            $pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
763
-
764
-            $cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
765
-
766
-            $actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
767
-
768
-            $dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
769
-
770
-            if (count($dirty) > 0) {
771
-                $id = $aggregate->getEntityId();
772
-
773
-                $this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
774
-            }
775
-        }
776
-    }
777
-
778
-    /**
779
-     * Compare two attributes array and return dirty attributes
780
-     *
781
-     * @param  array $actual
782
-     * @param  array $cached
783
-     * @return array
784
-     */
785
-    protected function getDirtyAttributes(array $actual, array $cached)
786
-    {
787
-        $dirty = [];
788
-
789
-        foreach ($actual as $key => $value) {
790
-            if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
791
-                $dirty[$key] = $actual[$key];
792
-            }
793
-        }
794
-
795
-        return $dirty;
796
-    }
797
-
798
-    /**
799
-     *
800
-     * @param  string $pivotHash
801
-     * @param  string $relation
802
-     * @return array
803
-     */
804
-    protected function getPivotAttributesFromCache($pivotHash, $relation)
805
-    {
806
-        $cachedAttributes = $this->getCachedRawAttributes();
807
-
808
-        $cachedRelations = $cachedAttributes[$relation];
809
-
810
-        foreach ($cachedRelations as $cachedRelation) {
811
-            if ($cachedRelation == $pivotHash) {
812
-                return $cachedRelation->getPivotAttributes();
813
-            }
814
-        }
815
-    }
816
-
817
-    /**
818
-     * Returns an array of related Aggregates from its entity hashes
819
-     *
820
-     * @param  array  $hashes
821
-     * @param  string $relation
822
-     * @return array
823
-     */
824
-    protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
825
-    {
826
-        $related = [];
827
-
828
-        foreach ($hashes as $hash) {
829
-            $aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
830
-
831
-            if (!is_null($aggregate)) {
832
-                $related[] = $aggregate;
833
-            }
834
-        }
835
-
836
-        return $related;
837
-    }
838
-
839
-    /**
840
-     * Get related aggregate from its hash
841
-     *
842
-     * @param  string $hash
843
-     * @param  string $relation
844
-     * @return \Analogue\ORM\System\Aggregate|null
845
-     */
846
-    protected function getRelatedAggregateFromHash($hash, $relation)
847
-    {
848
-        foreach ($this->relationships[$relation] as $aggregate) {
849
-            if ($aggregate->getEntityHash() == $hash) {
850
-                return $aggregate;
851
-            }
852
-        }
853
-        return null;
854
-    }
855
-
856
-    /**
857
-     * Return an array of Entity Hashes from a specific relation
858
-     *
859
-     * @param  string $relation
860
-     * @return array
861
-     */
862
-    protected function getEntityHashesFromRelation($relation)
863
-    {
864
-        return array_map(function ($aggregate) {
865
-            return $aggregate->getEntityHash();
866
-        }, $this->relationships[$relation]);
867
-    }
868
-
869
-    /**
870
-     * Check the existence of an actual relationship
871
-     *
872
-     * @param  string $relation
873
-     * @return boolean
874
-     */
875
-    protected function isActualRelationships($relation)
876
-    {
877
-        return array_key_exists($relation, $this->relationships)
878
-            && count($this->relationships[$relation]) > 0;
879
-    }
880
-
881
-    /**
882
-     * Return cache instance for the current entity type
883
-     *
884
-     * @return \Analogue\ORM\System\EntityCache
885
-     */
886
-    protected function getCache()
887
-    {
888
-        return $this->mapper->getEntityCache();
889
-    }
890
-
891
-    /**
892
-     * Get Only Raw Entiy's attributes which have been modified
893
-     * since last query
894
-     *
895
-     * @return array
896
-     */
897
-    public function getDirtyRawAttributes()
898
-    {
899
-        $attributes = $this->getRawAttributes();
900
-        $cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
901
-
902
-        $dirty = [];
903
-
904
-        foreach ($attributes as $key => $value) {
905
-            if ($this->isRelation($key) || $key == 'pivot') {
906
-                continue;
907
-            }
908
-
909
-            if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
910
-                $dirty[$key] = $value;
911
-            } elseif ($value !== $cachedAttributes[$key] &&
912
-                !$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
913
-                $dirty[$key] = $value;
914
-            }
915
-        }
916
-
917
-        return $dirty;
918
-    }
919
-
920
-    /**
921
-     * @param $key
922
-     * @return bool
923
-     */
924
-    protected function isRelation($key)
925
-    {
926
-        return in_array($key, $this->entityMap->getRelationships());
927
-    }
928
-
929
-    /**
930
-     * Determine if the new and old values for a given key are numerically equivalent.
931
-     *
932
-     * @param $current
933
-     * @param $original
934
-     * @return boolean
935
-     */
936
-    protected function originalIsNumericallyEquivalent($current, $original)
937
-    {
938
-        return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
939
-    }
940
-
941
-    /**
942
-     * Get the underlying entity object
943
-     *
944
-     * @return mixed
945
-     */
946
-    public function getEntityObject()
947
-    {
948
-        return $this->wrappedEntity->getObject();
949
-    }
950
-
951
-    /**
952
-     * Return the Mapper instance for the current Entity Type
953
-     *
954
-     * @return \Analogue\ORM\System\Mapper
955
-     */
956
-    public function getMapper()
957
-    {
958
-        return $this->mapper;
959
-    }
960
-
961
-    /**
962
-     * Check that the entity already exists in the database, by checking
963
-     * if it has an EntityCache record
964
-     *
965
-     * @return boolean
966
-     */
967
-    public function exists()
968
-    {
969
-        return $this->getCache()->has($this->getEntityId());
970
-    }
971
-
972
-    /**
973
-     * Set the object attribute raw values (hydration)
974
-     *
975
-     * @param array $attributes
976
-     */
977
-    public function setEntityAttributes(array $attributes)
978
-    {
979
-        $this->wrappedEntity->setEntityAttributes($attributes);
980
-    }
981
-
982
-    /**
983
-     * Get the raw object's values.
984
-     *
985
-     * @return array
986
-     */
987
-    public function getEntityAttributes()
988
-    {
989
-        return $this->wrappedEntity->getEntityAttributes();
990
-    }
991
-
992
-    /**
993
-     * Set the raw entity attributes
994
-     * @param string $key
995
-     * @param string $value
996
-     */
997
-    public function setEntityAttribute($key, $value)
998
-    {
999
-        $this->wrappedEntity->setEntityAttribute($key, $value);
1000
-    }
1001
-
1002
-    /**
1003
-     * Return the entity's attribute
1004
-     * @param  string $key
1005
-     * @return mixed
1006
-     */
1007
-    public function getEntityAttribute($key)
1008
-    {
1009
-        return $this->wrappedEntity->getEntityAttribute($key);
1010
-    }
1011
-
1012
-    /**
1013
-     * Does the attribute exists on the entity
1014
-     *
1015
-     * @param  string  $key
1016
-     * @return boolean
1017
-     */
1018
-    public function hasAttribute($key)
1019
-    {
1020
-        return $this->wrappedEntity->hasAttribute($key);
1021
-    }
1022
-
1023
-    /**
1024
-     * Set the lazyloading proxies on the wrapped entity
1025
-     */
1026
-    public function setProxies()
1027
-    {
1028
-        $this->wrappedEntity->setProxies();
1029
-    }
583
+		return $attributes;
584
+	}
585
+
586
+	/**
587
+	 * Return's entity raw attributes in the state they were at last
588
+	 * query.
589
+	 *
590
+	 * @param  array|null $columns
591
+	 * @return array
592
+	 */
593
+	protected function getCachedRawAttributes(array $columns = null)
594
+	{
595
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
596
+
597
+		if (is_null($columns)) {
598
+			return $cachedAttributes;
599
+		} else {
600
+			return array_only($cachedAttributes, $columns);
601
+		}
602
+	}
603
+
604
+	/**
605
+	 * Return a single attribute from the cache
606
+	 * @param  string $key
607
+	 * @return mixed
608
+	 */
609
+	protected function getCachedAttribute($key)
610
+	{
611
+		$cachedAttributes = $this->getCache()->get($this->getEntityId());
612
+
613
+		if (!array_key_exists($key, $cachedAttributes)) {
614
+			return null;
615
+		} else {
616
+			return $cachedAttributes[$key];
617
+		}
618
+	}
619
+
620
+	/**
621
+	 * Convert related Entity's attributes to foreign keys
622
+	 *
623
+	 * @return array
624
+	 */
625
+	protected function getForeignKeyAttributes()
626
+	{
627
+		$foreignKeys = [];
628
+
629
+		foreach ($this->entityMap->getLocalRelationships() as $relation) {
630
+			// check if relationship has been parsed, meaning it has an actual object
631
+			// in the entity's attributes
632
+			if ($this->isActualRelationships($relation)) {
633
+				$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromRelation($relation);
634
+			}
635
+		}
636
+
637
+		if (!is_null($this->parent)) {
638
+			$foreignKeys = $foreignKeys + $this->getForeignKeyAttributesFromParent();
639
+		}
640
+
641
+		return $foreignKeys;
642
+	}
643
+
644
+	/**
645
+	 * Return an associative array containing the key-value pair(s) from
646
+	 * the related entity.
647
+	 *
648
+	 * @param  string $relation
649
+	 * @return array
650
+	 */
651
+	protected function getForeignKeyAttributesFromRelation($relation)
652
+	{
653
+		$localRelations = $this->entityMap->getLocalRelationships();
654
+
655
+		if (in_array($relation, $localRelations)) {
656
+			// Call Relationship's method
657
+			$relationship = $this->entityMap->$relation($this->getEntityObject());
658
+
659
+			$relatedAggregate = $this->relationships[$relation][0];
660
+
661
+			return $relationship->getForeignKeyValuePair($relatedAggregate->getEntityObject());
662
+		} else {
663
+			return [];
664
+		}
665
+	}
666
+
667
+	/**
668
+	 * Get foreign key attribute(s) from a parent entity in this
669
+	 * aggregate context
670
+	 *
671
+	 * @return array
672
+	 */
673
+	protected function getForeignKeyAttributesFromParent()
674
+	{
675
+		$parentMap = $this->parent->getEntityMap();
676
+
677
+		$parentForeignRelations = $parentMap->getForeignRelationships();
678
+		$parentPivotRelations = $parentMap->getPivotRelationships();
679
+
680
+		$parentRelation = $this->parentRelationship;
681
+
682
+		if (in_array($parentRelation, $parentForeignRelations)
683
+			&& !in_array($parentRelation, $parentPivotRelations)
684
+		) {
685
+			$parentObject = $this->parent->getEntityObject();
686
+
687
+			// Call Relationship's method on parent map
688
+			$relationship = $parentMap->$parentRelation($parentObject);
689
+
690
+			return $relationship->getForeignKeyValuePair();
691
+		} else {
692
+			return [];
693
+		}
694
+	}
695
+
696
+	/**
697
+	 * Update Pivot records on loaded relationships, by comparing the
698
+	 * values from the Entity Cache to the actual relationship inside
699
+	 * the aggregated entity.
700
+	 *
701
+	 * @return void
702
+	 */
703
+	public function updatePivotRecords()
704
+	{
705
+		$pivots = $this->entityMap->getPivotRelationships();
706
+
707
+		foreach ($pivots as $pivot) {
708
+			if (array_key_exists($pivot, $this->relationships)) {
709
+				$this->updatePivotRelation($pivot);
710
+			}
711
+		}
712
+	}
713
+
714
+	/**
715
+	 * Update Single pivot relationship
716
+	 *
717
+	 * @param  string $relation
718
+	 * @return void
719
+	 */
720
+	protected function updatePivotRelation($relation)
721
+	{
722
+		$hashes = $this->getEntityHashesFromRelation($relation);
723
+
724
+		$cachedAttributes = $this->getCachedRawAttributes();
725
+
726
+		if (array_key_exists($relation, $cachedAttributes)) {
727
+			// Compare the two array of hashes to find out existing
728
+			// pivot records, and the ones to be created.
729
+			$new = array_diff($hashes, array_keys($cachedAttributes[$relation]));
730
+			$existing = array_intersect($hashes, array_keys($cachedAttributes[$relation]));
731
+		} else {
732
+			$existing = [];
733
+			$new = $hashes;
734
+		}
735
+
736
+		if (count($new) > 0) {
737
+			$pivots = $this->getRelatedAggregatesFromHashes($new, $relation);
738
+
739
+			$this->entityMap->$relation($this->getEntityObject())->createPivots($pivots);
740
+		}
741
+
742
+		if (count($existing) > 0) {
743
+			foreach ($existing as $pivotHash) {
744
+				$this->updatePivotIfDirty($pivotHash, $relation);
745
+			}
746
+		}
747
+	}
748
+
749
+	/**
750
+	 * Compare existing pivot record in cache and update it
751
+	 * if the pivot attributes are dirty
752
+	 *
753
+	 * @param  string $pivotHash
754
+	 * @param  string $relation
755
+	 * @return void
756
+	 */
757
+	protected function updatePivotIfDirty($pivotHash, $relation)
758
+	{
759
+		$aggregate = $this->getRelatedAggregateFromHash($pivotHash, $relation);
760
+
761
+		if ($aggregate->hasAttribute('pivot')) {
762
+			$pivot = $aggregate->getEntityAttribute('pivot')->getEntityAttributes();
763
+
764
+			$cachedPivotAttributes = $this->getPivotAttributesFromCache($pivotHash, $relation);
765
+
766
+			$actualPivotAttributes = array_only($pivot, array_keys($cachedPivotAttributes));
767
+
768
+			$dirty = $this->getDirtyAttributes($actualPivotAttributes, $cachedPivotAttributes);
769
+
770
+			if (count($dirty) > 0) {
771
+				$id = $aggregate->getEntityId();
772
+
773
+				$this->entityMap->$relation($this->getEntityObject())->updateExistingPivot($id, $dirty);
774
+			}
775
+		}
776
+	}
777
+
778
+	/**
779
+	 * Compare two attributes array and return dirty attributes
780
+	 *
781
+	 * @param  array $actual
782
+	 * @param  array $cached
783
+	 * @return array
784
+	 */
785
+	protected function getDirtyAttributes(array $actual, array $cached)
786
+	{
787
+		$dirty = [];
788
+
789
+		foreach ($actual as $key => $value) {
790
+			if (!$this->originalIsNumericallyEquivalent($value, $cached[$key])) {
791
+				$dirty[$key] = $actual[$key];
792
+			}
793
+		}
794
+
795
+		return $dirty;
796
+	}
797
+
798
+	/**
799
+	 *
800
+	 * @param  string $pivotHash
801
+	 * @param  string $relation
802
+	 * @return array
803
+	 */
804
+	protected function getPivotAttributesFromCache($pivotHash, $relation)
805
+	{
806
+		$cachedAttributes = $this->getCachedRawAttributes();
807
+
808
+		$cachedRelations = $cachedAttributes[$relation];
809
+
810
+		foreach ($cachedRelations as $cachedRelation) {
811
+			if ($cachedRelation == $pivotHash) {
812
+				return $cachedRelation->getPivotAttributes();
813
+			}
814
+		}
815
+	}
816
+
817
+	/**
818
+	 * Returns an array of related Aggregates from its entity hashes
819
+	 *
820
+	 * @param  array  $hashes
821
+	 * @param  string $relation
822
+	 * @return array
823
+	 */
824
+	protected function getRelatedAggregatesFromHashes(array $hashes, $relation)
825
+	{
826
+		$related = [];
827
+
828
+		foreach ($hashes as $hash) {
829
+			$aggregate = $this->getRelatedAggregateFromHash($hash, $relation);
830
+
831
+			if (!is_null($aggregate)) {
832
+				$related[] = $aggregate;
833
+			}
834
+		}
835
+
836
+		return $related;
837
+	}
838
+
839
+	/**
840
+	 * Get related aggregate from its hash
841
+	 *
842
+	 * @param  string $hash
843
+	 * @param  string $relation
844
+	 * @return \Analogue\ORM\System\Aggregate|null
845
+	 */
846
+	protected function getRelatedAggregateFromHash($hash, $relation)
847
+	{
848
+		foreach ($this->relationships[$relation] as $aggregate) {
849
+			if ($aggregate->getEntityHash() == $hash) {
850
+				return $aggregate;
851
+			}
852
+		}
853
+		return null;
854
+	}
855
+
856
+	/**
857
+	 * Return an array of Entity Hashes from a specific relation
858
+	 *
859
+	 * @param  string $relation
860
+	 * @return array
861
+	 */
862
+	protected function getEntityHashesFromRelation($relation)
863
+	{
864
+		return array_map(function ($aggregate) {
865
+			return $aggregate->getEntityHash();
866
+		}, $this->relationships[$relation]);
867
+	}
868
+
869
+	/**
870
+	 * Check the existence of an actual relationship
871
+	 *
872
+	 * @param  string $relation
873
+	 * @return boolean
874
+	 */
875
+	protected function isActualRelationships($relation)
876
+	{
877
+		return array_key_exists($relation, $this->relationships)
878
+			&& count($this->relationships[$relation]) > 0;
879
+	}
880
+
881
+	/**
882
+	 * Return cache instance for the current entity type
883
+	 *
884
+	 * @return \Analogue\ORM\System\EntityCache
885
+	 */
886
+	protected function getCache()
887
+	{
888
+		return $this->mapper->getEntityCache();
889
+	}
890
+
891
+	/**
892
+	 * Get Only Raw Entiy's attributes which have been modified
893
+	 * since last query
894
+	 *
895
+	 * @return array
896
+	 */
897
+	public function getDirtyRawAttributes()
898
+	{
899
+		$attributes = $this->getRawAttributes();
900
+		$cachedAttributes = $this->getCachedRawAttributes(array_keys($attributes));
901
+
902
+		$dirty = [];
903
+
904
+		foreach ($attributes as $key => $value) {
905
+			if ($this->isRelation($key) || $key == 'pivot') {
906
+				continue;
907
+			}
908
+
909
+			if (!array_key_exists($key, $cachedAttributes) && !$value instanceof Pivot) {
910
+				$dirty[$key] = $value;
911
+			} elseif ($value !== $cachedAttributes[$key] &&
912
+				!$this->originalIsNumericallyEquivalent($value, $cachedAttributes[$key])) {
913
+				$dirty[$key] = $value;
914
+			}
915
+		}
916
+
917
+		return $dirty;
918
+	}
919
+
920
+	/**
921
+	 * @param $key
922
+	 * @return bool
923
+	 */
924
+	protected function isRelation($key)
925
+	{
926
+		return in_array($key, $this->entityMap->getRelationships());
927
+	}
928
+
929
+	/**
930
+	 * Determine if the new and old values for a given key are numerically equivalent.
931
+	 *
932
+	 * @param $current
933
+	 * @param $original
934
+	 * @return boolean
935
+	 */
936
+	protected function originalIsNumericallyEquivalent($current, $original)
937
+	{
938
+		return is_numeric($current) && is_numeric($original) && strcmp((string) $current, (string) $original) === 0;
939
+	}
940
+
941
+	/**
942
+	 * Get the underlying entity object
943
+	 *
944
+	 * @return mixed
945
+	 */
946
+	public function getEntityObject()
947
+	{
948
+		return $this->wrappedEntity->getObject();
949
+	}
950
+
951
+	/**
952
+	 * Return the Mapper instance for the current Entity Type
953
+	 *
954
+	 * @return \Analogue\ORM\System\Mapper
955
+	 */
956
+	public function getMapper()
957
+	{
958
+		return $this->mapper;
959
+	}
960
+
961
+	/**
962
+	 * Check that the entity already exists in the database, by checking
963
+	 * if it has an EntityCache record
964
+	 *
965
+	 * @return boolean
966
+	 */
967
+	public function exists()
968
+	{
969
+		return $this->getCache()->has($this->getEntityId());
970
+	}
971
+
972
+	/**
973
+	 * Set the object attribute raw values (hydration)
974
+	 *
975
+	 * @param array $attributes
976
+	 */
977
+	public function setEntityAttributes(array $attributes)
978
+	{
979
+		$this->wrappedEntity->setEntityAttributes($attributes);
980
+	}
981
+
982
+	/**
983
+	 * Get the raw object's values.
984
+	 *
985
+	 * @return array
986
+	 */
987
+	public function getEntityAttributes()
988
+	{
989
+		return $this->wrappedEntity->getEntityAttributes();
990
+	}
991
+
992
+	/**
993
+	 * Set the raw entity attributes
994
+	 * @param string $key
995
+	 * @param string $value
996
+	 */
997
+	public function setEntityAttribute($key, $value)
998
+	{
999
+		$this->wrappedEntity->setEntityAttribute($key, $value);
1000
+	}
1001
+
1002
+	/**
1003
+	 * Return the entity's attribute
1004
+	 * @param  string $key
1005
+	 * @return mixed
1006
+	 */
1007
+	public function getEntityAttribute($key)
1008
+	{
1009
+		return $this->wrappedEntity->getEntityAttribute($key);
1010
+	}
1011
+
1012
+	/**
1013
+	 * Does the attribute exists on the entity
1014
+	 *
1015
+	 * @param  string  $key
1016
+	 * @return boolean
1017
+	 */
1018
+	public function hasAttribute($key)
1019
+	{
1020
+		return $this->wrappedEntity->hasAttribute($key);
1021
+	}
1022
+
1023
+	/**
1024
+	 * Set the lazyloading proxies on the wrapped entity
1025
+	 */
1026
+	public function setProxies()
1027
+	{
1028
+		$this->wrappedEntity->setProxies();
1029
+	}
1030 1030
 }
Please login to merge, or discard this patch.