Completed
Push — 5.1 ( 64105d...5947be )
by Rémi
08:21 queued 04:00
created
src/System/Proxies/EntityProxy.php 1 patch
Indentation   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -4,81 +4,81 @@
 block discarded – undo
4 4
 
5 5
 class EntityProxy extends Proxy
6 6
 {
7
-    /**
8
-     * Underlying entity
9
-     *
10
-     * @var mixed
11
-     */
12
-    protected $entity;
7
+	/**
8
+	 * Underlying entity
9
+	 *
10
+	 * @var mixed
11
+	 */
12
+	protected $entity;
13 13
 
14
-    /**
15
-     * Load the underlying relation
16
-     *
17
-     * @return void
18
-     */
19
-    protected function loadOnce()
20
-    {
21
-        $this->entity = $this->load();
22
-    }
14
+	/**
15
+	 * Load the underlying relation
16
+	 *
17
+	 * @return void
18
+	 */
19
+	protected function loadOnce()
20
+	{
21
+		$this->entity = $this->load();
22
+	}
23 23
 
24
-    /**
25
-     * Return the actual Entity
26
-     *
27
-     * @return mixed
28
-     */
29
-    public function getUnderlyingObject()
30
-    {
31
-        if (!$this->isLoaded()) {
32
-            $this->loadOnce();
33
-        }
24
+	/**
25
+	 * Return the actual Entity
26
+	 *
27
+	 * @return mixed
28
+	 */
29
+	public function getUnderlyingObject()
30
+	{
31
+		if (!$this->isLoaded()) {
32
+			$this->loadOnce();
33
+		}
34 34
 
35
-        return $this->entity;
36
-    }
35
+		return $this->entity;
36
+	}
37 37
 
38
-    /**
39
-     * Transparently passes get operation to underlying entity
40
-     *
41
-     * @param  string $attribute
42
-     * @return mixed
43
-     */
44
-    public function __get($attribute)
45
-    {
46
-        if (!$this->isLoaded()) {
47
-            $this->loadOnce();
48
-        }
38
+	/**
39
+	 * Transparently passes get operation to underlying entity
40
+	 *
41
+	 * @param  string $attribute
42
+	 * @return mixed
43
+	 */
44
+	public function __get($attribute)
45
+	{
46
+		if (!$this->isLoaded()) {
47
+			$this->loadOnce();
48
+		}
49 49
 
50
-        return $this->entity->$attribute;
51
-    }
50
+		return $this->entity->$attribute;
51
+	}
52 52
 
53
-    /**
54
-     * Transparently passes set operation to underlying entity
55
-     *
56
-     * @param  string $attribute [description]
57
-     * @param  mixed
58
-     * @return void
59
-     */
60
-    public function __set($attribute, $value)
61
-    {
62
-        if (!$this->isLoaded()) {
63
-            $this->loadOnce();
64
-        }
53
+	/**
54
+	 * Transparently passes set operation to underlying entity
55
+	 *
56
+	 * @param  string $attribute [description]
57
+	 * @param  mixed
58
+	 * @return void
59
+	 */
60
+	public function __set($attribute, $value)
61
+	{
62
+		if (!$this->isLoaded()) {
63
+			$this->loadOnce();
64
+		}
65 65
 
66
-        $this->entity->$attribute = $value;
67
-    }
66
+		$this->entity->$attribute = $value;
67
+	}
68 68
 
69
-    /**
70
-     * Transparently Redirect non overrided calls to the lazy loaded Entity
71
-     *
72
-     * @param  string $method
73
-     * @param  array  $parameters
74
-     * @return mixed
75
-     */
76
-    public function __call($method, $parameters)
77
-    {
78
-        if (!$this->isLoaded()) {
79
-            $this->loadOnce();
80
-        }
69
+	/**
70
+	 * Transparently Redirect non overrided calls to the lazy loaded Entity
71
+	 *
72
+	 * @param  string $method
73
+	 * @param  array  $parameters
74
+	 * @return mixed
75
+	 */
76
+	public function __call($method, $parameters)
77
+	{
78
+		if (!$this->isLoaded()) {
79
+			$this->loadOnce();
80
+		}
81 81
 
82
-        return call_user_func_array([$this->entity, $method], $parameters);
83
-    }
82
+		return call_user_func_array([$this->entity, $method], $parameters);
83
+	}
84 84
 }
Please login to merge, or discard this patch.
src/EntityCollection.php 2 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
             $key = $this->getEntityKey($key);
43 43
         }
44 44
 
45
-        return array_first($this->items, function ($itemKey, $entity) use ($key) {
45
+        return array_first($this->items, function($itemKey, $entity) use ($key) {
46 46
             return $this->getEntityKey($entity) == $key;
47 47
         }, $default);
48 48
     }
@@ -144,14 +144,14 @@  discard block
 block discarded – undo
144 144
      */
145 145
     public function getEntityHashes()
146 146
     {
147
-        return array_map(function ($entity) {
147
+        return array_map(function($entity) {
148 148
             $class = get_class($entity);
149 149
 
150 150
             $mapper = Manager::getMapper($class);
151 151
             
152 152
             $keyName = $mapper->getEntityMap()->getKeyName();
153 153
             
154
-            return $class . '.' . $entity->getEntityAttribute($keyName);
154
+            return $class.'.'.$entity->getEntityAttribute($keyName);
155 155
         },
156 156
         $this->items);
157 157
     }
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
             
175 175
             $keyName = $mapper->getEntityMap()->getKeyName();
176 176
 
177
-            if (in_array($class . '.' . $item->$keyName, $hashes)) {
177
+            if (in_array($class.'.'.$item->$keyName, $hashes)) {
178 178
                 $subset[] = $item;
179 179
             }
180 180
         }
@@ -321,7 +321,7 @@  discard block
 block discarded – undo
321 321
      */
322 322
     public function max($key = null)
323 323
     {
324
-        return $this->reduce(function ($result, $item) use ($key) {
324
+        return $this->reduce(function($result, $item) use ($key) {
325 325
             $wrapper = $this->factory->make($item);
326 326
 
327 327
             return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
@@ -338,7 +338,7 @@  discard block
 block discarded – undo
338 338
      */
339 339
     public function min($key = null)
340 340
     {
341
-        return $this->reduce(function ($result, $item) use ($key) {
341
+        return $this->reduce(function($result, $item) use ($key) {
342 342
             $wrapper = $this->factory->make($item);
343 343
 
344 344
             return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
Please login to merge, or discard this patch.
Indentation   +391 added lines, -391 removed lines patch added patch discarded remove patch
@@ -10,395 +10,395 @@
 block discarded – undo
10 10
 
11 11
 class EntityCollection extends Collection
12 12
 {
13
-    /**
14
-     * Wrapper Factory
15
-     *
16
-     * @var \Analogue\ORM\System\Wrappers\Factory
17
-     */
18
-    protected $factory;
19
-
20
-    /**
21
-     * EntityCollection constructor.
22
-     * @param array|null $entities
23
-     */
24
-    public function __construct(array $entities = null)
25
-    {
26
-        $this->factory = new Factory;
27
-
28
-        parent::__construct($entities);
29
-    }
30
-
31
-    /**
32
-     * Find an entity in the collection by key.
33
-     *
34
-     * @param  mixed $key
35
-     * @param  mixed $default
36
-     * @throws MappingException
37
-     * @return \Analogue\ORM\Entity
38
-     */
39
-    public function find($key, $default = null)
40
-    {
41
-        if ($key instanceof Mappable) {
42
-            $key = $this->getEntityKey($key);
43
-        }
44
-
45
-        return array_first($this->items, function ($itemKey, $entity) use ($key) {
46
-            return $this->getEntityKey($entity) == $key;
47
-        }, $default);
48
-    }
49
-
50
-    /**
51
-     * Add an entity to the collection.
52
-     *
53
-     * @param  Mappable $entity
54
-     * @return $this
55
-     */
56
-    public function add($entity)
57
-    {
58
-        $this->push($entity);
59
-
60
-        return $this;
61
-    }
62
-
63
-    /**
64
-     * Remove an entity from the collection
65
-     *
66
-     * @param $entity
67
-     * @throws MappingException
68
-     * @return mixed
69
-     */
70
-    public function remove($entity)
71
-    {
72
-        $key = $this->getEntityKey($entity);
73
-
74
-        return $this->pull($key);
75
-    }
76
-
77
-    /**
78
-     * Push an item onto the end of the collection.
79
-     *
80
-     * @param  mixed $value
81
-     * @return void
82
-     */
83
-    public function push($value)
84
-    {
85
-        $this->offsetSet(null, $value);
86
-    }
87
-
88
-    /**
89
-     * Put an item in the collection by key.
90
-     *
91
-     * @param  mixed $key
92
-     * @param  mixed $value
93
-     * @return void
94
-     */
95
-    public function put($key, $value)
96
-    {
97
-        $this->offsetSet($key, $value);
98
-    }
99
-
100
-    /**
101
-     * Set the item at a given offset.
102
-     *
103
-     * @param  mixed $key
104
-     * @param  mixed $value
105
-     * @return void
106
-     */
107
-    public function offsetSet($key, $value)
108
-    {
109
-        if (is_null($key)) {
110
-            $this->items[] = $value;
111
-        } else {
112
-            $this->items[$key] = $value;
113
-        }
114
-    }
115
-
116
-    /**
117
-     * Determine if a key exists in the collection.
118
-     *
119
-     * @param  mixed      $key
120
-     * @param  mixed|null $value
121
-     * @return bool
122
-     */
123
-    public function contains($key, $value = null)
124
-    {
125
-        if (func_num_args() == 2) {
126
-            return !$this->where($key, $value)->isEmpty();
127
-        }
128
-
129
-        if ($this->useAsCallable($key)) {
130
-            return !is_null($this->first($key));
131
-        }
132
-
133
-        return !is_null($this->find($key));
134
-    }
135
-
136
-    /**
137
-     * Fetch a nested element of the collection.
138
-     *
139
-     * @param  string $key
140
-     * @return self
141
-     */
142
-    public function fetch($key)
143
-    {
144
-        return new static(array_fetch($this->toArray(), $key));
145
-    }
146
-
147
-    /**
148
-     * Generic function for returning class.key value pairs
149
-     *
150
-     * @throws MappingException
151
-     * @return string
152
-     */
153
-    public function getEntityHashes()
154
-    {
155
-        return array_map(function ($entity) {
156
-            $class = get_class($entity);
157
-
158
-            $mapper = Manager::getMapper($class);
159
-
160
-            $keyName = $mapper->getEntityMap()->getKeyName();
161
-
162
-            return $class . '.' . $entity->getEntityAttribute($keyName);
163
-        },
164
-        $this->items);
165
-    }
166
-
167
-    /**
168
-     * Get a subset of the collection from entity hashes
169
-     *
170
-     * @param  array $hashes
171
-     * @throws MappingException
172
-     * @return array
173
-     */
174
-    public function getSubsetByHashes(array $hashes)
175
-    {
176
-        $subset = [];
177
-
178
-        foreach ($this->items as $item) {
179
-            $class = get_class($item);
180
-
181
-            $mapper = Manager::getMapper($class);
182
-
183
-            $keyName = $mapper->getEntityMap()->getKeyName();
184
-
185
-            if (in_array($class . '.' . $item->$keyName, $hashes)) {
186
-                $subset[] = $item;
187
-            }
188
-        }
189
-
190
-        return $subset;
191
-    }
192
-
193
-    /**
194
-     * Merge the collection with the given items.
195
-     *
196
-     * @param  array $items
197
-     * @throws MappingException
198
-     * @return self
199
-     */
200
-    public function merge($items)
201
-    {
202
-        $dictionary = $this->getDictionary();
203
-
204
-        foreach ($items as $item) {
205
-            $dictionary[$this->getEntityKey($item)] = $item;
206
-        }
207
-
208
-        return new static(array_values($dictionary));
209
-    }
210
-
211
-    /**
212
-     * Diff the collection with the given items.
213
-     *
214
-     * @param  \ArrayAccess|array $items
215
-     * @return self
216
-     */
217
-    public function diff($items)
218
-    {
219
-        $diff = new static;
220
-
221
-        $dictionary = $this->getDictionary($items);
222
-
223
-        foreach ($this->items as $item) {
224
-            if (!isset($dictionary[$this->getEntityKey($item)])) {
225
-                $diff->add($item);
226
-            }
227
-        }
228
-
229
-        return $diff;
230
-    }
231
-
232
-    /**
233
-     * Intersect the collection with the given items.
234
-     *
235
-     * @param  \ArrayAccess|array $items
236
-     * @throws MappingException
237
-     * @return self
238
-     */
239
-    public function intersect($items)
240
-    {
241
-        $intersect = new static;
242
-
243
-        $dictionary = $this->getDictionary($items);
244
-
245
-        foreach ($this->items as $item) {
246
-            if (isset($dictionary[$this->getEntityKey($item)])) {
247
-                $intersect->add($item);
248
-            }
249
-        }
250
-
251
-        return $intersect;
252
-    }
253
-
254
-    /**
255
-     * Returns only the models from the collection with the specified keys.
256
-     *
257
-     * @param  mixed $keys
258
-     * @return self
259
-     */
260
-    public function only($keys)
261
-    {
262
-        $dictionary = array_only($this->getDictionary(), $keys);
263
-
264
-        return new static(array_values($dictionary));
265
-    }
266
-
267
-    /**
268
-     * Returns all models in the collection except the models with specified keys.
269
-     *
270
-     * @param  mixed $keys
271
-     * @return self
272
-     */
273
-    public function except($keys)
274
-    {
275
-        $dictionary = array_except($this->getDictionary(), $keys);
276
-
277
-        return new static(array_values($dictionary));
278
-    }
279
-
280
-    /**
281
-     * Get a dictionary keyed by primary keys.
282
-     *
283
-     * @param  \ArrayAccess|array $items
284
-     * @throws MappingException
285
-     * @return array
286
-     */
287
-    public function getDictionary($items = null)
288
-    {
289
-        $items = is_null($items) ? $this->items : $items;
290
-
291
-        $dictionary = [];
292
-
293
-        foreach ($items as $value) {
294
-            $dictionary[$this->getEntityKey($value)] = $value;
295
-        }
296
-
297
-        return $dictionary;
298
-    }
299
-
300
-    /**
301
-     * @throws MappingException
302
-     * @return array
303
-     */
304
-    public function getEntityKeys()
305
-    {
306
-        return array_keys($this->getDictionary());
307
-    }
308
-
309
-    /**
310
-     * @param $entity
311
-     * @throws MappingException
312
-     * @return mixed
313
-     */
314
-    protected function getEntityKey($entity)
315
-    {
316
-        $keyName = Manager::getMapper($entity)->getEntityMap()->getKeyName();
317
-
318
-        $wrapper = $this->factory->make($entity);
319
-
320
-        return $wrapper->getEntityAttribute($keyName);
321
-    }
322
-
323
-    /**
324
-     * Get the max value of a given key.
325
-     *
326
-     * @param  string|null $key
327
-     * @throws MappingException
328
-     * @return mixed
329
-     */
330
-    public function max($key = null)
331
-    {
332
-        return $this->reduce(function ($result, $item) use ($key) {
333
-            $wrapper = $this->factory->make($item);
334
-
335
-            return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
336
-                $wrapper->getEntityAttribute($key) : $result;
337
-        });
338
-    }
339
-
340
-    /**
341
-     * Get the min value of a given key.
342
-     *
343
-     * @param  string|null $key
344
-     * @throws MappingException
345
-     * @return mixed
346
-     */
347
-    public function min($key = null)
348
-    {
349
-        return $this->reduce(function ($result, $item) use ($key) {
350
-            $wrapper = $this->factory->make($item);
351
-
352
-            return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
353
-                ? $wrapper->getEntityAttribute($key) : $result;
354
-        });
355
-    }
356
-
357
-    /**
358
-     * Get an array with the values of a given key.
359
-     *
360
-     * @param  string $value
361
-     * @param  string|null $key
362
-     * @return self
363
-     */
364
-    public function pluck($value, $key = null)
365
-    {
366
-        return new Collection(Arr::pluck($this->items, $value, $key));
367
-    }
368
-
369
-    /**
370
-     * Alias for the "pluck" method.
371
-     *
372
-     * @param  string $value
373
-     * @param  string|null $key
374
-     * @return self
375
-     */
376
-    public function lists($value, $key = null)
377
-    {
378
-        return $this->pluck($value, $key);
379
-    }
380
-
381
-    /**
382
-     * Return only unique items from the collection.
383
-     *
384
-     * @param  string|null $key
385
-     * @throws MappingException
386
-     * @return self
387
-     */
388
-    public function unique($key = null)
389
-    {
390
-        $dictionary = $this->getDictionary();
391
-
392
-        return new static(array_values($dictionary));
393
-    }
394
-
395
-    /**
396
-     * Get a base Support collection instance from this collection.
397
-     *
398
-     * @return \Illuminate\Support\Collection
399
-     */
400
-    public function toBase()
401
-    {
402
-        return new Collection($this->items);
403
-    }
13
+	/**
14
+	 * Wrapper Factory
15
+	 *
16
+	 * @var \Analogue\ORM\System\Wrappers\Factory
17
+	 */
18
+	protected $factory;
19
+
20
+	/**
21
+	 * EntityCollection constructor.
22
+	 * @param array|null $entities
23
+	 */
24
+	public function __construct(array $entities = null)
25
+	{
26
+		$this->factory = new Factory;
27
+
28
+		parent::__construct($entities);
29
+	}
30
+
31
+	/**
32
+	 * Find an entity in the collection by key.
33
+	 *
34
+	 * @param  mixed $key
35
+	 * @param  mixed $default
36
+	 * @throws MappingException
37
+	 * @return \Analogue\ORM\Entity
38
+	 */
39
+	public function find($key, $default = null)
40
+	{
41
+		if ($key instanceof Mappable) {
42
+			$key = $this->getEntityKey($key);
43
+		}
44
+
45
+		return array_first($this->items, function ($itemKey, $entity) use ($key) {
46
+			return $this->getEntityKey($entity) == $key;
47
+		}, $default);
48
+	}
49
+
50
+	/**
51
+	 * Add an entity to the collection.
52
+	 *
53
+	 * @param  Mappable $entity
54
+	 * @return $this
55
+	 */
56
+	public function add($entity)
57
+	{
58
+		$this->push($entity);
59
+
60
+		return $this;
61
+	}
62
+
63
+	/**
64
+	 * Remove an entity from the collection
65
+	 *
66
+	 * @param $entity
67
+	 * @throws MappingException
68
+	 * @return mixed
69
+	 */
70
+	public function remove($entity)
71
+	{
72
+		$key = $this->getEntityKey($entity);
73
+
74
+		return $this->pull($key);
75
+	}
76
+
77
+	/**
78
+	 * Push an item onto the end of the collection.
79
+	 *
80
+	 * @param  mixed $value
81
+	 * @return void
82
+	 */
83
+	public function push($value)
84
+	{
85
+		$this->offsetSet(null, $value);
86
+	}
87
+
88
+	/**
89
+	 * Put an item in the collection by key.
90
+	 *
91
+	 * @param  mixed $key
92
+	 * @param  mixed $value
93
+	 * @return void
94
+	 */
95
+	public function put($key, $value)
96
+	{
97
+		$this->offsetSet($key, $value);
98
+	}
99
+
100
+	/**
101
+	 * Set the item at a given offset.
102
+	 *
103
+	 * @param  mixed $key
104
+	 * @param  mixed $value
105
+	 * @return void
106
+	 */
107
+	public function offsetSet($key, $value)
108
+	{
109
+		if (is_null($key)) {
110
+			$this->items[] = $value;
111
+		} else {
112
+			$this->items[$key] = $value;
113
+		}
114
+	}
115
+
116
+	/**
117
+	 * Determine if a key exists in the collection.
118
+	 *
119
+	 * @param  mixed      $key
120
+	 * @param  mixed|null $value
121
+	 * @return bool
122
+	 */
123
+	public function contains($key, $value = null)
124
+	{
125
+		if (func_num_args() == 2) {
126
+			return !$this->where($key, $value)->isEmpty();
127
+		}
128
+
129
+		if ($this->useAsCallable($key)) {
130
+			return !is_null($this->first($key));
131
+		}
132
+
133
+		return !is_null($this->find($key));
134
+	}
135
+
136
+	/**
137
+	 * Fetch a nested element of the collection.
138
+	 *
139
+	 * @param  string $key
140
+	 * @return self
141
+	 */
142
+	public function fetch($key)
143
+	{
144
+		return new static(array_fetch($this->toArray(), $key));
145
+	}
146
+
147
+	/**
148
+	 * Generic function for returning class.key value pairs
149
+	 *
150
+	 * @throws MappingException
151
+	 * @return string
152
+	 */
153
+	public function getEntityHashes()
154
+	{
155
+		return array_map(function ($entity) {
156
+			$class = get_class($entity);
157
+
158
+			$mapper = Manager::getMapper($class);
159
+
160
+			$keyName = $mapper->getEntityMap()->getKeyName();
161
+
162
+			return $class . '.' . $entity->getEntityAttribute($keyName);
163
+		},
164
+		$this->items);
165
+	}
166
+
167
+	/**
168
+	 * Get a subset of the collection from entity hashes
169
+	 *
170
+	 * @param  array $hashes
171
+	 * @throws MappingException
172
+	 * @return array
173
+	 */
174
+	public function getSubsetByHashes(array $hashes)
175
+	{
176
+		$subset = [];
177
+
178
+		foreach ($this->items as $item) {
179
+			$class = get_class($item);
180
+
181
+			$mapper = Manager::getMapper($class);
182
+
183
+			$keyName = $mapper->getEntityMap()->getKeyName();
184
+
185
+			if (in_array($class . '.' . $item->$keyName, $hashes)) {
186
+				$subset[] = $item;
187
+			}
188
+		}
189
+
190
+		return $subset;
191
+	}
192
+
193
+	/**
194
+	 * Merge the collection with the given items.
195
+	 *
196
+	 * @param  array $items
197
+	 * @throws MappingException
198
+	 * @return self
199
+	 */
200
+	public function merge($items)
201
+	{
202
+		$dictionary = $this->getDictionary();
203
+
204
+		foreach ($items as $item) {
205
+			$dictionary[$this->getEntityKey($item)] = $item;
206
+		}
207
+
208
+		return new static(array_values($dictionary));
209
+	}
210
+
211
+	/**
212
+	 * Diff the collection with the given items.
213
+	 *
214
+	 * @param  \ArrayAccess|array $items
215
+	 * @return self
216
+	 */
217
+	public function diff($items)
218
+	{
219
+		$diff = new static;
220
+
221
+		$dictionary = $this->getDictionary($items);
222
+
223
+		foreach ($this->items as $item) {
224
+			if (!isset($dictionary[$this->getEntityKey($item)])) {
225
+				$diff->add($item);
226
+			}
227
+		}
228
+
229
+		return $diff;
230
+	}
231
+
232
+	/**
233
+	 * Intersect the collection with the given items.
234
+	 *
235
+	 * @param  \ArrayAccess|array $items
236
+	 * @throws MappingException
237
+	 * @return self
238
+	 */
239
+	public function intersect($items)
240
+	{
241
+		$intersect = new static;
242
+
243
+		$dictionary = $this->getDictionary($items);
244
+
245
+		foreach ($this->items as $item) {
246
+			if (isset($dictionary[$this->getEntityKey($item)])) {
247
+				$intersect->add($item);
248
+			}
249
+		}
250
+
251
+		return $intersect;
252
+	}
253
+
254
+	/**
255
+	 * Returns only the models from the collection with the specified keys.
256
+	 *
257
+	 * @param  mixed $keys
258
+	 * @return self
259
+	 */
260
+	public function only($keys)
261
+	{
262
+		$dictionary = array_only($this->getDictionary(), $keys);
263
+
264
+		return new static(array_values($dictionary));
265
+	}
266
+
267
+	/**
268
+	 * Returns all models in the collection except the models with specified keys.
269
+	 *
270
+	 * @param  mixed $keys
271
+	 * @return self
272
+	 */
273
+	public function except($keys)
274
+	{
275
+		$dictionary = array_except($this->getDictionary(), $keys);
276
+
277
+		return new static(array_values($dictionary));
278
+	}
279
+
280
+	/**
281
+	 * Get a dictionary keyed by primary keys.
282
+	 *
283
+	 * @param  \ArrayAccess|array $items
284
+	 * @throws MappingException
285
+	 * @return array
286
+	 */
287
+	public function getDictionary($items = null)
288
+	{
289
+		$items = is_null($items) ? $this->items : $items;
290
+
291
+		$dictionary = [];
292
+
293
+		foreach ($items as $value) {
294
+			$dictionary[$this->getEntityKey($value)] = $value;
295
+		}
296
+
297
+		return $dictionary;
298
+	}
299
+
300
+	/**
301
+	 * @throws MappingException
302
+	 * @return array
303
+	 */
304
+	public function getEntityKeys()
305
+	{
306
+		return array_keys($this->getDictionary());
307
+	}
308
+
309
+	/**
310
+	 * @param $entity
311
+	 * @throws MappingException
312
+	 * @return mixed
313
+	 */
314
+	protected function getEntityKey($entity)
315
+	{
316
+		$keyName = Manager::getMapper($entity)->getEntityMap()->getKeyName();
317
+
318
+		$wrapper = $this->factory->make($entity);
319
+
320
+		return $wrapper->getEntityAttribute($keyName);
321
+	}
322
+
323
+	/**
324
+	 * Get the max value of a given key.
325
+	 *
326
+	 * @param  string|null $key
327
+	 * @throws MappingException
328
+	 * @return mixed
329
+	 */
330
+	public function max($key = null)
331
+	{
332
+		return $this->reduce(function ($result, $item) use ($key) {
333
+			$wrapper = $this->factory->make($item);
334
+
335
+			return (is_null($result) || $wrapper->getEntityAttribute($key) > $result) ?
336
+				$wrapper->getEntityAttribute($key) : $result;
337
+		});
338
+	}
339
+
340
+	/**
341
+	 * Get the min value of a given key.
342
+	 *
343
+	 * @param  string|null $key
344
+	 * @throws MappingException
345
+	 * @return mixed
346
+	 */
347
+	public function min($key = null)
348
+	{
349
+		return $this->reduce(function ($result, $item) use ($key) {
350
+			$wrapper = $this->factory->make($item);
351
+
352
+			return (is_null($result) || $wrapper->getEntityAttribute($key) < $result)
353
+				? $wrapper->getEntityAttribute($key) : $result;
354
+		});
355
+	}
356
+
357
+	/**
358
+	 * Get an array with the values of a given key.
359
+	 *
360
+	 * @param  string $value
361
+	 * @param  string|null $key
362
+	 * @return self
363
+	 */
364
+	public function pluck($value, $key = null)
365
+	{
366
+		return new Collection(Arr::pluck($this->items, $value, $key));
367
+	}
368
+
369
+	/**
370
+	 * Alias for the "pluck" method.
371
+	 *
372
+	 * @param  string $value
373
+	 * @param  string|null $key
374
+	 * @return self
375
+	 */
376
+	public function lists($value, $key = null)
377
+	{
378
+		return $this->pluck($value, $key);
379
+	}
380
+
381
+	/**
382
+	 * Return only unique items from the collection.
383
+	 *
384
+	 * @param  string|null $key
385
+	 * @throws MappingException
386
+	 * @return self
387
+	 */
388
+	public function unique($key = null)
389
+	{
390
+		$dictionary = $this->getDictionary();
391
+
392
+		return new static(array_values($dictionary));
393
+	}
394
+
395
+	/**
396
+	 * Get a base Support collection instance from this collection.
397
+	 *
398
+	 * @return \Illuminate\Support\Collection
399
+	 */
400
+	public function toBase()
401
+	{
402
+		return new Collection($this->items);
403
+	}
404 404
 }
Please login to merge, or discard this patch.
src/System/Mapper.php 3 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
             throw new InvalidArgumentException('Fired Event with invalid Entity Object');
260 260
         }
261 261
 
262
-        $event = "analogue.{$event}." . $this->entityMap->getClass();
262
+        $event = "analogue.{$event}.".$this->entityMap->getClass();
263 263
 
264 264
         $method = $halt ? 'until' : 'fire';
265 265
 
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
      */
311 311
     public function getGlobalScope($scope)
312 312
     {
313
-        return array_first($this->globalScopes, function ($key, $value) use ($scope) {
313
+        return array_first($this->globalScopes, function($key, $value) use ($scope) {
314 314
             return $scope instanceof $value;
315 315
         });
316 316
     }
Please login to merge, or discard this patch.
Indentation   +550 added lines, -550 removed lines patch added patch discarded remove patch
@@ -22,554 +22,554 @@
 block discarded – undo
22 22
  */
23 23
 class Mapper
24 24
 {
25
-    /**
26
-     * The Manager instance
27
-     *
28
-     * @var \Analogue\ORM\System\Manager
29
-     */
30
-    protected $manager;
31
-
32
-    /**
33
-     * Instance of EntityMapper Object
34
-     *
35
-     * @var \Analogue\ORM\EntityMap
36
-     */
37
-    protected $entityMap;
38
-
39
-    /**
40
-     * The instance of db adapter
41
-     *
42
-     * @var \Analogue\ORM\Drivers\DBAdapter
43
-     */
44
-    protected $adapter;
45
-
46
-
47
-    /**
48
-     * Event dispatcher instance
49
-     *
50
-     * @var \Illuminate\Contracts\Events\Dispatcher
51
-     */
52
-    protected $dispatcher;
53
-
54
-    /**
55
-     * Entity Cache
56
-     *
57
-     * @var  \Analogue\ORM\System\EntityCache
58
-     */
59
-    protected $cache;
60
-
61
-    /**
62
-     * Global scopes
63
-     *
64
-     * @var array
65
-     */
66
-    protected $globalScopes = [];
67
-
68
-    /**
69
-     * Custom Commands
70
-     *
71
-     * @var array
72
-     */
73
-    protected $customCommands = [];
74
-
75
-    /**
76
-     * @param EntityMap  $entityMap
77
-     * @param DBAdapter  $adapter
78
-     * @param Dispatcher $dispatcher
79
-     * @param Manager    $manager
80
-     */
81
-    public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher $dispatcher, Manager $manager)
82
-    {
83
-        $this->entityMap = $entityMap;
84
-
85
-        $this->adapter = $adapter;
86
-
87
-        $this->dispatcher = $dispatcher;
88
-
89
-        $this->manager = $manager;
90
-
91
-        $this->cache = new EntityCache($entityMap);
92
-    }
93
-
94
-    /**
95
-     * Persist an entity or an entity collection into the database
96
-     *
97
-     * @param  Mappable|\Traversable|array $entity
98
-     * @throws \InvalidArgumentException
99
-     * @throws MappingException
100
-     * @return Mappable|\Traversable|array
101
-     */
102
-    public function store($entity)
103
-    {
104
-        if ($this->manager->isTraversable($entity)) {
105
-            return $this->storeCollection($entity);
106
-        } else {
107
-            return $this->storeEntity($entity);
108
-        }
109
-    }
110
-
111
-    /**
112
-     * Store an entity collection inside a single DB Transaction
113
-     *
114
-     * @param  \Traversable|array $entities
115
-     * @throws \InvalidArgumentException
116
-     * @throws MappingException
117
-     * @return \Traversable|array
118
-     */
119
-    protected function storeCollection($entities)
120
-    {
121
-        $this->adapter->beginTransaction();
122
-
123
-        foreach ($entities as $entity) {
124
-            $this->storeEntity($entity);
125
-        }
126
-
127
-        $this->adapter->commit();
128
-
129
-        return $entities;
130
-    }
131
-
132
-    /**
133
-     * Store a single entity into the database
134
-     *
135
-     * @param  Mappable $entity
136
-     * @throws \InvalidArgumentException
137
-     * @throws MappingException
138
-     * @return \Analogue\ORM\Entity
139
-     */
140
-    protected function storeEntity($entity)
141
-    {
142
-        $this->checkEntityType($entity);
143
-
144
-        $store = new Store($this->aggregate($entity), $this->newQueryBuilder());
145
-
146
-        return $store->execute();
147
-    }
148
-
149
-    /**
150
-     * Check that the entity correspond to the current mapper.
151
-     *
152
-     * @param  mixed $entity
153
-     * @throws InvalidArgumentException
154
-     * @return void
155
-     */
156
-    protected function checkEntityType($entity)
157
-    {
158
-        if (get_class($entity) != $this->entityMap->getClass()) {
159
-            $expected = $this->entityMap->getClass();
160
-            $actual = get_class($entity);
161
-            throw new InvalidArgumentException("Expected : $expected, got $actual.");
162
-        }
163
-    }
164
-
165
-    /**
166
-     * Convert an entity into an aggregate root
167
-     *
168
-     * @param  mixed $entity
169
-     * @throws MappingException
170
-     * @return \Analogue\ORM\System\Aggregate
171
-     */
172
-    protected function aggregate($entity)
173
-    {
174
-        return new Aggregate($entity);
175
-    }
176
-
177
-    /**
178
-     * Get a the Underlying QueryAdapter.
179
-     *
180
-     * @return \Analogue\ORM\Drivers\QueryAdapter
181
-     */
182
-    public function newQueryBuilder()
183
-    {
184
-        return $this->adapter->getQuery();
185
-    }
186
-
187
-    /**
188
-     * Delete an entity or an entity collection from the database
189
-     *
190
-     * @param  Mappable|\Traversable|array
191
-     * @throws MappingException
192
-     * @throws \InvalidArgumentException
193
-     * @return \Traversable|array
194
-     */
195
-    public function delete($entity)
196
-    {
197
-        if ($this->manager->isTraversable($entity)) {
198
-            return $this->deleteCollection($entity);
199
-        } else {
200
-            $this->deleteEntity($entity);
201
-        }
202
-    }
203
-
204
-    /**
205
-     * Delete an Entity Collection inside a single db transaction
206
-     *
207
-     * @param  \Traversable|array $entities
208
-     * @throws \InvalidArgumentException
209
-     * @throws MappingException
210
-     * @return \Traversable|array
211
-     */
212
-    protected function deleteCollection($entities)
213
-    {
214
-        $this->adapter->beginTransaction();
215
-
216
-        foreach ($entities as $entity) {
217
-            $this->deleteEntity($entity);
218
-        }
219
-
220
-        $this->adapter->commit();
221
-
222
-        return $entities;
223
-    }
224
-
225
-    /**
226
-     * Delete a single entity from the database.
227
-     *
228
-     * @param  Mappable $entity
229
-     * @throws \InvalidArgumentException
230
-     * @throws MappingException
231
-     * @return void
232
-     */
233
-    protected function deleteEntity($entity)
234
-    {
235
-        $this->checkEntityType($entity);
236
-
237
-        $delete = new Delete($this->aggregate($entity), $this->newQueryBuilder());
238
-
239
-        $delete->execute();
240
-    }
241
-
242
-    /**
243
-     * Return the entity map for this mapper
244
-     *
245
-     * @return EntityMap
246
-     */
247
-    public function getEntityMap()
248
-    {
249
-        return $this->entityMap;
250
-    }
251
-
252
-    /**
253
-     * Get the entity cache for the current mapper
254
-     *
255
-     * @return EntityCache  $entityCache
256
-     */
257
-    public function getEntityCache()
258
-    {
259
-        return $this->cache;
260
-    }
261
-
262
-    /**
263
-     * Fire the given event for the entity
264
-     *
265
-     * @param  string               $event
266
-     * @param  \Analogue\ORM\Entity $entity
267
-     * @param  bool                 $halt
268
-     * @throws InvalidArgumentException
269
-     * @return mixed
270
-     */
271
-    public function fireEvent($event, $entity, $halt = true)
272
-    {
273
-        if ($entity instanceof Wrapper) {
274
-            throw new InvalidArgumentException('Fired Event with invalid Entity Object');
275
-        }
276
-
277
-        $event = "analogue.{$event}." . $this->entityMap->getClass();
278
-
279
-        $method = $halt ? 'until' : 'fire';
280
-
281
-        return $this->dispatcher->$method($event, $entity);
282
-    }
283
-
284
-    /**
285
-     * Register an entity event with the dispatcher.
286
-     *
287
-     * @param  string   $event
288
-     * @param  \Closure $callback
289
-     * @return void
290
-     */
291
-    public function registerEvent($event, $callback)
292
-    {
293
-        $name = $this->entityMap->getClass();
294
-
295
-        $this->dispatcher->listen("analogue.{$event}.{$name}", $callback);
296
-    }
297
-
298
-    /**
299
-     * Add a global scope to this mapper query builder
300
-     *
301
-     * @param  ScopeInterface $scope
302
-     * @return void
303
-     */
304
-    public function addGlobalScope(ScopeInterface $scope)
305
-    {
306
-        $this->globalScopes[get_class($scope)] = $scope;
307
-    }
308
-
309
-    /**
310
-     * Determine if the mapper has a global scope.
311
-     *
312
-     * @param  \Analogue\ORM\System\ScopeInterface $scope
313
-     * @return bool
314
-     */
315
-    public function hasGlobalScope($scope)
316
-    {
317
-        return !is_null($this->getGlobalScope($scope));
318
-    }
319
-
320
-    /**
321
-     * Get a global scope registered with the modal.
322
-     *
323
-     * @param  \Analogue\ORM\System\ScopeInterface $scope
324
-     * @return \Analogue\ORM\System\ScopeInterface|null
325
-     */
326
-    public function getGlobalScope($scope)
327
-    {
328
-        return array_first($this->globalScopes, function ($key, $value) use ($scope) {
329
-            return $scope instanceof $value;
330
-        });
331
-    }
332
-
333
-    /**
334
-     * Get a new query instance without a given scope.
335
-     *
336
-     * @param  \Analogue\ORM\System\ScopeInterface $scope
337
-     * @return \Analogue\ORM\System\Query
338
-     */
339
-    public function newQueryWithoutScope($scope)
340
-    {
341
-        $this->getGlobalScope($scope)->remove($query = $this->getQuery(), $this);
342
-
343
-        return $query;
344
-    }
345
-
346
-    /**
347
-     * Get the Analogue Query Builder for this instance
348
-     *
349
-     * @return \Analogue\ORM\System\Query
350
-     */
351
-    public function getQuery()
352
-    {
353
-        $query = new Query($this, $this->adapter);
354
-
355
-        return $this->applyGlobalScopes($query);
356
-    }
357
-
358
-    /**
359
-     * Apply all of the global scopes to an Analogue Query builder.
360
-     *
361
-     * @param Query $query
362
-     * @return \Analogue\ORM\System\Query
363
-     */
364
-    public function applyGlobalScopes($query)
365
-    {
366
-        foreach ($this->getGlobalScopes() as $scope) {
367
-            $scope->apply($query, $this);
368
-        }
369
-
370
-        return $query;
371
-    }
372
-
373
-    /**
374
-     * Get the global scopes for this class instance.
375
-     *
376
-     * @return \Analogue\ORM\System\ScopeInterface
377
-     */
378
-    public function getGlobalScopes()
379
-    {
380
-        return $this->globalScopes;
381
-    }
382
-
383
-    /**
384
-     * Add a dynamic method that extends the mapper/repository
385
-     *
386
-     * @param string $command
387
-     */
388
-    public function addCustomCommand($command)
389
-    {
390
-        $name = lcfirst(class_basename($command));
391
-
392
-        $this->customCommands[$name] = $command;
393
-    }
394
-
395
-    /**
396
-     * Create a new instance of the mapped entity class
397
-     *
398
-     * @param  array $attributes
399
-     * @return mixed
400
-     */
401
-    public function newInstance($attributes = [])
402
-    {
403
-        $class = $this->entityMap->getClass();
404
-
405
-        if ($this->entityMap->activator() != null) {
406
-            $entity = $this->entityMap->activator();
407
-        } else {
408
-            $entity = $this->customClassInstance($class);
409
-        }
410
-
411
-        // prevent hydrating with an empty array
412
-        if (count($attributes) > 0) {
413
-            $entity->setEntityAttributes($attributes);
414
-        }
415
-
416
-        return $entity;
417
-    }
418
-
419
-    /**
420
-     * Use a trick to generate a class prototype that we
421
-     * can instantiate without calling the constructor.
422
-     *
423
-     * @param string|null $className
424
-     * @throws MappingException
425
-     * @return mixed
426
-     */
427
-    protected function customClassInstance($className)
428
-    {
429
-        if (!class_exists($className)) {
430
-            throw new MappingException("Tried to instantiate a non-existing Entity class : $className");
431
-        }
432
-
433
-        $prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
434
-
435
-        return $prototype;
436
-    }
437
-
438
-    /**
439
-     * Get an unscoped Analogue Query Builder for this instance
440
-     *
441
-     * @return \Analogue\ORM\System\Query
442
-     */
443
-    public function globalQuery()
444
-    {
445
-        return $this->newQueryWithoutScopes();
446
-    }
447
-
448
-    /**
449
-     * Get a new query builder that doesn't have any global scopes.
450
-     *
451
-     * @return Query
452
-     */
453
-    public function newQueryWithoutScopes()
454
-    {
455
-        return $this->removeGlobalScopes($this->getQuery());
456
-    }
457
-
458
-    /**
459
-     * Remove all of the global scopes from an Analogue Query builder.
460
-     *
461
-     * @param Query $query
462
-     * @return \Analogue\ORM\System\Query
463
-     */
464
-    public function removeGlobalScopes($query)
465
-    {
466
-        foreach ($this->getGlobalScopes() as $scope) {
467
-            $scope->remove($query, $this);
468
-        }
469
-
470
-        return $query;
471
-    }
472
-
473
-    /**
474
-     * Return the manager instance
475
-     *
476
-     * @return \Analogue\ORM\System\Manager
477
-     */
478
-    public function getManager()
479
-    {
480
-        return $this->manager;
481
-    }
482
-
483
-    /**
484
-     * Dynamically handle calls to custom commands, or Redirects to query()
485
-     *
486
-     * @param  string $method
487
-     * @param  array  $parameters
488
-     * @throws \Exception
489
-     * @return mixed
490
-     */
491
-    public function __call($method, $parameters)
492
-    {
493
-        // Check if method is a custom command on the mapper
494
-        if ($this->hasCustomCommand($method)) {
495
-            if (count($parameters) == 0) {
496
-                throw new \Exception("$method must at least have 1 argument");
497
-            }
498
-
499
-            return $this->executeCustomCommand($method, $parameters[0]);
500
-        }
501
-
502
-        // Redirect call on a new query instance
503
-        return call_user_func_array([$this->query(), $method], $parameters);
504
-    }
505
-
506
-    /**
507
-     * Check if this mapper supports this command
508
-     * @param  string $command
509
-     * @return boolean
510
-     */
511
-    public function hasCustomCommand($command)
512
-    {
513
-        return in_array($command, $this->getCustomCommands());
514
-    }
515
-
516
-    /**
517
-     * Get all the custom commands registered on this mapper
518
-     *
519
-     * @return array
520
-     */
521
-    public function getCustomCommands()
522
-    {
523
-        return array_keys($this->customCommands);
524
-    }
525
-
526
-    /**
527
-     * Execute a custom command on an Entity
528
-     *
529
-     * @param  string                 $command
530
-     * @param  mixed|Collection|array $entity
531
-     * @throws \InvalidArgumentException
532
-     * @throws MappingException
533
-     * @return mixed
534
-     */
535
-    public function executeCustomCommand($command, $entity)
536
-    {
537
-        $commandClass = $this->customCommands[$command];
538
-
539
-        if ($this->manager->isTraversable($entity)) {
540
-            foreach ($entity as $instance) {
541
-                $this->executeSingleCustomCommand($commandClass, $instance);
542
-            }
543
-        } else {
544
-            return $this->executeSingleCustomCommand($commandClass, $entity);
545
-        }
546
-    }
547
-
548
-    /**
549
-     * Execute a single command instance
550
-     *
551
-     * @param  string $commandClass
552
-     * @param  mixed  $entity
553
-     * @throws \InvalidArgumentException
554
-     * @throws MappingException
555
-     * @return mixed
556
-     */
557
-    protected function executeSingleCustomCommand($commandClass, $entity)
558
-    {
559
-        $this->checkEntityType($entity);
560
-
561
-        $instance = new $commandClass($this->aggregate($entity), $this->newQueryBuilder());
562
-
563
-        return $instance->execute();
564
-    }
565
-
566
-    /**
567
-     * Get the Analogue Query Builder for this instance
568
-     *
569
-     * @return \Analogue\ORM\System\Query
570
-     */
571
-    public function query()
572
-    {
573
-        return $this->getQuery();
574
-    }
25
+	/**
26
+	 * The Manager instance
27
+	 *
28
+	 * @var \Analogue\ORM\System\Manager
29
+	 */
30
+	protected $manager;
31
+
32
+	/**
33
+	 * Instance of EntityMapper Object
34
+	 *
35
+	 * @var \Analogue\ORM\EntityMap
36
+	 */
37
+	protected $entityMap;
38
+
39
+	/**
40
+	 * The instance of db adapter
41
+	 *
42
+	 * @var \Analogue\ORM\Drivers\DBAdapter
43
+	 */
44
+	protected $adapter;
45
+
46
+
47
+	/**
48
+	 * Event dispatcher instance
49
+	 *
50
+	 * @var \Illuminate\Contracts\Events\Dispatcher
51
+	 */
52
+	protected $dispatcher;
53
+
54
+	/**
55
+	 * Entity Cache
56
+	 *
57
+	 * @var  \Analogue\ORM\System\EntityCache
58
+	 */
59
+	protected $cache;
60
+
61
+	/**
62
+	 * Global scopes
63
+	 *
64
+	 * @var array
65
+	 */
66
+	protected $globalScopes = [];
67
+
68
+	/**
69
+	 * Custom Commands
70
+	 *
71
+	 * @var array
72
+	 */
73
+	protected $customCommands = [];
74
+
75
+	/**
76
+	 * @param EntityMap  $entityMap
77
+	 * @param DBAdapter  $adapter
78
+	 * @param Dispatcher $dispatcher
79
+	 * @param Manager    $manager
80
+	 */
81
+	public function __construct(EntityMap $entityMap, DBAdapter $adapter, Dispatcher $dispatcher, Manager $manager)
82
+	{
83
+		$this->entityMap = $entityMap;
84
+
85
+		$this->adapter = $adapter;
86
+
87
+		$this->dispatcher = $dispatcher;
88
+
89
+		$this->manager = $manager;
90
+
91
+		$this->cache = new EntityCache($entityMap);
92
+	}
93
+
94
+	/**
95
+	 * Persist an entity or an entity collection into the database
96
+	 *
97
+	 * @param  Mappable|\Traversable|array $entity
98
+	 * @throws \InvalidArgumentException
99
+	 * @throws MappingException
100
+	 * @return Mappable|\Traversable|array
101
+	 */
102
+	public function store($entity)
103
+	{
104
+		if ($this->manager->isTraversable($entity)) {
105
+			return $this->storeCollection($entity);
106
+		} else {
107
+			return $this->storeEntity($entity);
108
+		}
109
+	}
110
+
111
+	/**
112
+	 * Store an entity collection inside a single DB Transaction
113
+	 *
114
+	 * @param  \Traversable|array $entities
115
+	 * @throws \InvalidArgumentException
116
+	 * @throws MappingException
117
+	 * @return \Traversable|array
118
+	 */
119
+	protected function storeCollection($entities)
120
+	{
121
+		$this->adapter->beginTransaction();
122
+
123
+		foreach ($entities as $entity) {
124
+			$this->storeEntity($entity);
125
+		}
126
+
127
+		$this->adapter->commit();
128
+
129
+		return $entities;
130
+	}
131
+
132
+	/**
133
+	 * Store a single entity into the database
134
+	 *
135
+	 * @param  Mappable $entity
136
+	 * @throws \InvalidArgumentException
137
+	 * @throws MappingException
138
+	 * @return \Analogue\ORM\Entity
139
+	 */
140
+	protected function storeEntity($entity)
141
+	{
142
+		$this->checkEntityType($entity);
143
+
144
+		$store = new Store($this->aggregate($entity), $this->newQueryBuilder());
145
+
146
+		return $store->execute();
147
+	}
148
+
149
+	/**
150
+	 * Check that the entity correspond to the current mapper.
151
+	 *
152
+	 * @param  mixed $entity
153
+	 * @throws InvalidArgumentException
154
+	 * @return void
155
+	 */
156
+	protected function checkEntityType($entity)
157
+	{
158
+		if (get_class($entity) != $this->entityMap->getClass()) {
159
+			$expected = $this->entityMap->getClass();
160
+			$actual = get_class($entity);
161
+			throw new InvalidArgumentException("Expected : $expected, got $actual.");
162
+		}
163
+	}
164
+
165
+	/**
166
+	 * Convert an entity into an aggregate root
167
+	 *
168
+	 * @param  mixed $entity
169
+	 * @throws MappingException
170
+	 * @return \Analogue\ORM\System\Aggregate
171
+	 */
172
+	protected function aggregate($entity)
173
+	{
174
+		return new Aggregate($entity);
175
+	}
176
+
177
+	/**
178
+	 * Get a the Underlying QueryAdapter.
179
+	 *
180
+	 * @return \Analogue\ORM\Drivers\QueryAdapter
181
+	 */
182
+	public function newQueryBuilder()
183
+	{
184
+		return $this->adapter->getQuery();
185
+	}
186
+
187
+	/**
188
+	 * Delete an entity or an entity collection from the database
189
+	 *
190
+	 * @param  Mappable|\Traversable|array
191
+	 * @throws MappingException
192
+	 * @throws \InvalidArgumentException
193
+	 * @return \Traversable|array
194
+	 */
195
+	public function delete($entity)
196
+	{
197
+		if ($this->manager->isTraversable($entity)) {
198
+			return $this->deleteCollection($entity);
199
+		} else {
200
+			$this->deleteEntity($entity);
201
+		}
202
+	}
203
+
204
+	/**
205
+	 * Delete an Entity Collection inside a single db transaction
206
+	 *
207
+	 * @param  \Traversable|array $entities
208
+	 * @throws \InvalidArgumentException
209
+	 * @throws MappingException
210
+	 * @return \Traversable|array
211
+	 */
212
+	protected function deleteCollection($entities)
213
+	{
214
+		$this->adapter->beginTransaction();
215
+
216
+		foreach ($entities as $entity) {
217
+			$this->deleteEntity($entity);
218
+		}
219
+
220
+		$this->adapter->commit();
221
+
222
+		return $entities;
223
+	}
224
+
225
+	/**
226
+	 * Delete a single entity from the database.
227
+	 *
228
+	 * @param  Mappable $entity
229
+	 * @throws \InvalidArgumentException
230
+	 * @throws MappingException
231
+	 * @return void
232
+	 */
233
+	protected function deleteEntity($entity)
234
+	{
235
+		$this->checkEntityType($entity);
236
+
237
+		$delete = new Delete($this->aggregate($entity), $this->newQueryBuilder());
238
+
239
+		$delete->execute();
240
+	}
241
+
242
+	/**
243
+	 * Return the entity map for this mapper
244
+	 *
245
+	 * @return EntityMap
246
+	 */
247
+	public function getEntityMap()
248
+	{
249
+		return $this->entityMap;
250
+	}
251
+
252
+	/**
253
+	 * Get the entity cache for the current mapper
254
+	 *
255
+	 * @return EntityCache  $entityCache
256
+	 */
257
+	public function getEntityCache()
258
+	{
259
+		return $this->cache;
260
+	}
261
+
262
+	/**
263
+	 * Fire the given event for the entity
264
+	 *
265
+	 * @param  string               $event
266
+	 * @param  \Analogue\ORM\Entity $entity
267
+	 * @param  bool                 $halt
268
+	 * @throws InvalidArgumentException
269
+	 * @return mixed
270
+	 */
271
+	public function fireEvent($event, $entity, $halt = true)
272
+	{
273
+		if ($entity instanceof Wrapper) {
274
+			throw new InvalidArgumentException('Fired Event with invalid Entity Object');
275
+		}
276
+
277
+		$event = "analogue.{$event}." . $this->entityMap->getClass();
278
+
279
+		$method = $halt ? 'until' : 'fire';
280
+
281
+		return $this->dispatcher->$method($event, $entity);
282
+	}
283
+
284
+	/**
285
+	 * Register an entity event with the dispatcher.
286
+	 *
287
+	 * @param  string   $event
288
+	 * @param  \Closure $callback
289
+	 * @return void
290
+	 */
291
+	public function registerEvent($event, $callback)
292
+	{
293
+		$name = $this->entityMap->getClass();
294
+
295
+		$this->dispatcher->listen("analogue.{$event}.{$name}", $callback);
296
+	}
297
+
298
+	/**
299
+	 * Add a global scope to this mapper query builder
300
+	 *
301
+	 * @param  ScopeInterface $scope
302
+	 * @return void
303
+	 */
304
+	public function addGlobalScope(ScopeInterface $scope)
305
+	{
306
+		$this->globalScopes[get_class($scope)] = $scope;
307
+	}
308
+
309
+	/**
310
+	 * Determine if the mapper has a global scope.
311
+	 *
312
+	 * @param  \Analogue\ORM\System\ScopeInterface $scope
313
+	 * @return bool
314
+	 */
315
+	public function hasGlobalScope($scope)
316
+	{
317
+		return !is_null($this->getGlobalScope($scope));
318
+	}
319
+
320
+	/**
321
+	 * Get a global scope registered with the modal.
322
+	 *
323
+	 * @param  \Analogue\ORM\System\ScopeInterface $scope
324
+	 * @return \Analogue\ORM\System\ScopeInterface|null
325
+	 */
326
+	public function getGlobalScope($scope)
327
+	{
328
+		return array_first($this->globalScopes, function ($key, $value) use ($scope) {
329
+			return $scope instanceof $value;
330
+		});
331
+	}
332
+
333
+	/**
334
+	 * Get a new query instance without a given scope.
335
+	 *
336
+	 * @param  \Analogue\ORM\System\ScopeInterface $scope
337
+	 * @return \Analogue\ORM\System\Query
338
+	 */
339
+	public function newQueryWithoutScope($scope)
340
+	{
341
+		$this->getGlobalScope($scope)->remove($query = $this->getQuery(), $this);
342
+
343
+		return $query;
344
+	}
345
+
346
+	/**
347
+	 * Get the Analogue Query Builder for this instance
348
+	 *
349
+	 * @return \Analogue\ORM\System\Query
350
+	 */
351
+	public function getQuery()
352
+	{
353
+		$query = new Query($this, $this->adapter);
354
+
355
+		return $this->applyGlobalScopes($query);
356
+	}
357
+
358
+	/**
359
+	 * Apply all of the global scopes to an Analogue Query builder.
360
+	 *
361
+	 * @param Query $query
362
+	 * @return \Analogue\ORM\System\Query
363
+	 */
364
+	public function applyGlobalScopes($query)
365
+	{
366
+		foreach ($this->getGlobalScopes() as $scope) {
367
+			$scope->apply($query, $this);
368
+		}
369
+
370
+		return $query;
371
+	}
372
+
373
+	/**
374
+	 * Get the global scopes for this class instance.
375
+	 *
376
+	 * @return \Analogue\ORM\System\ScopeInterface
377
+	 */
378
+	public function getGlobalScopes()
379
+	{
380
+		return $this->globalScopes;
381
+	}
382
+
383
+	/**
384
+	 * Add a dynamic method that extends the mapper/repository
385
+	 *
386
+	 * @param string $command
387
+	 */
388
+	public function addCustomCommand($command)
389
+	{
390
+		$name = lcfirst(class_basename($command));
391
+
392
+		$this->customCommands[$name] = $command;
393
+	}
394
+
395
+	/**
396
+	 * Create a new instance of the mapped entity class
397
+	 *
398
+	 * @param  array $attributes
399
+	 * @return mixed
400
+	 */
401
+	public function newInstance($attributes = [])
402
+	{
403
+		$class = $this->entityMap->getClass();
404
+
405
+		if ($this->entityMap->activator() != null) {
406
+			$entity = $this->entityMap->activator();
407
+		} else {
408
+			$entity = $this->customClassInstance($class);
409
+		}
410
+
411
+		// prevent hydrating with an empty array
412
+		if (count($attributes) > 0) {
413
+			$entity->setEntityAttributes($attributes);
414
+		}
415
+
416
+		return $entity;
417
+	}
418
+
419
+	/**
420
+	 * Use a trick to generate a class prototype that we
421
+	 * can instantiate without calling the constructor.
422
+	 *
423
+	 * @param string|null $className
424
+	 * @throws MappingException
425
+	 * @return mixed
426
+	 */
427
+	protected function customClassInstance($className)
428
+	{
429
+		if (!class_exists($className)) {
430
+			throw new MappingException("Tried to instantiate a non-existing Entity class : $className");
431
+		}
432
+
433
+		$prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($className), $className));
434
+
435
+		return $prototype;
436
+	}
437
+
438
+	/**
439
+	 * Get an unscoped Analogue Query Builder for this instance
440
+	 *
441
+	 * @return \Analogue\ORM\System\Query
442
+	 */
443
+	public function globalQuery()
444
+	{
445
+		return $this->newQueryWithoutScopes();
446
+	}
447
+
448
+	/**
449
+	 * Get a new query builder that doesn't have any global scopes.
450
+	 *
451
+	 * @return Query
452
+	 */
453
+	public function newQueryWithoutScopes()
454
+	{
455
+		return $this->removeGlobalScopes($this->getQuery());
456
+	}
457
+
458
+	/**
459
+	 * Remove all of the global scopes from an Analogue Query builder.
460
+	 *
461
+	 * @param Query $query
462
+	 * @return \Analogue\ORM\System\Query
463
+	 */
464
+	public function removeGlobalScopes($query)
465
+	{
466
+		foreach ($this->getGlobalScopes() as $scope) {
467
+			$scope->remove($query, $this);
468
+		}
469
+
470
+		return $query;
471
+	}
472
+
473
+	/**
474
+	 * Return the manager instance
475
+	 *
476
+	 * @return \Analogue\ORM\System\Manager
477
+	 */
478
+	public function getManager()
479
+	{
480
+		return $this->manager;
481
+	}
482
+
483
+	/**
484
+	 * Dynamically handle calls to custom commands, or Redirects to query()
485
+	 *
486
+	 * @param  string $method
487
+	 * @param  array  $parameters
488
+	 * @throws \Exception
489
+	 * @return mixed
490
+	 */
491
+	public function __call($method, $parameters)
492
+	{
493
+		// Check if method is a custom command on the mapper
494
+		if ($this->hasCustomCommand($method)) {
495
+			if (count($parameters) == 0) {
496
+				throw new \Exception("$method must at least have 1 argument");
497
+			}
498
+
499
+			return $this->executeCustomCommand($method, $parameters[0]);
500
+		}
501
+
502
+		// Redirect call on a new query instance
503
+		return call_user_func_array([$this->query(), $method], $parameters);
504
+	}
505
+
506
+	/**
507
+	 * Check if this mapper supports this command
508
+	 * @param  string $command
509
+	 * @return boolean
510
+	 */
511
+	public function hasCustomCommand($command)
512
+	{
513
+		return in_array($command, $this->getCustomCommands());
514
+	}
515
+
516
+	/**
517
+	 * Get all the custom commands registered on this mapper
518
+	 *
519
+	 * @return array
520
+	 */
521
+	public function getCustomCommands()
522
+	{
523
+		return array_keys($this->customCommands);
524
+	}
525
+
526
+	/**
527
+	 * Execute a custom command on an Entity
528
+	 *
529
+	 * @param  string                 $command
530
+	 * @param  mixed|Collection|array $entity
531
+	 * @throws \InvalidArgumentException
532
+	 * @throws MappingException
533
+	 * @return mixed
534
+	 */
535
+	public function executeCustomCommand($command, $entity)
536
+	{
537
+		$commandClass = $this->customCommands[$command];
538
+
539
+		if ($this->manager->isTraversable($entity)) {
540
+			foreach ($entity as $instance) {
541
+				$this->executeSingleCustomCommand($commandClass, $instance);
542
+			}
543
+		} else {
544
+			return $this->executeSingleCustomCommand($commandClass, $entity);
545
+		}
546
+	}
547
+
548
+	/**
549
+	 * Execute a single command instance
550
+	 *
551
+	 * @param  string $commandClass
552
+	 * @param  mixed  $entity
553
+	 * @throws \InvalidArgumentException
554
+	 * @throws MappingException
555
+	 * @return mixed
556
+	 */
557
+	protected function executeSingleCustomCommand($commandClass, $entity)
558
+	{
559
+		$this->checkEntityType($entity);
560
+
561
+		$instance = new $commandClass($this->aggregate($entity), $this->newQueryBuilder());
562
+
563
+		return $instance->execute();
564
+	}
565
+
566
+	/**
567
+	 * Get the Analogue Query Builder for this instance
568
+	 *
569
+	 * @return \Analogue\ORM\System\Query
570
+	 */
571
+	public function query()
572
+	{
573
+		return $this->getQuery();
574
+	}
575 575
 }
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -177,7 +177,7 @@
 block discarded – undo
177 177
     /**
178 178
      * Get a the Underlying QueryAdapter.
179 179
      *
180
-     * @return \Analogue\ORM\Drivers\QueryAdapter
180
+     * @return \Illuminate\Database\Query\Builder
181 181
      */
182 182
     public function newQueryBuilder()
183 183
     {
Please login to merge, or discard this patch.
src/System/Query.php 2 patches
Indentation   +837 added lines, -837 removed lines patch added patch discarded remove patch
@@ -20,846 +20,846 @@
 block discarded – undo
20 20
  */
21 21
 class Query
22 22
 {
23
-    /**
24
-     * Mapper Instance
25
-     *
26
-     * @var \Analogue\ORM\System\Mapper
27
-     */
28
-    protected $mapper;
29
-
30
-    /**
31
-     * DB Adatper
32
-     *
33
-     * @var \Analogue\ORM\Drivers\DBAdapter
34
-     */
35
-    protected $adapter;
36
-
37
-    /**
38
-     * Query Builder Instance
39
-     *
40
-     * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
41
-     */
42
-    protected $query;
43
-
44
-    /**
45
-     * Entity Map Instance
46
-     *
47
-     * @var \Analogue\ORM\EntityMap
48
-     */
49
-    protected $entityMap;
23
+	/**
24
+	 * Mapper Instance
25
+	 *
26
+	 * @var \Analogue\ORM\System\Mapper
27
+	 */
28
+	protected $mapper;
29
+
30
+	/**
31
+	 * DB Adatper
32
+	 *
33
+	 * @var \Analogue\ORM\Drivers\DBAdapter
34
+	 */
35
+	protected $adapter;
36
+
37
+	/**
38
+	 * Query Builder Instance
39
+	 *
40
+	 * @var \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
41
+	 */
42
+	protected $query;
43
+
44
+	/**
45
+	 * Entity Map Instance
46
+	 *
47
+	 * @var \Analogue\ORM\EntityMap
48
+	 */
49
+	protected $entityMap;
50 50
     
51
-    /**
52
-     * The relationships that should be eager loaded.
53
-     *
54
-     * @var array
55
-     */
56
-    protected $eagerLoad = [];
57
-
58
-    /**
59
-     * All of the registered builder macros.
60
-     *
61
-     * @var array
62
-     */
63
-    protected $macros = [];
64
-
65
-    /**
66
-     * The methods that should be returned from query builder.
67
-     *
68
-     * @var array
69
-     */
70
-    protected $passthru = [
71
-        'toSql',
72
-        'lists',
73
-        'pluck',
74
-        'count',
75
-        'min',
76
-        'max',
77
-        'avg',
78
-        'sum',
79
-        'exists',
80
-        'getBindings',
81
-    ];
82
-
83
-    /**
84
-     * Query Builder Blacklist
85
-     */
86
-    protected $blacklist = [
87
-        'insert',
88
-        'insertGetId',
89
-        'lock',
90
-        'lockForUpdate',
91
-        'sharedLock',
92
-        'update',
93
-        'increment',
94
-        'decrement',
95
-        'delete',
96
-        'truncate',
97
-        'raw',
98
-    ];
99
-
100
-    /**
101
-     * Create a new Analogue Query Builder instance.
102
-     *
103
-     * @param  Mapper    $mapper
104
-     * @param  DBAdapter $adapter
105
-     */
106
-    public function __construct(Mapper $mapper, DBAdapter $adapter)
107
-    {
108
-        $this->mapper = $mapper;
109
-
110
-        $this->adapter = $adapter;
111
-
112
-        $this->entityMap = $mapper->getEntityMap();
113
-
114
-        // Specify the table to work on
115
-        $this->query = $adapter->getQuery()->from($this->entityMap->getTable());
116
-
117
-        $this->with($this->entityMap->getEagerloadedRelationships());
118
-    }
119
-
120
-    /**
121
-     * Run the query and return the result
122
-     *
123
-     * @param  array $columns
124
-     * @return \Analogue\ORM\EntityCollection
125
-     */
126
-    public function get($columns = ['*'])
127
-    {
128
-        $entities = $this->getEntities($columns);
129
-
130
-        // If we actually found models we will also eager load any relationships that
131
-        // have been specified as needing to be eager loaded, which will solve the
132
-        // n+1 query issue for the developers to avoid running a lot of queries.
133
-
134
-        if (count($entities) > 0) {
135
-            $entities = $this->eagerLoadRelations($entities);
136
-        }
137
-
138
-        return $this->entityMap->newCollection($entities);
139
-    }
140
-
141
-    /**
142
-     * Find an entity by its primary key
143
-     *
144
-     * @param  string|integer $id
145
-     * @param  array          $columns
146
-     * @return \Analogue\ORM\Mappable
147
-     */
148
-    public function find($id, $columns = ['*'])
149
-    {
150
-        if (is_array($id)) {
151
-            return $this->findMany($id, $columns);
152
-        }
153
-
154
-        $this->query->where($this->entityMap->getQualifiedKeyName(), '=', $id);
155
-
156
-        return $this->first($columns);
157
-    }
158
-
159
-    /**
160
-     * Find many entities by their primary keys.
161
-     *
162
-     * @param  array $id
163
-     * @param  array $columns
164
-     * @return EntityCollection
165
-     */
166
-    public function findMany($id, $columns = ['*'])
167
-    {
168
-        if (empty($id)) {
169
-            return new EntityCollection;
170
-        }
171
-
172
-        $this->query->whereIn($this->entityMap->getQualifiedKeyName(), $id);
173
-
174
-        return $this->get($columns);
175
-    }
176
-
177
-    /**
178
-     * Find a model by its primary key or throw an exception.
179
-     *
180
-     * @param  mixed $id
181
-     * @param  array $columns
182
-     * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
183
-     * @return mixed|self
184
-     */
185
-    public function findOrFail($id, $columns = ['*'])
186
-    {
187
-        if (!is_null($entity = $this->find($id, $columns))) {
188
-            return $entity;
189
-        }
190
-
191
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
192
-    }
193
-
194
-
195
-    /**
196
-     * Execute the query and get the first result.
197
-     *
198
-     * @param  array $columns
199
-     * @return \Analogue\ORM\Entity
200
-     */
201
-    public function first($columns = ['*'])
202
-    {
203
-        return $this->take(1)->get($columns)->first();
204
-    }
205
-
206
-    /**
207
-     * Execute the query and get the first result or throw an exception.
208
-     *
209
-     * @param  array $columns
210
-     * @throws EntityNotFoundException
211
-     * @return \Analogue\ORM\Entity
212
-     */
213
-    public function firstOrFail($columns = ['*'])
214
-    {
215
-        if (!is_null($entity = $this->first($columns))) {
216
-            return $entity;
217
-        }
218
-
219
-        throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
220
-    }
221
-
222
-    /**
223
-     * Pluck a single column from the database.
224
-     *
225
-     * @param  string $column
226
-     * @return mixed
227
-     */
228
-    public function pluck($column)
229
-    {
230
-        $result = $this->first([$column]);
231
-
232
-        if ($result) {
233
-            return $result->{$column};
234
-        }
235
-    }
236
-
237
-    /**
238
-     * Chunk the results of the query.
239
-     *
240
-     * @param  int      $count
241
-     * @param  callable $callback
242
-     * @return void
243
-     */
244
-    public function chunk($count, callable $callback)
245
-    {
246
-        $results = $this->forPage($page = 1, $count)->get();
247
-
248
-        while (count($results) > 0) {
249
-            // On each chunk result set, we will pass them to the callback and then let the
250
-            // developer take care of everything within the callback, which allows us to
251
-            // keep the memory low for spinning through large result sets for working.
252
-            call_user_func($callback, $results);
253
-
254
-            $page++;
255
-
256
-            $results = $this->forPage($page, $count)->get();
257
-        }
258
-    }
51
+	/**
52
+	 * The relationships that should be eager loaded.
53
+	 *
54
+	 * @var array
55
+	 */
56
+	protected $eagerLoad = [];
57
+
58
+	/**
59
+	 * All of the registered builder macros.
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $macros = [];
64
+
65
+	/**
66
+	 * The methods that should be returned from query builder.
67
+	 *
68
+	 * @var array
69
+	 */
70
+	protected $passthru = [
71
+		'toSql',
72
+		'lists',
73
+		'pluck',
74
+		'count',
75
+		'min',
76
+		'max',
77
+		'avg',
78
+		'sum',
79
+		'exists',
80
+		'getBindings',
81
+	];
82
+
83
+	/**
84
+	 * Query Builder Blacklist
85
+	 */
86
+	protected $blacklist = [
87
+		'insert',
88
+		'insertGetId',
89
+		'lock',
90
+		'lockForUpdate',
91
+		'sharedLock',
92
+		'update',
93
+		'increment',
94
+		'decrement',
95
+		'delete',
96
+		'truncate',
97
+		'raw',
98
+	];
99
+
100
+	/**
101
+	 * Create a new Analogue Query Builder instance.
102
+	 *
103
+	 * @param  Mapper    $mapper
104
+	 * @param  DBAdapter $adapter
105
+	 */
106
+	public function __construct(Mapper $mapper, DBAdapter $adapter)
107
+	{
108
+		$this->mapper = $mapper;
109
+
110
+		$this->adapter = $adapter;
111
+
112
+		$this->entityMap = $mapper->getEntityMap();
113
+
114
+		// Specify the table to work on
115
+		$this->query = $adapter->getQuery()->from($this->entityMap->getTable());
116
+
117
+		$this->with($this->entityMap->getEagerloadedRelationships());
118
+	}
119
+
120
+	/**
121
+	 * Run the query and return the result
122
+	 *
123
+	 * @param  array $columns
124
+	 * @return \Analogue\ORM\EntityCollection
125
+	 */
126
+	public function get($columns = ['*'])
127
+	{
128
+		$entities = $this->getEntities($columns);
129
+
130
+		// If we actually found models we will also eager load any relationships that
131
+		// have been specified as needing to be eager loaded, which will solve the
132
+		// n+1 query issue for the developers to avoid running a lot of queries.
133
+
134
+		if (count($entities) > 0) {
135
+			$entities = $this->eagerLoadRelations($entities);
136
+		}
137
+
138
+		return $this->entityMap->newCollection($entities);
139
+	}
140
+
141
+	/**
142
+	 * Find an entity by its primary key
143
+	 *
144
+	 * @param  string|integer $id
145
+	 * @param  array          $columns
146
+	 * @return \Analogue\ORM\Mappable
147
+	 */
148
+	public function find($id, $columns = ['*'])
149
+	{
150
+		if (is_array($id)) {
151
+			return $this->findMany($id, $columns);
152
+		}
153
+
154
+		$this->query->where($this->entityMap->getQualifiedKeyName(), '=', $id);
155
+
156
+		return $this->first($columns);
157
+	}
158
+
159
+	/**
160
+	 * Find many entities by their primary keys.
161
+	 *
162
+	 * @param  array $id
163
+	 * @param  array $columns
164
+	 * @return EntityCollection
165
+	 */
166
+	public function findMany($id, $columns = ['*'])
167
+	{
168
+		if (empty($id)) {
169
+			return new EntityCollection;
170
+		}
171
+
172
+		$this->query->whereIn($this->entityMap->getQualifiedKeyName(), $id);
173
+
174
+		return $this->get($columns);
175
+	}
176
+
177
+	/**
178
+	 * Find a model by its primary key or throw an exception.
179
+	 *
180
+	 * @param  mixed $id
181
+	 * @param  array $columns
182
+	 * @throws \Analogue\ORM\Exceptions\EntityNotFoundException
183
+	 * @return mixed|self
184
+	 */
185
+	public function findOrFail($id, $columns = ['*'])
186
+	{
187
+		if (!is_null($entity = $this->find($id, $columns))) {
188
+			return $entity;
189
+		}
190
+
191
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
192
+	}
193
+
194
+
195
+	/**
196
+	 * Execute the query and get the first result.
197
+	 *
198
+	 * @param  array $columns
199
+	 * @return \Analogue\ORM\Entity
200
+	 */
201
+	public function first($columns = ['*'])
202
+	{
203
+		return $this->take(1)->get($columns)->first();
204
+	}
205
+
206
+	/**
207
+	 * Execute the query and get the first result or throw an exception.
208
+	 *
209
+	 * @param  array $columns
210
+	 * @throws EntityNotFoundException
211
+	 * @return \Analogue\ORM\Entity
212
+	 */
213
+	public function firstOrFail($columns = ['*'])
214
+	{
215
+		if (!is_null($entity = $this->first($columns))) {
216
+			return $entity;
217
+		}
218
+
219
+		throw (new EntityNotFoundException)->setEntity(get_class($this->entityMap));
220
+	}
221
+
222
+	/**
223
+	 * Pluck a single column from the database.
224
+	 *
225
+	 * @param  string $column
226
+	 * @return mixed
227
+	 */
228
+	public function pluck($column)
229
+	{
230
+		$result = $this->first([$column]);
231
+
232
+		if ($result) {
233
+			return $result->{$column};
234
+		}
235
+	}
236
+
237
+	/**
238
+	 * Chunk the results of the query.
239
+	 *
240
+	 * @param  int      $count
241
+	 * @param  callable $callback
242
+	 * @return void
243
+	 */
244
+	public function chunk($count, callable $callback)
245
+	{
246
+		$results = $this->forPage($page = 1, $count)->get();
247
+
248
+		while (count($results) > 0) {
249
+			// On each chunk result set, we will pass them to the callback and then let the
250
+			// developer take care of everything within the callback, which allows us to
251
+			// keep the memory low for spinning through large result sets for working.
252
+			call_user_func($callback, $results);
253
+
254
+			$page++;
255
+
256
+			$results = $this->forPage($page, $count)->get();
257
+		}
258
+	}
259 259
     
260
-    /**
261
-     * Get an array with the values of a given column.
262
-     *
263
-     * @param  string $column
264
-     * @param  string $key
265
-     * @return array
266
-     */
267
-    public function lists($column, $key = null)
268
-    {
269
-        return $this->query->lists($column, $key);
270
-    }
271
-
272
-    /**
273
-     * Get a paginator for the "select" statement.
274
-     *
275
-     * @param  int   $perPage
276
-     * @param  array $columns
277
-     * @return LengthAwarePaginator
278
-     */
279
-    public function paginate($perPage = null, $columns = ['*'])
280
-    {
281
-        $total = $this->query->getCountForPagination();
282
-
283
-        $this->query->forPage(
284
-            $page = Paginator::resolveCurrentPage(),
285
-            $perPage = $perPage ?: $this->entityMap->getPerPage()
286
-        );
287
-
288
-        return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
289
-            'path' => Paginator::resolveCurrentPath()
290
-        ]);
291
-    }
292
-
293
-    /**
294
-     * Get a paginator for a grouped statement.
295
-     *
296
-     * @param  \Illuminate\Pagination\Factory $paginator
297
-     * @param  int                            $perPage
298
-     * @param  array                          $columns
299
-     * @return \Illuminate\Pagination\Paginator
300
-     */
301
-    protected function groupedPaginate($paginator, $perPage, $columns)
302
-    {
303
-        $results = $this->get($columns)->all();
304
-
305
-        return $this->query->buildRawPaginator($paginator, $results, $perPage);
306
-    }
307
-
308
-    /**
309
-     * Get a paginator for an ungrouped statement.
310
-     *
311
-     * @param  \Illuminate\Pagination\Factory $paginator
312
-     * @param  int                            $perPage
313
-     * @param  array                          $columns
314
-     * @return \Illuminate\Pagination\Paginator
315
-     */
316
-    protected function ungroupedPaginate($paginator, $perPage, $columns)
317
-    {
318
-        $total = $this->query->getPaginationCount();
319
-
320
-        // Once we have the paginator we need to set the limit and offset values for
321
-        // the query so we can get the properly paginated items. Once we have an
322
-        // array of items we can create the paginator instances for the items.
323
-        $page = $paginator->getCurrentPage($total);
324
-
325
-        $this->query->forPage($page, $perPage);
326
-
327
-        return $paginator->make($this->get($columns)->all(), $total, $perPage);
328
-    }
329
-
330
-    /**
331
-     * Paginate the given query into a simple paginator.
332
-     *
333
-     * @param  int   $perPage
334
-     * @param  array $columns
335
-     * @return \Illuminate\Contracts\Pagination\Paginator
336
-     */
337
-    public function simplePaginate($perPage = null, $columns = ['*'])
338
-    {
339
-        $page = Paginator::resolveCurrentPage();
340
-
341
-        $perPage = $perPage ?: $this->entityMap->getPerPage();
342
-
343
-        $this->skip(($page - 1) * $perPage)->take($perPage + 1);
344
-
345
-        return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
346
-    }
347
-
348
-    /**
349
-     * Add a basic where clause to the query.
350
-     *
351
-     * @param  string $column
352
-     * @param  string $operator
353
-     * @param  mixed  $value
354
-     * @param  string $boolean
355
-     * @return $this
356
-     */
357
-    public function where($column, $operator = null, $value = null, $boolean = 'and')
358
-    {
359
-        if ($column instanceof Closure) {
360
-            $query = $this->newQueryWithoutScopes();
361
-
362
-            call_user_func($column, $query);
363
-
364
-            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
365
-        } else {
366
-            call_user_func_array([$this->query, 'where'], func_get_args());
367
-        }
368
-
369
-        return $this;
370
-    }
371
-
372
-    /**
373
-     * Add an "or where" clause to the query.
374
-     *
375
-     * @param  string $column
376
-     * @param  string $operator
377
-     * @param  mixed  $value
378
-     * @return \Analogue\ORM\System\Query
379
-     */
380
-    public function orWhere($column, $operator = null, $value = null)
381
-    {
382
-        return $this->where($column, $operator, $value, 'or');
383
-    }
384
-
385
-    /**
386
-     * Add a relationship count condition to the query.
387
-     *
388
-     * @param  string   $relation
389
-     * @param  string   $operator
390
-     * @param  int      $count
391
-     * @param  string   $boolean
392
-     * @param  \Closure $callback
393
-     * @return \Analogue\ORM\System\Query
394
-     */
395
-    public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
396
-    {
397
-        $entity = $this->mapper->newInstance();
398
-
399
-        $relation = $this->getHasRelationQuery($relation, $entity);
400
-
401
-        $query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
402
-
403
-        if ($callback) {
404
-            call_user_func($callback, $query);
405
-        }
406
-
407
-        return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
408
-    }
409
-
410
-    /**
411
-     * Add a relationship count condition to the query with where clauses.
412
-     *
413
-     * @param  string   $relation
414
-     * @param  \Closure $callback
415
-     * @param  string   $operator
416
-     * @param  int      $count
417
-     * @return \Analogue\ORM\System\Query
418
-     */
419
-    public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
420
-    {
421
-        return $this->has($relation, $operator, $count, 'and', $callback);
422
-    }
423
-
424
-    /**
425
-     * Add a relationship count condition to the query with an "or".
426
-     *
427
-     * @param  string $relation
428
-     * @param  string $operator
429
-     * @param  int    $count
430
-     * @return \Analogue\ORM\System\Query
431
-     */
432
-    public function orHas($relation, $operator = '>=', $count = 1)
433
-    {
434
-        return $this->has($relation, $operator, $count, 'or');
435
-    }
436
-
437
-    /**
438
-     * Add a relationship count condition to the query with where clauses and an "or".
439
-     *
440
-     * @param  string   $relation
441
-     * @param  \Closure $callback
442
-     * @param  string   $operator
443
-     * @param  int      $count
444
-     * @return \Analogue\ORM\System\Query
445
-     */
446
-    public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
447
-    {
448
-        return $this->has($relation, $operator, $count, 'or', $callback);
449
-    }
450
-
451
-    /**
452
-     * Add the "has" condition where clause to the query.
453
-     *
454
-     * @param  \Analogue\ORM\System\Query               $hasQuery
455
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
456
-     * @param  string                                   $operator
457
-     * @param  int                                      $count
458
-     * @param  string                                   $boolean
459
-     * @return \Analogue\ORM\System\Query
460
-     */
461
-    protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
462
-    {
463
-        $this->mergeWheresToHas($hasQuery, $relation);
464
-
465
-        if (is_numeric($count)) {
466
-            $count = new Expression($count);
467
-        }
468
-
469
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
470
-    }
471
-
472
-    /**
473
-     * Merge the "wheres" from a relation query to a has query.
474
-     *
475
-     * @param  \Analogue\ORM\System\Query               $hasQuery
476
-     * @param  \Analogue\ORM\Relationships\Relationship $relation
477
-     * @return void
478
-     */
479
-    protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
480
-    {
481
-        // Here we have the "has" query and the original relation. We need to copy over any
482
-        // where clauses the developer may have put in the relationship function over to
483
-        // the has query, and then copy the bindings from the "has" query to the main.
484
-        $relationQuery = $relation->getBaseQuery();
485
-
486
-        $hasQuery->mergeWheres(
487
-            $relationQuery->wheres, $relationQuery->getBindings()
488
-        );
489
-
490
-        $this->query->mergeBindings($hasQuery->getQuery());
491
-    }
492
-
493
-    /**
494
-     * Get the "has relation" base query instance.
495
-     *
496
-     * @param  string $relation
497
-     * @param         $entity
498
-     * @return \Analogue\ORM\System\Query
499
-     */
500
-    protected function getHasRelationQuery($relation, $entity)
501
-    {
502
-        return Relationship::noConstraints(function () use ($relation, $entity) {
503
-            return $this->entityMap->$relation($entity);
504
-        });
505
-    }
506
-
507
-    /**
508
-     * Get the table for the current query object
509
-     *
510
-     * @return string
511
-     */
512
-    public function getTable()
513
-    {
514
-        return $this->entityMap->getTable();
515
-    }
516
-
517
-    /**
518
-     * Set the relationships that should be eager loaded.
519
-     *
520
-     * @param  mixed $relations
521
-     * @return $this
522
-     */
523
-    public function with($relations)
524
-    {
525
-        if (is_string($relations)) {
526
-            $relations = func_get_args();
527
-        }
528
-
529
-        $eagers = $this->parseRelations($relations);
530
-
531
-        $this->eagerLoad = array_merge($this->eagerLoad, $eagers);
532
-
533
-        return $this;
534
-    }
535
-
536
-    /**
537
-     * Parse a list of relations into individuals.
538
-     *
539
-     * @param  array $relations
540
-     * @return array
541
-     */
542
-    protected function parseRelations(array $relations)
543
-    {
544
-        $results = [];
545
-
546
-        foreach ($relations as $name => $constraints) {
547
-            // If the "relation" value is actually a numeric key, we can assume that no
548
-            // constraints have been specified for the eager load and we'll just put
549
-            // an empty Closure with the loader so that we can treat all the same.
550
-            if (is_numeric($name)) {
551
-                $f = function () {};
552
-
553
-                list($name, $constraints) = [$constraints, $f];
554
-            }
555
-
556
-            // We need to separate out any nested includes. Which allows the developers
557
-            // to load deep relationships using "dots" without stating each level of
558
-            // the relationship with its own key in the array of eager load names.
559
-            $results = $this->parseNested($name, $results);
560
-
561
-            $results[$name] = $constraints;
562
-        }
563
-
564
-        return $results;
565
-    }
566
-
567
-
568
-    /**
569
-     * Parse the nested relationships in a relation.
570
-     *
571
-     * @param  string $name
572
-     * @param  array  $results
573
-     * @return array
574
-     */
575
-    protected function parseNested($name, $results)
576
-    {
577
-        $progress = [];
578
-
579
-        // If the relation has already been set on the result array, we will not set it
580
-        // again, since that would override any constraints that were already placed
581
-        // on the relationships. We will only set the ones that are not specified.
582
-        foreach (explode('.', $name) as $segment) {
583
-            $progress[] = $segment;
584
-
585
-            if (!isset($results[$last = implode('.', $progress)])) {
586
-                $results[$last] = function () {};
587
-            }
588
-        }
589
-
590
-        return $results;
591
-    }
592
-
593
-    /**
594
-     * Get the relationships being eagerly loaded.
595
-     *
596
-     * @return array
597
-     */
598
-    public function getEagerLoads()
599
-    {
600
-        return $this->eagerLoad;
601
-    }
602
-
603
-    /**
604
-     * Set the relationships being eagerly loaded.
605
-     *
606
-     * @param  array $eagerLoad
607
-     * @return void
608
-     */
609
-    public function setEagerLoads(array $eagerLoad)
610
-    {
611
-        $this->eagerLoad = $eagerLoad;
612
-    }
613
-
614
-    /**
615
-     * Eager load the relationships for the entities.
616
-     *
617
-     * @param  array $entities
618
-     * @return array
619
-     */
620
-    public function eagerLoadRelations($entities)
621
-    {
622
-        foreach ($this->eagerLoad as $name => $constraints) {
623
-            // For nested eager loads we'll skip loading them here and they will be set as an
624
-            // eager load on the query to retrieve the relation so that they will be eager
625
-            // loaded on that query, because that is where they get hydrated as models.
626
-            if (strpos($name, '.') === false) {
627
-                $entities = $this->loadRelation($entities, $name, $constraints);
628
-            }
629
-        }
630
-
631
-        return $entities;
632
-    }
633
-
634
-    /**
635
-     * Eagerly load the relationship on a set of entities.
636
-     *
637
-     * @param  array    $entities
638
-     * @param  string   $name
639
-     * @param  \Closure $constraints
640
-     * @return array
641
-     */
642
-    protected function loadRelation(array $entities, $name, Closure $constraints)
643
-    {
644
-        // First we will "back up" the existing where conditions on the query so we can
645
-        // add our eager constraints. Then we will merge the wheres that were on the
646
-        // query back to it in order that any where conditions might be specified.
647
-        $relation = $this->getRelation($name);
648
-
649
-        $relation->addEagerConstraints($entities);
650
-
651
-        call_user_func($constraints, $relation);
652
-
653
-        $entities = $relation->initRelation($entities, $name);
654
-
655
-        // Once we have the results, we just match those back up to their parent models
656
-        // using the relationship instance. Then we just return the finished arrays
657
-        // of models which have been eagerly hydrated and are readied for return.
658
-
659
-        $results = $relation->getEager();
660
-
661
-        return $relation->match($entities, $results, $name);
662
-    }
663
-
664
-    /**
665
-     * Get the relation instance for the given relation name.
666
-     *
667
-     * @param  string $relation
668
-     * @return \Analogue\ORM\Relationships\Relationship
669
-     */
670
-    public function getRelation($relation)
671
-    {
672
-        // We want to run a relationship query without any constrains so that we will
673
-        // not have to remove these where clauses manually which gets really hacky
674
-        // and is error prone while we remove the developer's own where clauses.
675
-        $query = Relationship::noConstraints(function () use ($relation) {
676
-            return $this->entityMap->$relation($this->getEntityInstance());
677
-        });
678
-
679
-        $nested = $this->nestedRelations($relation);
680
-
681
-        // If there are nested relationships set on the query, we will put those onto
682
-        // the query instances so that they can be handled after this relationship
683
-        // is loaded. In this way they will all trickle down as they are loaded.
684
-        if (count($nested) > 0) {
685
-            $query->getQuery()->with($nested);
686
-        }
687
-
688
-        return $query;
689
-    }
690
-
691
-    /**
692
-     * Get the deeply nested relations for a given top-level relation.
693
-     *
694
-     * @param  string $relation
695
-     * @return array
696
-     */
697
-    protected function nestedRelations($relation)
698
-    {
699
-        $nested = [];
700
-
701
-        // We are basically looking for any relationships that are nested deeper than
702
-        // the given top-level relationship. We will just check for any relations
703
-        // that start with the given top relations and adds them to our arrays.
704
-        foreach ($this->eagerLoad as $name => $constraints) {
705
-            if ($this->isNested($name, $relation)) {
706
-                $nested[substr($name, strlen($relation . '.'))] = $constraints;
707
-            }
708
-        }
709
-
710
-        return $nested;
711
-    }
712
-
713
-    /**
714
-     * Determine if the relationship is nested.
715
-     *
716
-     * @param  string $name
717
-     * @param  string $relation
718
-     * @return bool
719
-     */
720
-    protected function isNested($name, $relation)
721
-    {
722
-        $dots = str_contains($name, '.');
723
-
724
-        return $dots && starts_with($name, $relation . '.');
725
-    }
726
-
727
-    /**
728
-     * Add the Entity primary key if not in requested columns
729
-     *
730
-     * @param  array $columns
731
-     * @return array
732
-     */
733
-    protected function enforceIdColumn($columns)
734
-    {
735
-        if (!in_array($this->entityMap->getKeyName(), $columns)) {
736
-            $columns[] = $this->entityMap->getKeyName();
737
-        }
738
-        return $columns;
739
-    }
740
-
741
-    /**
742
-     * Get the hydrated models without eager loading.
743
-     *
744
-     * @param  array  $columns
745
-     * @return \Analogue\ORM\EntityCollection
746
-     */
747
-    public function getEntities($columns = ['*'])
748
-    {
749
-        // As we need the primary key to feed the
750
-        // entity cache, we need it loaded on each
751
-        // request
752
-        $columns = $this->enforceIdColumn($columns);
753
-
754
-        // Run the query
755
-        $results = $this->query->get($columns);
756
-
757
-        $builder = new EntityBuilder($this->mapper, array_keys($this->getEagerLoads()));
758
-
759
-        return $builder->build($results);
760
-    }
761
-
762
-    /**
763
-     * Get a new instance for the entity
764
-     *
765
-     * @param  array  $attributes
766
-     * @return \Analogue\ORM\Entity
767
-     */
768
-    public function getEntityInstance(array $attributes = [])
769
-    {
770
-        return $this->mapper->newInstance($attributes);
771
-    }
772
-
773
-    /**
774
-     * Extend the builder with a given callback.
775
-     *
776
-     * @param  string   $name
777
-     * @param  \Closure $callback
778
-     * @return void
779
-     */
780
-    public function macro($name, Closure $callback)
781
-    {
782
-        $this->macros[$name] = $callback;
783
-    }
784
-
785
-    /**
786
-     * Get the given macro by name.
787
-     *
788
-     * @param  string $name
789
-     * @return \Closure
790
-     */
791
-    public function getMacro($name)
792
-    {
793
-        return array_get($this->macros, $name);
794
-    }
795
-
796
-    /**
797
-     * Get a new query builder for the model's table.
798
-     *
799
-     * @return \Analogue\ORM\System\Query
800
-     */
801
-    public function newQuery()
802
-    {
803
-        $builder = new Query($this->mapper, $this->adapter);
804
-
805
-        return $this->applyGlobalScopes($builder);
806
-    }
807
-
808
-    /**
809
-     * Get a new query builder without any scope applied.
810
-     *
811
-     * @return \Analogue\ORM\System\Query
812
-     */
813
-    public function newQueryWithoutScopes()
814
-    {
815
-        return new Query($this->mapper, $this->adapter);
816
-    }
817
-
818
-    /**
819
-     * Get the Mapper instance for this Query Builder
820
-     *
821
-     * @return \Analogue\ORM\System\Mapper
822
-     */
823
-    public function getMapper()
824
-    {
825
-        return $this->mapper;
826
-    }
827
-
828
-    /**
829
-     * Get the underlying query adapter
830
-     *
831
-     * (REFACTOR: this method should move out, we need to provide the client classes
832
-     * with the adapter instead.)
833
-     *
834
-     * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
835
-     */
836
-    public function getQuery()
837
-    {
838
-        return $this->query;
839
-    }
840
-
841
-    /**
842
-     * Dynamically handle calls into the query instance.
843
-     *
844
-     * @param  string $method
845
-     * @param  array  $parameters
846
-     * @throws Exception
847
-     * @return mixed
848
-     */
849
-    public function __call($method, $parameters)
850
-    {
851
-        if (isset($this->macros[$method])) {
852
-            array_unshift($parameters, $this);
853
-
854
-            return call_user_func_array($this->macros[$method], $parameters);
855
-        }
260
+	/**
261
+	 * Get an array with the values of a given column.
262
+	 *
263
+	 * @param  string $column
264
+	 * @param  string $key
265
+	 * @return array
266
+	 */
267
+	public function lists($column, $key = null)
268
+	{
269
+		return $this->query->lists($column, $key);
270
+	}
271
+
272
+	/**
273
+	 * Get a paginator for the "select" statement.
274
+	 *
275
+	 * @param  int   $perPage
276
+	 * @param  array $columns
277
+	 * @return LengthAwarePaginator
278
+	 */
279
+	public function paginate($perPage = null, $columns = ['*'])
280
+	{
281
+		$total = $this->query->getCountForPagination();
282
+
283
+		$this->query->forPage(
284
+			$page = Paginator::resolveCurrentPage(),
285
+			$perPage = $perPage ?: $this->entityMap->getPerPage()
286
+		);
287
+
288
+		return new LengthAwarePaginator($this->get($columns)->all(), $total, $perPage, $page, [
289
+			'path' => Paginator::resolveCurrentPath()
290
+		]);
291
+	}
292
+
293
+	/**
294
+	 * Get a paginator for a grouped statement.
295
+	 *
296
+	 * @param  \Illuminate\Pagination\Factory $paginator
297
+	 * @param  int                            $perPage
298
+	 * @param  array                          $columns
299
+	 * @return \Illuminate\Pagination\Paginator
300
+	 */
301
+	protected function groupedPaginate($paginator, $perPage, $columns)
302
+	{
303
+		$results = $this->get($columns)->all();
304
+
305
+		return $this->query->buildRawPaginator($paginator, $results, $perPage);
306
+	}
307
+
308
+	/**
309
+	 * Get a paginator for an ungrouped statement.
310
+	 *
311
+	 * @param  \Illuminate\Pagination\Factory $paginator
312
+	 * @param  int                            $perPage
313
+	 * @param  array                          $columns
314
+	 * @return \Illuminate\Pagination\Paginator
315
+	 */
316
+	protected function ungroupedPaginate($paginator, $perPage, $columns)
317
+	{
318
+		$total = $this->query->getPaginationCount();
319
+
320
+		// Once we have the paginator we need to set the limit and offset values for
321
+		// the query so we can get the properly paginated items. Once we have an
322
+		// array of items we can create the paginator instances for the items.
323
+		$page = $paginator->getCurrentPage($total);
324
+
325
+		$this->query->forPage($page, $perPage);
326
+
327
+		return $paginator->make($this->get($columns)->all(), $total, $perPage);
328
+	}
329
+
330
+	/**
331
+	 * Paginate the given query into a simple paginator.
332
+	 *
333
+	 * @param  int   $perPage
334
+	 * @param  array $columns
335
+	 * @return \Illuminate\Contracts\Pagination\Paginator
336
+	 */
337
+	public function simplePaginate($perPage = null, $columns = ['*'])
338
+	{
339
+		$page = Paginator::resolveCurrentPage();
340
+
341
+		$perPage = $perPage ?: $this->entityMap->getPerPage();
342
+
343
+		$this->skip(($page - 1) * $perPage)->take($perPage + 1);
344
+
345
+		return new Paginator($this->get($columns)->all(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]);
346
+	}
347
+
348
+	/**
349
+	 * Add a basic where clause to the query.
350
+	 *
351
+	 * @param  string $column
352
+	 * @param  string $operator
353
+	 * @param  mixed  $value
354
+	 * @param  string $boolean
355
+	 * @return $this
356
+	 */
357
+	public function where($column, $operator = null, $value = null, $boolean = 'and')
358
+	{
359
+		if ($column instanceof Closure) {
360
+			$query = $this->newQueryWithoutScopes();
361
+
362
+			call_user_func($column, $query);
363
+
364
+			$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
365
+		} else {
366
+			call_user_func_array([$this->query, 'where'], func_get_args());
367
+		}
368
+
369
+		return $this;
370
+	}
371
+
372
+	/**
373
+	 * Add an "or where" clause to the query.
374
+	 *
375
+	 * @param  string $column
376
+	 * @param  string $operator
377
+	 * @param  mixed  $value
378
+	 * @return \Analogue\ORM\System\Query
379
+	 */
380
+	public function orWhere($column, $operator = null, $value = null)
381
+	{
382
+		return $this->where($column, $operator, $value, 'or');
383
+	}
384
+
385
+	/**
386
+	 * Add a relationship count condition to the query.
387
+	 *
388
+	 * @param  string   $relation
389
+	 * @param  string   $operator
390
+	 * @param  int      $count
391
+	 * @param  string   $boolean
392
+	 * @param  \Closure $callback
393
+	 * @return \Analogue\ORM\System\Query
394
+	 */
395
+	public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', $callback = null)
396
+	{
397
+		$entity = $this->mapper->newInstance();
398
+
399
+		$relation = $this->getHasRelationQuery($relation, $entity);
400
+
401
+		$query = $relation->getRelationCountQuery($relation->getRelatedMapper()->getQuery(), $this);
402
+
403
+		if ($callback) {
404
+			call_user_func($callback, $query);
405
+		}
406
+
407
+		return $this->addHasWhere($query, $relation, $operator, $count, $boolean);
408
+	}
409
+
410
+	/**
411
+	 * Add a relationship count condition to the query with where clauses.
412
+	 *
413
+	 * @param  string   $relation
414
+	 * @param  \Closure $callback
415
+	 * @param  string   $operator
416
+	 * @param  int      $count
417
+	 * @return \Analogue\ORM\System\Query
418
+	 */
419
+	public function whereHas($relation, Closure $callback, $operator = '>=', $count = 1)
420
+	{
421
+		return $this->has($relation, $operator, $count, 'and', $callback);
422
+	}
423
+
424
+	/**
425
+	 * Add a relationship count condition to the query with an "or".
426
+	 *
427
+	 * @param  string $relation
428
+	 * @param  string $operator
429
+	 * @param  int    $count
430
+	 * @return \Analogue\ORM\System\Query
431
+	 */
432
+	public function orHas($relation, $operator = '>=', $count = 1)
433
+	{
434
+		return $this->has($relation, $operator, $count, 'or');
435
+	}
436
+
437
+	/**
438
+	 * Add a relationship count condition to the query with where clauses and an "or".
439
+	 *
440
+	 * @param  string   $relation
441
+	 * @param  \Closure $callback
442
+	 * @param  string   $operator
443
+	 * @param  int      $count
444
+	 * @return \Analogue\ORM\System\Query
445
+	 */
446
+	public function orWhereHas($relation, Closure $callback, $operator = '>=', $count = 1)
447
+	{
448
+		return $this->has($relation, $operator, $count, 'or', $callback);
449
+	}
450
+
451
+	/**
452
+	 * Add the "has" condition where clause to the query.
453
+	 *
454
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
455
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
456
+	 * @param  string                                   $operator
457
+	 * @param  int                                      $count
458
+	 * @param  string                                   $boolean
459
+	 * @return \Analogue\ORM\System\Query
460
+	 */
461
+	protected function addHasWhere(Query $hasQuery, Relationship $relation, $operator, $count, $boolean)
462
+	{
463
+		$this->mergeWheresToHas($hasQuery, $relation);
464
+
465
+		if (is_numeric($count)) {
466
+			$count = new Expression($count);
467
+		}
468
+
469
+		return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
470
+	}
471
+
472
+	/**
473
+	 * Merge the "wheres" from a relation query to a has query.
474
+	 *
475
+	 * @param  \Analogue\ORM\System\Query               $hasQuery
476
+	 * @param  \Analogue\ORM\Relationships\Relationship $relation
477
+	 * @return void
478
+	 */
479
+	protected function mergeWheresToHas(Query $hasQuery, Relationship $relation)
480
+	{
481
+		// Here we have the "has" query and the original relation. We need to copy over any
482
+		// where clauses the developer may have put in the relationship function over to
483
+		// the has query, and then copy the bindings from the "has" query to the main.
484
+		$relationQuery = $relation->getBaseQuery();
485
+
486
+		$hasQuery->mergeWheres(
487
+			$relationQuery->wheres, $relationQuery->getBindings()
488
+		);
489
+
490
+		$this->query->mergeBindings($hasQuery->getQuery());
491
+	}
492
+
493
+	/**
494
+	 * Get the "has relation" base query instance.
495
+	 *
496
+	 * @param  string $relation
497
+	 * @param         $entity
498
+	 * @return \Analogue\ORM\System\Query
499
+	 */
500
+	protected function getHasRelationQuery($relation, $entity)
501
+	{
502
+		return Relationship::noConstraints(function () use ($relation, $entity) {
503
+			return $this->entityMap->$relation($entity);
504
+		});
505
+	}
506
+
507
+	/**
508
+	 * Get the table for the current query object
509
+	 *
510
+	 * @return string
511
+	 */
512
+	public function getTable()
513
+	{
514
+		return $this->entityMap->getTable();
515
+	}
516
+
517
+	/**
518
+	 * Set the relationships that should be eager loaded.
519
+	 *
520
+	 * @param  mixed $relations
521
+	 * @return $this
522
+	 */
523
+	public function with($relations)
524
+	{
525
+		if (is_string($relations)) {
526
+			$relations = func_get_args();
527
+		}
528
+
529
+		$eagers = $this->parseRelations($relations);
530
+
531
+		$this->eagerLoad = array_merge($this->eagerLoad, $eagers);
532
+
533
+		return $this;
534
+	}
535
+
536
+	/**
537
+	 * Parse a list of relations into individuals.
538
+	 *
539
+	 * @param  array $relations
540
+	 * @return array
541
+	 */
542
+	protected function parseRelations(array $relations)
543
+	{
544
+		$results = [];
545
+
546
+		foreach ($relations as $name => $constraints) {
547
+			// If the "relation" value is actually a numeric key, we can assume that no
548
+			// constraints have been specified for the eager load and we'll just put
549
+			// an empty Closure with the loader so that we can treat all the same.
550
+			if (is_numeric($name)) {
551
+				$f = function () {};
552
+
553
+				list($name, $constraints) = [$constraints, $f];
554
+			}
555
+
556
+			// We need to separate out any nested includes. Which allows the developers
557
+			// to load deep relationships using "dots" without stating each level of
558
+			// the relationship with its own key in the array of eager load names.
559
+			$results = $this->parseNested($name, $results);
560
+
561
+			$results[$name] = $constraints;
562
+		}
563
+
564
+		return $results;
565
+	}
566
+
567
+
568
+	/**
569
+	 * Parse the nested relationships in a relation.
570
+	 *
571
+	 * @param  string $name
572
+	 * @param  array  $results
573
+	 * @return array
574
+	 */
575
+	protected function parseNested($name, $results)
576
+	{
577
+		$progress = [];
578
+
579
+		// If the relation has already been set on the result array, we will not set it
580
+		// again, since that would override any constraints that were already placed
581
+		// on the relationships. We will only set the ones that are not specified.
582
+		foreach (explode('.', $name) as $segment) {
583
+			$progress[] = $segment;
584
+
585
+			if (!isset($results[$last = implode('.', $progress)])) {
586
+				$results[$last] = function () {};
587
+			}
588
+		}
589
+
590
+		return $results;
591
+	}
592
+
593
+	/**
594
+	 * Get the relationships being eagerly loaded.
595
+	 *
596
+	 * @return array
597
+	 */
598
+	public function getEagerLoads()
599
+	{
600
+		return $this->eagerLoad;
601
+	}
602
+
603
+	/**
604
+	 * Set the relationships being eagerly loaded.
605
+	 *
606
+	 * @param  array $eagerLoad
607
+	 * @return void
608
+	 */
609
+	public function setEagerLoads(array $eagerLoad)
610
+	{
611
+		$this->eagerLoad = $eagerLoad;
612
+	}
613
+
614
+	/**
615
+	 * Eager load the relationships for the entities.
616
+	 *
617
+	 * @param  array $entities
618
+	 * @return array
619
+	 */
620
+	public function eagerLoadRelations($entities)
621
+	{
622
+		foreach ($this->eagerLoad as $name => $constraints) {
623
+			// For nested eager loads we'll skip loading them here and they will be set as an
624
+			// eager load on the query to retrieve the relation so that they will be eager
625
+			// loaded on that query, because that is where they get hydrated as models.
626
+			if (strpos($name, '.') === false) {
627
+				$entities = $this->loadRelation($entities, $name, $constraints);
628
+			}
629
+		}
630
+
631
+		return $entities;
632
+	}
633
+
634
+	/**
635
+	 * Eagerly load the relationship on a set of entities.
636
+	 *
637
+	 * @param  array    $entities
638
+	 * @param  string   $name
639
+	 * @param  \Closure $constraints
640
+	 * @return array
641
+	 */
642
+	protected function loadRelation(array $entities, $name, Closure $constraints)
643
+	{
644
+		// First we will "back up" the existing where conditions on the query so we can
645
+		// add our eager constraints. Then we will merge the wheres that were on the
646
+		// query back to it in order that any where conditions might be specified.
647
+		$relation = $this->getRelation($name);
648
+
649
+		$relation->addEagerConstraints($entities);
650
+
651
+		call_user_func($constraints, $relation);
652
+
653
+		$entities = $relation->initRelation($entities, $name);
654
+
655
+		// Once we have the results, we just match those back up to their parent models
656
+		// using the relationship instance. Then we just return the finished arrays
657
+		// of models which have been eagerly hydrated and are readied for return.
658
+
659
+		$results = $relation->getEager();
660
+
661
+		return $relation->match($entities, $results, $name);
662
+	}
663
+
664
+	/**
665
+	 * Get the relation instance for the given relation name.
666
+	 *
667
+	 * @param  string $relation
668
+	 * @return \Analogue\ORM\Relationships\Relationship
669
+	 */
670
+	public function getRelation($relation)
671
+	{
672
+		// We want to run a relationship query without any constrains so that we will
673
+		// not have to remove these where clauses manually which gets really hacky
674
+		// and is error prone while we remove the developer's own where clauses.
675
+		$query = Relationship::noConstraints(function () use ($relation) {
676
+			return $this->entityMap->$relation($this->getEntityInstance());
677
+		});
678
+
679
+		$nested = $this->nestedRelations($relation);
680
+
681
+		// If there are nested relationships set on the query, we will put those onto
682
+		// the query instances so that they can be handled after this relationship
683
+		// is loaded. In this way they will all trickle down as they are loaded.
684
+		if (count($nested) > 0) {
685
+			$query->getQuery()->with($nested);
686
+		}
687
+
688
+		return $query;
689
+	}
690
+
691
+	/**
692
+	 * Get the deeply nested relations for a given top-level relation.
693
+	 *
694
+	 * @param  string $relation
695
+	 * @return array
696
+	 */
697
+	protected function nestedRelations($relation)
698
+	{
699
+		$nested = [];
700
+
701
+		// We are basically looking for any relationships that are nested deeper than
702
+		// the given top-level relationship. We will just check for any relations
703
+		// that start with the given top relations and adds them to our arrays.
704
+		foreach ($this->eagerLoad as $name => $constraints) {
705
+			if ($this->isNested($name, $relation)) {
706
+				$nested[substr($name, strlen($relation . '.'))] = $constraints;
707
+			}
708
+		}
709
+
710
+		return $nested;
711
+	}
712
+
713
+	/**
714
+	 * Determine if the relationship is nested.
715
+	 *
716
+	 * @param  string $name
717
+	 * @param  string $relation
718
+	 * @return bool
719
+	 */
720
+	protected function isNested($name, $relation)
721
+	{
722
+		$dots = str_contains($name, '.');
723
+
724
+		return $dots && starts_with($name, $relation . '.');
725
+	}
726
+
727
+	/**
728
+	 * Add the Entity primary key if not in requested columns
729
+	 *
730
+	 * @param  array $columns
731
+	 * @return array
732
+	 */
733
+	protected function enforceIdColumn($columns)
734
+	{
735
+		if (!in_array($this->entityMap->getKeyName(), $columns)) {
736
+			$columns[] = $this->entityMap->getKeyName();
737
+		}
738
+		return $columns;
739
+	}
740
+
741
+	/**
742
+	 * Get the hydrated models without eager loading.
743
+	 *
744
+	 * @param  array  $columns
745
+	 * @return \Analogue\ORM\EntityCollection
746
+	 */
747
+	public function getEntities($columns = ['*'])
748
+	{
749
+		// As we need the primary key to feed the
750
+		// entity cache, we need it loaded on each
751
+		// request
752
+		$columns = $this->enforceIdColumn($columns);
753
+
754
+		// Run the query
755
+		$results = $this->query->get($columns);
756
+
757
+		$builder = new EntityBuilder($this->mapper, array_keys($this->getEagerLoads()));
758
+
759
+		return $builder->build($results);
760
+	}
761
+
762
+	/**
763
+	 * Get a new instance for the entity
764
+	 *
765
+	 * @param  array  $attributes
766
+	 * @return \Analogue\ORM\Entity
767
+	 */
768
+	public function getEntityInstance(array $attributes = [])
769
+	{
770
+		return $this->mapper->newInstance($attributes);
771
+	}
772
+
773
+	/**
774
+	 * Extend the builder with a given callback.
775
+	 *
776
+	 * @param  string   $name
777
+	 * @param  \Closure $callback
778
+	 * @return void
779
+	 */
780
+	public function macro($name, Closure $callback)
781
+	{
782
+		$this->macros[$name] = $callback;
783
+	}
784
+
785
+	/**
786
+	 * Get the given macro by name.
787
+	 *
788
+	 * @param  string $name
789
+	 * @return \Closure
790
+	 */
791
+	public function getMacro($name)
792
+	{
793
+		return array_get($this->macros, $name);
794
+	}
795
+
796
+	/**
797
+	 * Get a new query builder for the model's table.
798
+	 *
799
+	 * @return \Analogue\ORM\System\Query
800
+	 */
801
+	public function newQuery()
802
+	{
803
+		$builder = new Query($this->mapper, $this->adapter);
804
+
805
+		return $this->applyGlobalScopes($builder);
806
+	}
807
+
808
+	/**
809
+	 * Get a new query builder without any scope applied.
810
+	 *
811
+	 * @return \Analogue\ORM\System\Query
812
+	 */
813
+	public function newQueryWithoutScopes()
814
+	{
815
+		return new Query($this->mapper, $this->adapter);
816
+	}
817
+
818
+	/**
819
+	 * Get the Mapper instance for this Query Builder
820
+	 *
821
+	 * @return \Analogue\ORM\System\Mapper
822
+	 */
823
+	public function getMapper()
824
+	{
825
+		return $this->mapper;
826
+	}
827
+
828
+	/**
829
+	 * Get the underlying query adapter
830
+	 *
831
+	 * (REFACTOR: this method should move out, we need to provide the client classes
832
+	 * with the adapter instead.)
833
+	 *
834
+	 * @return \Analogue\ORM\Drivers\QueryAdapter|\Analogue\ORM\Drivers\IlluminateQueryAdapter
835
+	 */
836
+	public function getQuery()
837
+	{
838
+		return $this->query;
839
+	}
840
+
841
+	/**
842
+	 * Dynamically handle calls into the query instance.
843
+	 *
844
+	 * @param  string $method
845
+	 * @param  array  $parameters
846
+	 * @throws Exception
847
+	 * @return mixed
848
+	 */
849
+	public function __call($method, $parameters)
850
+	{
851
+		if (isset($this->macros[$method])) {
852
+			array_unshift($parameters, $this);
853
+
854
+			return call_user_func_array($this->macros[$method], $parameters);
855
+		}
856 856
         
857
-        if (in_array($method, $this->blacklist)) {
858
-            throw new Exception("Method $method doesn't exist");
859
-        }
857
+		if (in_array($method, $this->blacklist)) {
858
+			throw new Exception("Method $method doesn't exist");
859
+		}
860 860
 
861
-        $result = call_user_func_array([$this->query, $method], $parameters);
861
+		$result = call_user_func_array([$this->query, $method], $parameters);
862 862
 
863
-        return in_array($method, $this->passthru) ? $result : $this;
864
-    }
863
+		return in_array($method, $this->passthru) ? $result : $this;
864
+	}
865 865
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -466,7 +466,7 @@  discard block
 block discarded – undo
466 466
             $count = new Expression($count);
467 467
         }
468 468
 
469
-        return $this->where(new Expression('(' . $hasQuery->toSql() . ')'), $operator, $count, $boolean);
469
+        return $this->where(new Expression('('.$hasQuery->toSql().')'), $operator, $count, $boolean);
470 470
     }
471 471
 
472 472
     /**
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
      */
500 500
     protected function getHasRelationQuery($relation, $entity)
501 501
     {
502
-        return Relationship::noConstraints(function () use ($relation, $entity) {
502
+        return Relationship::noConstraints(function() use ($relation, $entity) {
503 503
             return $this->entityMap->$relation($entity);
504 504
         });
505 505
     }
@@ -548,7 +548,7 @@  discard block
 block discarded – undo
548 548
             // constraints have been specified for the eager load and we'll just put
549 549
             // an empty Closure with the loader so that we can treat all the same.
550 550
             if (is_numeric($name)) {
551
-                $f = function () {};
551
+                $f = function() {};
552 552
 
553 553
                 list($name, $constraints) = [$constraints, $f];
554 554
             }
@@ -583,7 +583,7 @@  discard block
 block discarded – undo
583 583
             $progress[] = $segment;
584 584
 
585 585
             if (!isset($results[$last = implode('.', $progress)])) {
586
-                $results[$last] = function () {};
586
+                $results[$last] = function() {};
587 587
             }
588 588
         }
589 589
 
@@ -672,7 +672,7 @@  discard block
 block discarded – undo
672 672
         // We want to run a relationship query without any constrains so that we will
673 673
         // not have to remove these where clauses manually which gets really hacky
674 674
         // and is error prone while we remove the developer's own where clauses.
675
-        $query = Relationship::noConstraints(function () use ($relation) {
675
+        $query = Relationship::noConstraints(function() use ($relation) {
676 676
             return $this->entityMap->$relation($this->getEntityInstance());
677 677
         });
678 678
 
@@ -703,7 +703,7 @@  discard block
 block discarded – undo
703 703
         // that start with the given top relations and adds them to our arrays.
704 704
         foreach ($this->eagerLoad as $name => $constraints) {
705 705
             if ($this->isNested($name, $relation)) {
706
-                $nested[substr($name, strlen($relation . '.'))] = $constraints;
706
+                $nested[substr($name, strlen($relation.'.'))] = $constraints;
707 707
             }
708 708
         }
709 709
 
@@ -721,7 +721,7 @@  discard block
 block discarded – undo
721 721
     {
722 722
         $dots = str_contains($name, '.');
723 723
 
724
-        return $dots && starts_with($name, $relation . '.');
724
+        return $dots && starts_with($name, $relation.'.');
725 725
     }
726 726
 
727 727
     /**
Please login to merge, or discard this patch.
src/Exceptions/EntityMapNotFoundException.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -6,5 +6,5 @@
 block discarded – undo
6 6
 
7 7
 class EntityMapNotFoundException extends RuntimeException
8 8
 {
9
-    //
9
+	//
10 10
 }
Please login to merge, or discard this patch.
src/System/Manager.php 2 patches
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -165,7 +165,7 @@  discard block
 block discarded – undo
165 165
         // We first check if the entity is traversable and we'll resolve
166 166
         // the entity based on the first item of the object.   
167 167
         if ($this->isTraversable($entity)) {
168
-            if (! count($entity)) {
168
+            if (!count($entity)) {
169 169
                 throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
170 170
             }
171 171
 
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
         }
297 297
 
298 298
         if (!$entityMap instanceof EntityMap) {
299
-            throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
299
+            throw new MappingException(get_class($entityMap).' must be an instance of EntityMap.');
300 300
         }
301 301
 
302 302
         $entityMap->setClass($entity);
@@ -314,8 +314,8 @@  discard block
 block discarded – undo
314 314
      */
315 315
     protected function getEntityMapInstanceFor($entity)
316 316
     {
317
-        if (class_exists($entity . 'Map')) {
318
-            $map = $entity . 'Map';
317
+        if (class_exists($entity.'Map')) {
318
+            $map = $entity.'Map';
319 319
             $map = new $map;
320 320
         } else {
321 321
             if ($this->strictMode) {
@@ -434,7 +434,7 @@  discard block
 block discarded – undo
434 434
         }
435 435
 
436 436
         if (is_null($valueMap)) {
437
-            $valueMap = $valueObject . 'Map';
437
+            $valueMap = $valueObject.'Map';
438 438
         }
439 439
 
440 440
         if (!class_exists($valueMap)) {
Please login to merge, or discard this patch.
Indentation   +547 added lines, -547 removed lines patch added patch discarded remove patch
@@ -21,554 +21,554 @@
 block discarded – undo
21 21
  */
22 22
 class Manager
23 23
 {
24
-    /**
25
-     * Manager instance
26
-     *
27
-     * @var Manager
28
-     */
29
-    protected static $instance;
30
-
31
-    /**
32
-     * Driver Manager
33
-     *
34
-     * @var \Analogue\ORM\Drivers\Manager
35
-     */
36
-    protected $drivers;
37
-
38
-    /**
39
-     * Registered entity classes and corresponding map objects.
40
-     *
41
-     * @var array
42
-     */
43
-    protected $entityClasses = [];
44
-
45
-    /**
46
-     * Key value store of ValueObject Classes and corresponding map classes
47
-     *
48
-     * @var array|ValueMap[]
49
-     */
50
-    protected $valueClasses = [];
51
-
52
-    /**
53
-     * Morph map
54
-     */
55
-    protected $morphMap = [];
56
-
57
-    /**
58
-     * Loaded Mappers
59
-     *
60
-     * @var array
61
-     */
62
-    protected $mappers = [];
63
-
64
-    /**
65
-     * Loaded Repositories
66
-     *
67
-     * @var array
68
-     */
69
-    protected $repositories = [];
70
-
71
-    /**
72
-     * Event dispatcher instance
73
-     *
74
-     * @var \Illuminate\Contracts\Events\Dispatcher
75
-     */
76
-    protected $eventDispatcher;
77
-
78
-    /**
79
-     * Available Analogue Events
80
-     *
81
-     * @var array
82
-     */
83
-    protected $events = [
84
-        'initializing',
85
-        'initialized',
86
-        'store',
87
-        'stored',
88
-        'creating',
89
-        'created',
90
-        'updating',
91
-        'updated',
92
-        'deleting',
93
-        'deleted',
94
-    ];
95
-
96
-    /**
97
-     * If strictMode is set to true, Manager will throw
98
-     * an exception if no entityMap class are registered
99
-     * for a given entity class.
100
-     * 
101
-     * @var boolean
102
-     */
103
-    protected $strictMode = true;
104
-
105
-    /**
106
-     * @param \Analogue\ORM\Drivers\Manager $driverManager
107
-     * @param Dispatcher                    $event
108
-     */
109
-    public function __construct(DriverManager $driverManager, Dispatcher $event)
110
-    {
111
-        $this->drivers = $driverManager;
112
-
113
-        $this->eventDispatcher = $event;
114
-
115
-        static::$instance = $this;
116
-    }
117
-
118
-    /**
119
-     * Create a mapper for a given entity (static alias)
120
-     *
121
-     * @param  \Analogue\ORM\Mappable|string $entity
122
-     * @param  null|EntityMap                $entityMap
123
-     * @throws MappingException
124
-     * @return Mapper
125
-     * @throws \InvalidArgumentException
126
-     */
127
-    public static function getMapper($entity, $entityMap = null)
128
-    {
129
-        return static::$instance->mapper($entity, $entityMap);
130
-    }
131
-
132
-    /**
133
-     * Create a mapper for a given entity
134
-     *
135
-     * @param  \Analogue\ORM\Mappable|string|array|\Traversable $entity
136
-     * @param  mixed                                            $entityMap
137
-     * @throws MappingException
138
-     * @throws \InvalidArgumentException
139
-     * @return Mapper
140
-     */
141
-    public function mapper($entity, $entityMap = null)
142
-    {
143
-        if ($entity instanceof Wrapper) {
144
-            throw new MappingException('Tried to instantiate mapper on wrapped Entity');
145
-        }
146
-
147
-        $entity = $this->resolveEntityClass($entity);
148
-
149
-        $entity = $this->getInverseMorphMap($entity);
150
-
151
-        // Return existing mapper instance if exists.
152
-        if (array_key_exists($entity, $this->mappers)) {
153
-            return $this->mappers[$entity];
154
-        } else {
155
-            return $this->buildMapper($entity, $entityMap);
156
-        }
157
-    }
158
-
159
-    /**
160
-     * This method resolve entity class from mappable instances or iterators
161
-     *
162
-     * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
163
-     * @return string
164
-     *
165
-     * @throws \InvalidArgumentException
166
-     */
167
-    protected function resolveEntityClass($entity)
168
-    {
169
-        // We first check if the entity is traversable and we'll resolve
170
-        // the entity based on the first item of the object.   
171
-        if ($this->isTraversable($entity)) {
172
-            if (! count($entity)) {
173
-                throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
174
-            }
175
-
176
-            $firstEntityItem = ($entity instanceof \Iterator)
177
-                ? $entity->current()
178
-                : current($entity);
179
-
180
-            return $this->resolveEntityClass($firstEntityItem);
181
-        }
24
+	/**
25
+	 * Manager instance
26
+	 *
27
+	 * @var Manager
28
+	 */
29
+	protected static $instance;
30
+
31
+	/**
32
+	 * Driver Manager
33
+	 *
34
+	 * @var \Analogue\ORM\Drivers\Manager
35
+	 */
36
+	protected $drivers;
37
+
38
+	/**
39
+	 * Registered entity classes and corresponding map objects.
40
+	 *
41
+	 * @var array
42
+	 */
43
+	protected $entityClasses = [];
44
+
45
+	/**
46
+	 * Key value store of ValueObject Classes and corresponding map classes
47
+	 *
48
+	 * @var array|ValueMap[]
49
+	 */
50
+	protected $valueClasses = [];
51
+
52
+	/**
53
+	 * Morph map
54
+	 */
55
+	protected $morphMap = [];
56
+
57
+	/**
58
+	 * Loaded Mappers
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $mappers = [];
63
+
64
+	/**
65
+	 * Loaded Repositories
66
+	 *
67
+	 * @var array
68
+	 */
69
+	protected $repositories = [];
70
+
71
+	/**
72
+	 * Event dispatcher instance
73
+	 *
74
+	 * @var \Illuminate\Contracts\Events\Dispatcher
75
+	 */
76
+	protected $eventDispatcher;
77
+
78
+	/**
79
+	 * Available Analogue Events
80
+	 *
81
+	 * @var array
82
+	 */
83
+	protected $events = [
84
+		'initializing',
85
+		'initialized',
86
+		'store',
87
+		'stored',
88
+		'creating',
89
+		'created',
90
+		'updating',
91
+		'updated',
92
+		'deleting',
93
+		'deleted',
94
+	];
95
+
96
+	/**
97
+	 * If strictMode is set to true, Manager will throw
98
+	 * an exception if no entityMap class are registered
99
+	 * for a given entity class.
100
+	 * 
101
+	 * @var boolean
102
+	 */
103
+	protected $strictMode = true;
104
+
105
+	/**
106
+	 * @param \Analogue\ORM\Drivers\Manager $driverManager
107
+	 * @param Dispatcher                    $event
108
+	 */
109
+	public function __construct(DriverManager $driverManager, Dispatcher $event)
110
+	{
111
+		$this->drivers = $driverManager;
112
+
113
+		$this->eventDispatcher = $event;
114
+
115
+		static::$instance = $this;
116
+	}
117
+
118
+	/**
119
+	 * Create a mapper for a given entity (static alias)
120
+	 *
121
+	 * @param  \Analogue\ORM\Mappable|string $entity
122
+	 * @param  null|EntityMap                $entityMap
123
+	 * @throws MappingException
124
+	 * @return Mapper
125
+	 * @throws \InvalidArgumentException
126
+	 */
127
+	public static function getMapper($entity, $entityMap = null)
128
+	{
129
+		return static::$instance->mapper($entity, $entityMap);
130
+	}
131
+
132
+	/**
133
+	 * Create a mapper for a given entity
134
+	 *
135
+	 * @param  \Analogue\ORM\Mappable|string|array|\Traversable $entity
136
+	 * @param  mixed                                            $entityMap
137
+	 * @throws MappingException
138
+	 * @throws \InvalidArgumentException
139
+	 * @return Mapper
140
+	 */
141
+	public function mapper($entity, $entityMap = null)
142
+	{
143
+		if ($entity instanceof Wrapper) {
144
+			throw new MappingException('Tried to instantiate mapper on wrapped Entity');
145
+		}
146
+
147
+		$entity = $this->resolveEntityClass($entity);
148
+
149
+		$entity = $this->getInverseMorphMap($entity);
150
+
151
+		// Return existing mapper instance if exists.
152
+		if (array_key_exists($entity, $this->mappers)) {
153
+			return $this->mappers[$entity];
154
+		} else {
155
+			return $this->buildMapper($entity, $entityMap);
156
+		}
157
+	}
158
+
159
+	/**
160
+	 * This method resolve entity class from mappable instances or iterators
161
+	 *
162
+	 * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
163
+	 * @return string
164
+	 *
165
+	 * @throws \InvalidArgumentException
166
+	 */
167
+	protected function resolveEntityClass($entity)
168
+	{
169
+		// We first check if the entity is traversable and we'll resolve
170
+		// the entity based on the first item of the object.   
171
+		if ($this->isTraversable($entity)) {
172
+			if (! count($entity)) {
173
+				throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
174
+			}
175
+
176
+			$firstEntityItem = ($entity instanceof \Iterator)
177
+				? $entity->current()
178
+				: current($entity);
179
+
180
+			return $this->resolveEntityClass($firstEntityItem);
181
+		}
182 182
             
183
-        if (is_object($entity)) {
184
-            return get_class($entity);
185
-        }
183
+		if (is_object($entity)) {
184
+			return get_class($entity);
185
+		}
186 186
 
187
-        if (is_string($entity)) {
188
-            return $entity;
189
-        }
187
+		if (is_string($entity)) {
188
+			return $entity;
189
+		}
190 190
      
191
-        throw new \InvalidArgumentException('Invalid entity type');
192
-    }
193
-
194
-    /**
195
-     * @param string $key
196
-     * @return string
197
-     */
198
-    public function getInverseMorphMap($key)
199
-    {
200
-        return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
201
-    }
202
-
203
-    /**
204
-     * Build a new Mapper instance for a given Entity
205
-     *
206
-     * @param  string $entity
207
-     * @param         $entityMap
208
-     * @throws MappingException
209
-     * @return Mapper
210
-     */
211
-    protected function buildMapper($entity, $entityMap)
212
-    {
213
-        // If an EntityMap hasn't been manually registered by the user
214
-        // register it at runtime.
215
-        if (!$this->isRegisteredEntity($entity)) {
216
-            $this->register($entity, $entityMap);
217
-        }
218
-
219
-        $entityMap = $this->entityClasses[$entity];
220
-
221
-        $factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
222
-
223
-        $mapper = $factory->make($entity, $entityMap);
224
-
225
-        $this->mappers[$entity] = $mapper;
226
-
227
-        // At this point we can safely call the boot() method on the entityMap as
228
-        // the mapper is now instantiated & registered within the manager.
229
-
230
-        $mapper->getEntityMap()->boot();
231
-
232
-        return $mapper;
233
-    }
234
-
235
-    /**
236
-     * Check if the entity is already registered
237
-     *
238
-     * @param  string|Entity $entity
239
-     * @return boolean
240
-     */
241
-    public function isRegisteredEntity($entity)
242
-    {
243
-        if (!is_string($entity)) {
244
-            $entity = get_class($entity);
245
-        }
246
-
247
-        return array_key_exists($entity, $this->entityClasses);
248
-    }
249
-
250
-    /**
251
-     * Return true if an object is an array or iterator
252
-     *
253
-     * @param  mixed $argument
254
-     * @return boolean
255
-     */
256
-    public function isTraversable($argument)
257
-    {
258
-        return $argument instanceof \Traversable || is_array($argument);
259
-    }
260
-
261
-    /**
262
-     * Set strict mode for entityMap instantiation
263
-     * 
264
-     * @param boolean $mode
265
-     */
266
-    public function setStrictMode($mode)
267
-    {
268
-        $this->strictMode = $mode;
269
-    }
270
-
271
-    /**
272
-     * Register an entity
273
-     *
274
-     * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
275
-     * @param  string|EntityMap              $entityMap map's class name
276
-     * @throws MappingException
277
-     * @return void
278
-     */
279
-    public function register($entity, $entityMap = null)
280
-    {
281
-        // If an object is provider, get the class name from it
282
-        if (!is_string($entity)) {
283
-            $entity = get_class($entity);
284
-        }
285
-
286
-        if ($this->isRegisteredEntity($entity)) {
287
-            throw new MappingException("Entity $entity is already registered.");
288
-        }
289
-
290
-        if (!class_exists($entity)) {
291
-            throw new MappingException("Class $entity does not exists");
292
-        }
293
-
294
-        if ($entityMap === null) {
295
-            $entityMap = $this->getEntityMapInstanceFor($entity);
296
-        }
297
-
298
-        if (is_string($entityMap)) {
299
-            $entityMap = new $entityMap;
300
-        }
301
-
302
-        if (!$entityMap instanceof EntityMap) {
303
-            throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
304
-        }
305
-
306
-        $entityMap->setClass($entity);
307
-
308
-        $entityMap->setManager($this);
309
-
310
-        $this->entityClasses[$entity] = $entityMap;
311
-    }
312
-
313
-    /**
314
-     * Get the entity map instance for a custom entity
315
-     *
316
-     * @param  string $entity
317
-     * @return \Analogue\ORM\Mappable
318
-     * @throws EntityMapNotFoundException
319
-     */
320
-    protected function getEntityMapInstanceFor($entity)
321
-    {
322
-        if (class_exists($entity . 'Map')) {
323
-            $map = $entity . 'Map';
324
-            $map = new $map;
325
-        } else {
326
-            if ($this->strictMode) {
327
-                throw new EntityMapNotFoundException("No EntityMap registered for $entity");
328
-            }
329
-            $map = $this->getNewEntityMap();
330
-        }
331
-
332
-        return $map;
333
-    }
334
-
335
-    /**
336
-     * Dynamically create an entity map for a custom entity class
337
-     *
338
-     * @return EntityMap
339
-     */
340
-    protected function getNewEntityMap()
341
-    {
342
-        return new EntityMap;
343
-    }
344
-
345
-    /**
346
-     * Return the Singleton instance of the manager
347
-     *
348
-     * @return Manager
349
-     */
350
-    public static function getInstance()
351
-    {
352
-        return static::$instance;
353
-    }
354
-
355
-    /**
356
-     * Return the Driver Manager's instance
357
-     *
358
-     * @return \Analogue\ORM\Drivers\Manager
359
-     */
360
-    public function getDriverManager()
361
-    {
362
-        return $this->drivers;
363
-    }
364
-
365
-    /**
366
-     * Get the Repository instance for the given Entity
367
-     *
368
-     * @param  \Analogue\ORM\Mappable|string $entity
369
-     * @throws \InvalidArgumentException
370
-     * @throws MappingException
371
-     * @return \Analogue\ORM\Repository
372
-     */
373
-    public function repository($entity)
374
-    {
375
-        if (!is_string($entity)) {
376
-            $entity = get_class($entity);
377
-        }
378
-
379
-        // First we check if the repository is not already created.
380
-        if (array_key_exists($entity, $this->repositories)) {
381
-            return $this->repositories[$entity];
382
-        }
383
-
384
-        $this->repositories[$entity] = new Repository($this->mapper($entity));
385
-
386
-        return $this->repositories[$entity];
387
-    }
388
-
389
-    /**
390
-     * Return true is the object is registered as value object
391
-     *
392
-     * @param  mixed $object
393
-     * @return boolean
394
-     */
395
-    public function isValueObject($object)
396
-    {
397
-        if (!is_string($object)) {
398
-            $object = get_class($object);
399
-        }
400
-
401
-        return array_key_exists($object, $this->valueClasses);
402
-    }
403
-
404
-    /**
405
-     * Get the Value Map for a given Value Object Class
406
-     *
407
-     * @param  string $valueObject
408
-     * @throws MappingException
409
-     * @return \Analogue\ORM\ValueMap
410
-     */
411
-    public function getValueMap($valueObject)
412
-    {
413
-        if (!is_string($valueObject)) {
414
-            $valueObject = get_class($valueObject);
415
-        }
416
-
417
-        if (!array_key_exists($valueObject, $this->valueClasses)) {
418
-            $this->registerValueObject($valueObject);
419
-        }
420
-
421
-        /** @var ValueMap $valueMap */
422
-        $valueMap = new $this->valueClasses[$valueObject];
423
-
424
-        $valueMap->setClass($valueObject);
425
-
426
-        return $valueMap;
427
-    }
428
-
429
-    /**
430
-     * Register a Value Object
431
-     *
432
-     * @param  string $valueObject
433
-     * @param  string $valueMap
434
-     * @throws MappingException
435
-     * @return void
436
-     */
437
-    public function registerValueObject($valueObject, $valueMap = null)
438
-    {
439
-        if (!is_string($valueObject)) {
440
-            $valueObject = get_class($valueObject);
441
-        }
442
-
443
-        if ($valueMap === null) {
444
-            $valueMap = $valueObject . 'Map';
445
-        }
446
-
447
-        if (!class_exists($valueMap)) {
448
-            throw new MappingException("$valueMap doesn't exists");
449
-        }
450
-
451
-        $this->valueClasses[$valueObject] = $valueMap;
452
-    }
453
-
454
-    /**
455
-     * Instantiate a new Value Object instance
456
-     *
457
-     * @param  string $valueObject
458
-     * @return \Analogue\ORM\ValueObject
459
-     */
460
-    public function getValueObjectInstance($valueObject)
461
-    {
462
-        $prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
463
-
464
-        return $prototype;
465
-    }
466
-
467
-    /**
468
-     * Register Analogue Plugin
469
-     *
470
-     * @param  string $plugin class
471
-     * @return void
472
-     */
473
-    public function registerPlugin($plugin)
474
-    {
475
-        /** @var AnaloguePluginInterface $plugin */
476
-        $plugin = new $plugin($this);
477
-
478
-        $this->events = array_merge($this->events, $plugin->getCustomEvents());
479
-
480
-        $plugin->register();
481
-    }
482
-
483
-    /**
484
-     * Register event listeners that will be fired regardless the type
485
-     * of the entity.
486
-     *
487
-     * @param  string   $event
488
-     * @param  \Closure $callback
489
-     * @throws \LogicException
490
-     * @return void
491
-     */
492
-    public function registerGlobalEvent($event, $callback)
493
-    {
494
-        if (!in_array($event, $this->events, false)) {
495
-            throw new \LogicException("Analogue : Event $event doesn't exist");
496
-        }
497
-
498
-        $this->eventDispatcher->listen("analogue.{$event}.*", $callback);
499
-    }
500
-
501
-    /**
502
-     * Shortcut to Mapper store
503
-     *
504
-     * @param  mixed $entity
505
-     * @throws MappingException
506
-     * @return mixed
507
-     * @throws \InvalidArgumentException
508
-     */
509
-    public function store($entity)
510
-    {
511
-        return $this->mapper($entity)->store($entity);
512
-    }
513
-
514
-    /**
515
-     * Shortcut to Mapper delete
516
-     *
517
-     * @param  mixed $entity
518
-     * @throws MappingException
519
-     * @return \Illuminate\Support\Collection|null
520
-     * @throws \InvalidArgumentException
521
-     */
522
-    public function delete($entity)
523
-    {
524
-        return $this->mapper($entity)->delete($entity);
525
-    }
526
-
527
-    /**
528
-     * Shortcut to Mapper query
529
-     *
530
-     * @param  mixed $entity
531
-     * @throws MappingException
532
-     * @return Query
533
-     * @throws \InvalidArgumentException
534
-     */
535
-    public function query($entity)
536
-    {
537
-        return $this->mapper($entity)->query();
538
-    }
539
-
540
-    /**
541
-     * Shortcut to Mapper Global Query
542
-     *
543
-     * @param  mixed $entity
544
-     * @throws MappingException
545
-     * @return Query
546
-     * @throws \InvalidArgumentException
547
-     */
548
-    public function globalQuery($entity)
549
-    {
550
-        return $this->mapper($entity)->globalQuery();
551
-    }
552
-
553
-    /**
554
-     * @param array $morphMap
555
-     * @return $this
556
-     */
557
-    public function morphMap(array $morphMap)
558
-    {
559
-        $this->morphMap = $morphMap;
560
-
561
-        return $this;
562
-    }
563
-
564
-    /**
565
-     * @param string $class
566
-     * @return mixed
567
-     */
568
-    public function getMorphMap($class)
569
-    {
570
-        $key = array_search($class, $this->morphMap, false);
571
-
572
-        return $key !== false ? $key : $class;
573
-    }
191
+		throw new \InvalidArgumentException('Invalid entity type');
192
+	}
193
+
194
+	/**
195
+	 * @param string $key
196
+	 * @return string
197
+	 */
198
+	public function getInverseMorphMap($key)
199
+	{
200
+		return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
201
+	}
202
+
203
+	/**
204
+	 * Build a new Mapper instance for a given Entity
205
+	 *
206
+	 * @param  string $entity
207
+	 * @param         $entityMap
208
+	 * @throws MappingException
209
+	 * @return Mapper
210
+	 */
211
+	protected function buildMapper($entity, $entityMap)
212
+	{
213
+		// If an EntityMap hasn't been manually registered by the user
214
+		// register it at runtime.
215
+		if (!$this->isRegisteredEntity($entity)) {
216
+			$this->register($entity, $entityMap);
217
+		}
218
+
219
+		$entityMap = $this->entityClasses[$entity];
220
+
221
+		$factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
222
+
223
+		$mapper = $factory->make($entity, $entityMap);
224
+
225
+		$this->mappers[$entity] = $mapper;
226
+
227
+		// At this point we can safely call the boot() method on the entityMap as
228
+		// the mapper is now instantiated & registered within the manager.
229
+
230
+		$mapper->getEntityMap()->boot();
231
+
232
+		return $mapper;
233
+	}
234
+
235
+	/**
236
+	 * Check if the entity is already registered
237
+	 *
238
+	 * @param  string|Entity $entity
239
+	 * @return boolean
240
+	 */
241
+	public function isRegisteredEntity($entity)
242
+	{
243
+		if (!is_string($entity)) {
244
+			$entity = get_class($entity);
245
+		}
246
+
247
+		return array_key_exists($entity, $this->entityClasses);
248
+	}
249
+
250
+	/**
251
+	 * Return true if an object is an array or iterator
252
+	 *
253
+	 * @param  mixed $argument
254
+	 * @return boolean
255
+	 */
256
+	public function isTraversable($argument)
257
+	{
258
+		return $argument instanceof \Traversable || is_array($argument);
259
+	}
260
+
261
+	/**
262
+	 * Set strict mode for entityMap instantiation
263
+	 * 
264
+	 * @param boolean $mode
265
+	 */
266
+	public function setStrictMode($mode)
267
+	{
268
+		$this->strictMode = $mode;
269
+	}
270
+
271
+	/**
272
+	 * Register an entity
273
+	 *
274
+	 * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
275
+	 * @param  string|EntityMap              $entityMap map's class name
276
+	 * @throws MappingException
277
+	 * @return void
278
+	 */
279
+	public function register($entity, $entityMap = null)
280
+	{
281
+		// If an object is provider, get the class name from it
282
+		if (!is_string($entity)) {
283
+			$entity = get_class($entity);
284
+		}
285
+
286
+		if ($this->isRegisteredEntity($entity)) {
287
+			throw new MappingException("Entity $entity is already registered.");
288
+		}
289
+
290
+		if (!class_exists($entity)) {
291
+			throw new MappingException("Class $entity does not exists");
292
+		}
293
+
294
+		if ($entityMap === null) {
295
+			$entityMap = $this->getEntityMapInstanceFor($entity);
296
+		}
297
+
298
+		if (is_string($entityMap)) {
299
+			$entityMap = new $entityMap;
300
+		}
301
+
302
+		if (!$entityMap instanceof EntityMap) {
303
+			throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
304
+		}
305
+
306
+		$entityMap->setClass($entity);
307
+
308
+		$entityMap->setManager($this);
309
+
310
+		$this->entityClasses[$entity] = $entityMap;
311
+	}
312
+
313
+	/**
314
+	 * Get the entity map instance for a custom entity
315
+	 *
316
+	 * @param  string $entity
317
+	 * @return \Analogue\ORM\Mappable
318
+	 * @throws EntityMapNotFoundException
319
+	 */
320
+	protected function getEntityMapInstanceFor($entity)
321
+	{
322
+		if (class_exists($entity . 'Map')) {
323
+			$map = $entity . 'Map';
324
+			$map = new $map;
325
+		} else {
326
+			if ($this->strictMode) {
327
+				throw new EntityMapNotFoundException("No EntityMap registered for $entity");
328
+			}
329
+			$map = $this->getNewEntityMap();
330
+		}
331
+
332
+		return $map;
333
+	}
334
+
335
+	/**
336
+	 * Dynamically create an entity map for a custom entity class
337
+	 *
338
+	 * @return EntityMap
339
+	 */
340
+	protected function getNewEntityMap()
341
+	{
342
+		return new EntityMap;
343
+	}
344
+
345
+	/**
346
+	 * Return the Singleton instance of the manager
347
+	 *
348
+	 * @return Manager
349
+	 */
350
+	public static function getInstance()
351
+	{
352
+		return static::$instance;
353
+	}
354
+
355
+	/**
356
+	 * Return the Driver Manager's instance
357
+	 *
358
+	 * @return \Analogue\ORM\Drivers\Manager
359
+	 */
360
+	public function getDriverManager()
361
+	{
362
+		return $this->drivers;
363
+	}
364
+
365
+	/**
366
+	 * Get the Repository instance for the given Entity
367
+	 *
368
+	 * @param  \Analogue\ORM\Mappable|string $entity
369
+	 * @throws \InvalidArgumentException
370
+	 * @throws MappingException
371
+	 * @return \Analogue\ORM\Repository
372
+	 */
373
+	public function repository($entity)
374
+	{
375
+		if (!is_string($entity)) {
376
+			$entity = get_class($entity);
377
+		}
378
+
379
+		// First we check if the repository is not already created.
380
+		if (array_key_exists($entity, $this->repositories)) {
381
+			return $this->repositories[$entity];
382
+		}
383
+
384
+		$this->repositories[$entity] = new Repository($this->mapper($entity));
385
+
386
+		return $this->repositories[$entity];
387
+	}
388
+
389
+	/**
390
+	 * Return true is the object is registered as value object
391
+	 *
392
+	 * @param  mixed $object
393
+	 * @return boolean
394
+	 */
395
+	public function isValueObject($object)
396
+	{
397
+		if (!is_string($object)) {
398
+			$object = get_class($object);
399
+		}
400
+
401
+		return array_key_exists($object, $this->valueClasses);
402
+	}
403
+
404
+	/**
405
+	 * Get the Value Map for a given Value Object Class
406
+	 *
407
+	 * @param  string $valueObject
408
+	 * @throws MappingException
409
+	 * @return \Analogue\ORM\ValueMap
410
+	 */
411
+	public function getValueMap($valueObject)
412
+	{
413
+		if (!is_string($valueObject)) {
414
+			$valueObject = get_class($valueObject);
415
+		}
416
+
417
+		if (!array_key_exists($valueObject, $this->valueClasses)) {
418
+			$this->registerValueObject($valueObject);
419
+		}
420
+
421
+		/** @var ValueMap $valueMap */
422
+		$valueMap = new $this->valueClasses[$valueObject];
423
+
424
+		$valueMap->setClass($valueObject);
425
+
426
+		return $valueMap;
427
+	}
428
+
429
+	/**
430
+	 * Register a Value Object
431
+	 *
432
+	 * @param  string $valueObject
433
+	 * @param  string $valueMap
434
+	 * @throws MappingException
435
+	 * @return void
436
+	 */
437
+	public function registerValueObject($valueObject, $valueMap = null)
438
+	{
439
+		if (!is_string($valueObject)) {
440
+			$valueObject = get_class($valueObject);
441
+		}
442
+
443
+		if ($valueMap === null) {
444
+			$valueMap = $valueObject . 'Map';
445
+		}
446
+
447
+		if (!class_exists($valueMap)) {
448
+			throw new MappingException("$valueMap doesn't exists");
449
+		}
450
+
451
+		$this->valueClasses[$valueObject] = $valueMap;
452
+	}
453
+
454
+	/**
455
+	 * Instantiate a new Value Object instance
456
+	 *
457
+	 * @param  string $valueObject
458
+	 * @return \Analogue\ORM\ValueObject
459
+	 */
460
+	public function getValueObjectInstance($valueObject)
461
+	{
462
+		$prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
463
+
464
+		return $prototype;
465
+	}
466
+
467
+	/**
468
+	 * Register Analogue Plugin
469
+	 *
470
+	 * @param  string $plugin class
471
+	 * @return void
472
+	 */
473
+	public function registerPlugin($plugin)
474
+	{
475
+		/** @var AnaloguePluginInterface $plugin */
476
+		$plugin = new $plugin($this);
477
+
478
+		$this->events = array_merge($this->events, $plugin->getCustomEvents());
479
+
480
+		$plugin->register();
481
+	}
482
+
483
+	/**
484
+	 * Register event listeners that will be fired regardless the type
485
+	 * of the entity.
486
+	 *
487
+	 * @param  string   $event
488
+	 * @param  \Closure $callback
489
+	 * @throws \LogicException
490
+	 * @return void
491
+	 */
492
+	public function registerGlobalEvent($event, $callback)
493
+	{
494
+		if (!in_array($event, $this->events, false)) {
495
+			throw new \LogicException("Analogue : Event $event doesn't exist");
496
+		}
497
+
498
+		$this->eventDispatcher->listen("analogue.{$event}.*", $callback);
499
+	}
500
+
501
+	/**
502
+	 * Shortcut to Mapper store
503
+	 *
504
+	 * @param  mixed $entity
505
+	 * @throws MappingException
506
+	 * @return mixed
507
+	 * @throws \InvalidArgumentException
508
+	 */
509
+	public function store($entity)
510
+	{
511
+		return $this->mapper($entity)->store($entity);
512
+	}
513
+
514
+	/**
515
+	 * Shortcut to Mapper delete
516
+	 *
517
+	 * @param  mixed $entity
518
+	 * @throws MappingException
519
+	 * @return \Illuminate\Support\Collection|null
520
+	 * @throws \InvalidArgumentException
521
+	 */
522
+	public function delete($entity)
523
+	{
524
+		return $this->mapper($entity)->delete($entity);
525
+	}
526
+
527
+	/**
528
+	 * Shortcut to Mapper query
529
+	 *
530
+	 * @param  mixed $entity
531
+	 * @throws MappingException
532
+	 * @return Query
533
+	 * @throws \InvalidArgumentException
534
+	 */
535
+	public function query($entity)
536
+	{
537
+		return $this->mapper($entity)->query();
538
+	}
539
+
540
+	/**
541
+	 * Shortcut to Mapper Global Query
542
+	 *
543
+	 * @param  mixed $entity
544
+	 * @throws MappingException
545
+	 * @return Query
546
+	 * @throws \InvalidArgumentException
547
+	 */
548
+	public function globalQuery($entity)
549
+	{
550
+		return $this->mapper($entity)->globalQuery();
551
+	}
552
+
553
+	/**
554
+	 * @param array $morphMap
555
+	 * @return $this
556
+	 */
557
+	public function morphMap(array $morphMap)
558
+	{
559
+		$this->morphMap = $morphMap;
560
+
561
+		return $this;
562
+	}
563
+
564
+	/**
565
+	 * @param string $class
566
+	 * @return mixed
567
+	 */
568
+	public function getMorphMap($class)
569
+	{
570
+		$key = array_search($class, $this->morphMap, false);
571
+
572
+		return $key !== false ? $key : $class;
573
+	}
574 574
 }
Please login to merge, or discard this patch.
src/helpers.php 2 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -2,7 +2,7 @@  discard block
 block discarded – undo
2 2
 
3 3
 use Analogue\ORM\System\Manager;
4 4
 
5
-if (! function_exists('analogue')) {
5
+if (!function_exists('analogue')) {
6 6
 
7 7
     /**
8 8
      * Return analogue's manager instance
@@ -17,7 +17,7 @@  discard block
 block discarded – undo
17 17
 }
18 18
 
19 19
 
20
-if (! function_exists('mapper')) {
20
+if (!function_exists('mapper')) {
21 21
 
22 22
     /**
23 23
      * Create a mapper for a given entity (static alias)
Please login to merge, or discard this patch.
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -4,29 +4,29 @@
 block discarded – undo
4 4
 
5 5
 if (! function_exists('analogue')) {
6 6
 
7
-    /**
8
-     * Return analogue's manager instance
9
-     * 
10
-     * @return \Analogue\ORM\System\Manager
11
-     */
12
-    function analogue()
13
-    {
14
-        return Manager::getInstance();
15
-    }
7
+	/**
8
+	 * Return analogue's manager instance
9
+	 * 
10
+	 * @return \Analogue\ORM\System\Manager
11
+	 */
12
+	function analogue()
13
+	{
14
+		return Manager::getInstance();
15
+	}
16 16
 }
17 17
 
18 18
 
19 19
 if (! function_exists('mapper')) {
20 20
 
21
-    /**
22
-     * Create a mapper for a given entity (static alias)
23
-     * 
24
-     * @param \Analogue\ORM\Mappable|string $entity
25
-     * @param mixed $entityMap 
26
-     * @return Mapper
27
-     */
28
-    function mapper($entity, $entityMap = null)
29
-    {
30
-        return Manager::getMapper($entity, $entityMap);
31
-    }
21
+	/**
22
+	 * Create a mapper for a given entity (static alias)
23
+	 * 
24
+	 * @param \Analogue\ORM\Mappable|string $entity
25
+	 * @param mixed $entityMap 
26
+	 * @return Mapper
27
+	 */
28
+	function mapper($entity, $entityMap = null)
29
+	{
30
+		return Manager::getMapper($entity, $entityMap);
31
+	}
32 32
 }
Please login to merge, or discard this patch.
src/System/Wrappers/Wrapper.php 1 patch
Indentation   +113 added lines, -113 removed lines patch added patch discarded remove patch
@@ -11,117 +11,117 @@
 block discarded – undo
11 11
  */
12 12
 abstract class Wrapper implements InternallyMappable
13 13
 {
14
-    /**
15
-     * Original Entity Object
16
-     *
17
-     * @var mixed
18
-     */
19
-    protected $entity;
20
-
21
-    /**
22
-     * Corresponding EntityMap
23
-     *
24
-     * @var \Analogue\ORM\EntityMap
25
-     */
26
-    protected $entityMap;
27
-
28
-    /**
29
-     * Wrapper constructor.
30
-     * @param $entity
31
-     * @param $entityMap
32
-     */
33
-    public function __construct($entity, $entityMap)
34
-    {
35
-        $this->entity = $entity;
36
-        $this->entityMap = $entityMap;
37
-    }
38
-
39
-    /**
40
-     * Return the wrapped entity class
41
-     *
42
-     * @return mixed
43
-     */
44
-    public function getEntityClass()
45
-    {
46
-        return get_class($this->entity);
47
-    }
48
-
49
-    /**
50
-     * Returns the wrapped entity
51
-     *
52
-     * @return mixed
53
-     */
54
-    public function getObject()
55
-    {
56
-        return $this->entity;
57
-    }
58
-
59
-    /**
60
-     * Returns the wrapped entity's map
61
-     *
62
-     * @return mixed
63
-     */
64
-    public function getMap()
65
-    {
66
-        return $this->entityMap;
67
-    }
68
-
69
-    /**
70
-     * Set the lazyloading proxies on the wrapped entity objet
71
-     *
72
-     * @return void
73
-     */
74
-    public function setProxies()
75
-    {
76
-        $attributes = $this->getEntityAttributes();
77
-        $singleRelations = $this->entityMap->getSingleRelationships();
78
-        $manyRelations = $this->entityMap->getManyRelationships();
79
-
80
-        $proxies = [];
81
-
82
-        foreach ($this->entityMap->getRelationships() as $relation) {
83
-            if (!array_key_exists($relation, $attributes) || is_null($attributes[$relation])) {
84
-                if (in_array($relation, $singleRelations)) {
85
-                    $proxies[$relation] = new EntityProxy($this->getObject(), $relation);
86
-                }
87
-                if (in_array($relation, $manyRelations)) {
88
-                    $proxies[$relation] = new CollectionProxy($this->getObject(), $relation);
89
-                }
90
-            }
91
-        }
92
-
93
-        foreach ($proxies as $key => $value) {
94
-            $this->setEntityAttribute($key, $value);
95
-        }
96
-    }
97
-
98
-    /**
99
-     * @param string $key
100
-     * @param string $value
101
-     * @return mixed
102
-     */
103
-    abstract public function setEntityAttribute($key, $value);
104
-
105
-    /**
106
-     * @param string $key
107
-     * @return mixed
108
-     */
109
-    abstract public function getEntityAttribute($key);
110
-
111
-    /**
112
-     * @param array $attributes
113
-     * @return mixed
114
-     */
115
-    abstract public function setEntityAttributes(array $attributes);
116
-
117
-    /**
118
-     * @return mixed
119
-     */
120
-    abstract public function getEntityAttributes();
121
-
122
-    /**
123
-     * @param string $key
124
-     * @return mixed
125
-     */
126
-    abstract public function hasAttribute($key);
14
+	/**
15
+	 * Original Entity Object
16
+	 *
17
+	 * @var mixed
18
+	 */
19
+	protected $entity;
20
+
21
+	/**
22
+	 * Corresponding EntityMap
23
+	 *
24
+	 * @var \Analogue\ORM\EntityMap
25
+	 */
26
+	protected $entityMap;
27
+
28
+	/**
29
+	 * Wrapper constructor.
30
+	 * @param $entity
31
+	 * @param $entityMap
32
+	 */
33
+	public function __construct($entity, $entityMap)
34
+	{
35
+		$this->entity = $entity;
36
+		$this->entityMap = $entityMap;
37
+	}
38
+
39
+	/**
40
+	 * Return the wrapped entity class
41
+	 *
42
+	 * @return mixed
43
+	 */
44
+	public function getEntityClass()
45
+	{
46
+		return get_class($this->entity);
47
+	}
48
+
49
+	/**
50
+	 * Returns the wrapped entity
51
+	 *
52
+	 * @return mixed
53
+	 */
54
+	public function getObject()
55
+	{
56
+		return $this->entity;
57
+	}
58
+
59
+	/**
60
+	 * Returns the wrapped entity's map
61
+	 *
62
+	 * @return mixed
63
+	 */
64
+	public function getMap()
65
+	{
66
+		return $this->entityMap;
67
+	}
68
+
69
+	/**
70
+	 * Set the lazyloading proxies on the wrapped entity objet
71
+	 *
72
+	 * @return void
73
+	 */
74
+	public function setProxies()
75
+	{
76
+		$attributes = $this->getEntityAttributes();
77
+		$singleRelations = $this->entityMap->getSingleRelationships();
78
+		$manyRelations = $this->entityMap->getManyRelationships();
79
+
80
+		$proxies = [];
81
+
82
+		foreach ($this->entityMap->getRelationships() as $relation) {
83
+			if (!array_key_exists($relation, $attributes) || is_null($attributes[$relation])) {
84
+				if (in_array($relation, $singleRelations)) {
85
+					$proxies[$relation] = new EntityProxy($this->getObject(), $relation);
86
+				}
87
+				if (in_array($relation, $manyRelations)) {
88
+					$proxies[$relation] = new CollectionProxy($this->getObject(), $relation);
89
+				}
90
+			}
91
+		}
92
+
93
+		foreach ($proxies as $key => $value) {
94
+			$this->setEntityAttribute($key, $value);
95
+		}
96
+	}
97
+
98
+	/**
99
+	 * @param string $key
100
+	 * @param string $value
101
+	 * @return mixed
102
+	 */
103
+	abstract public function setEntityAttribute($key, $value);
104
+
105
+	/**
106
+	 * @param string $key
107
+	 * @return mixed
108
+	 */
109
+	abstract public function getEntityAttribute($key);
110
+
111
+	/**
112
+	 * @param array $attributes
113
+	 * @return mixed
114
+	 */
115
+	abstract public function setEntityAttributes(array $attributes);
116
+
117
+	/**
118
+	 * @return mixed
119
+	 */
120
+	abstract public function getEntityAttributes();
121
+
122
+	/**
123
+	 * @param string $key
124
+	 * @return mixed
125
+	 */
126
+	abstract public function hasAttribute($key);
127 127
 }
Please login to merge, or discard this patch.
src/Drivers/IlluminateDBAdapter.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -27,7 +27,7 @@
 block discarded – undo
27 27
     /**
28 28
      * Return a new Query instance for this driver
29 29
      *
30
-     * @return QueryAdapter
30
+     * @return IlluminateQueryBuilder
31 31
      */
32 32
     public function getQuery()
33 33
     {
Please login to merge, or discard this patch.
Indentation   +56 added lines, -56 removed lines patch added patch discarded remove patch
@@ -10,68 +10,68 @@
 block discarded – undo
10 10
  */
11 11
 class IlluminateDBAdapter implements DBAdapter
12 12
 {
13
-    /**
14
-     * @var Connection
15
-     */
16
-    protected $connection;
13
+	/**
14
+	 * @var Connection
15
+	 */
16
+	protected $connection;
17 17
 
18
-    /**
19
-     * IlluminateDBAdapter constructor.
20
-     * @param Connection $connection
21
-     */
22
-    public function __construct(Connection $connection)
23
-    {
24
-        $this->connection = $connection;
25
-    }
18
+	/**
19
+	 * IlluminateDBAdapter constructor.
20
+	 * @param Connection $connection
21
+	 */
22
+	public function __construct(Connection $connection)
23
+	{
24
+		$this->connection = $connection;
25
+	}
26 26
 
27
-    /**
28
-     * Return a new Query instance for this driver
29
-     *
30
-     * @return QueryAdapter
31
-     */
32
-    public function getQuery()
33
-    {
34
-        $connection = $this->connection;
27
+	/**
28
+	 * Return a new Query instance for this driver
29
+	 *
30
+	 * @return QueryAdapter
31
+	 */
32
+	public function getQuery()
33
+	{
34
+		$connection = $this->connection;
35 35
 
36
-        $grammar = $connection->getQueryGrammar();
36
+		$grammar = $connection->getQueryGrammar();
37 37
 
38
-        return new IlluminateQueryBuilder($connection, $grammar, $connection->getPostProcessor());
39
-    }
38
+		return new IlluminateQueryBuilder($connection, $grammar, $connection->getPostProcessor());
39
+	}
40 40
 
41
-    /**
42
-     * Get the date format supported by the current connection
43
-     *
44
-     * @return string
45
-     */
46
-    public function getDateFormat()
47
-    {
48
-        return $this->connection->getQueryGrammar()->getDateFormat();
49
-    }
41
+	/**
42
+	 * Get the date format supported by the current connection
43
+	 *
44
+	 * @return string
45
+	 */
46
+	public function getDateFormat()
47
+	{
48
+		return $this->connection->getQueryGrammar()->getDateFormat();
49
+	}
50 50
 
51
-    /**
52
-     * Start a DB transaction on driver that supports it.
53
-     * @return void
54
-     */
55
-    public function beginTransaction()
56
-    {
57
-        $this->connection->beginTransaction();
58
-    }
51
+	/**
52
+	 * Start a DB transaction on driver that supports it.
53
+	 * @return void
54
+	 */
55
+	public function beginTransaction()
56
+	{
57
+		$this->connection->beginTransaction();
58
+	}
59 59
 
60
-    /**
61
-     * Commit a DB transaction on driver that supports it.
62
-     * @return void
63
-     */
64
-    public function commit()
65
-    {
66
-        $this->connection->commit();
67
-    }
60
+	/**
61
+	 * Commit a DB transaction on driver that supports it.
62
+	 * @return void
63
+	 */
64
+	public function commit()
65
+	{
66
+		$this->connection->commit();
67
+	}
68 68
 
69
-    /**
70
-     * Rollback a DB transaction
71
-     * @return void
72
-     */
73
-    public function rollback()
74
-    {
75
-        $this->connection->rollBack();
76
-    }
69
+	/**
70
+	 * Rollback a DB transaction
71
+	 * @return void
72
+	 */
73
+	public function rollback()
74
+	{
75
+		$this->connection->rollBack();
76
+	}
77 77
 }
Please login to merge, or discard this patch.