Completed
Push — 5.1 ( f422a4...a89502 )
by Rémi
8s
created
src/System/Manager.php 2 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
      * Create a mapper for a given entity
118 118
      *
119 119
      * @param  \Analogue\ORM\Mappable|string|array|\Traversable $entity
120
-     * @param  mixed                                            $entityMap
120
+     * @param  null|EntityMap                                            $entityMap
121 121
      * @throws MappingException
122 122
      * @throws \InvalidArgumentException
123 123
      * @return Mapper
@@ -215,7 +215,7 @@  discard block
 block discarded – undo
215 215
     /**
216 216
      * Check if the entity is already registered
217 217
      *
218
-     * @param  string|object $entity
218
+     * @param  string $entity
219 219
      * @return boolean
220 220
      */
221 221
     public function isRegisteredEntity($entity)
@@ -230,7 +230,7 @@  discard block
 block discarded – undo
230 230
     /**
231 231
      * Register an entity
232 232
      *
233
-     * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
233
+     * @param  string $entity    entity's class name
234 234
      * @param  string|EntityMap              $entityMap map's class name
235 235
      * @throws MappingException
236 236
      * @return void
Please login to merge, or discard this patch.
Indentation   +504 added lines, -504 removed lines patch added patch discarded remove patch
@@ -15,508 +15,508 @@
 block discarded – undo
15 15
  */
16 16
 class Manager
17 17
 {
18
-    /**
19
-     * Manager instance
20
-     *
21
-     * @var Manager
22
-     */
23
-    protected static $instance;
24
-
25
-    /**
26
-     * Driver Manager
27
-     *
28
-     * @var \Analogue\ORM\Drivers\Manager
29
-     */
30
-    protected $drivers;
31
-
32
-    /**
33
-     * Registered entity classes and corresponding map objects.
34
-     *
35
-     * @var array
36
-     */
37
-    protected $entityClasses = [];
38
-
39
-    /**
40
-     * Key value store of ValueObject Classes and corresponding map classes
41
-     *
42
-     * @var array
43
-     */
44
-    protected $valueClasses = [];
45
-
46
-    /**
47
-     * Morph map
48
-     */
49
-    protected $morphMap = [];
50
-
51
-    /**
52
-     * Loaded Mappers
53
-     *
54
-     * @var array
55
-     */
56
-    protected $mappers = [];
57
-
58
-    /**
59
-     * Loaded Repositories
60
-     *
61
-     * @var array
62
-     */
63
-    protected $repositories = [];
64
-
65
-    /**
66
-     * Event dispatcher instance
67
-     *
68
-     * @var \Illuminate\Contracts\Events\Dispatcher
69
-     */
70
-    protected $eventDispatcher;
71
-
72
-    /**
73
-     * Available Analogue Events
74
-     *
75
-     * @var array
76
-     */
77
-    protected $events = [
78
-        'initializing',
79
-        'initialized',
80
-        'store',
81
-        'stored',
82
-        'creating',
83
-        'created',
84
-        'updating',
85
-        'updated',
86
-        'deleting',
87
-        'deleted',
88
-    ];
89
-
90
-    /**
91
-     * @param \Analogue\ORM\Drivers\Manager $driverManager
92
-     * @param Dispatcher                    $event
93
-     */
94
-    public function __construct(DriverManager $driverManager, Dispatcher $event)
95
-    {
96
-        $this->drivers = $driverManager;
97
-
98
-        $this->eventDispatcher = $event;
99
-
100
-        static::$instance = $this;
101
-    }
102
-
103
-    /**
104
-     * Create a mapper for a given entity (static alias)
105
-     *
106
-     * @param  \Analogue\ORM\Mappable|string $entity
107
-     * @param  null|EntityMap                $entityMap
108
-     * @throws MappingException
109
-     * @return Mapper
110
-     */
111
-    public static function getMapper($entity, $entityMap = null)
112
-    {
113
-        return static::$instance->mapper($entity, $entityMap);
114
-    }
115
-
116
-    /**
117
-     * Create a mapper for a given entity
118
-     *
119
-     * @param  \Analogue\ORM\Mappable|string|array|\Traversable $entity
120
-     * @param  mixed                                            $entityMap
121
-     * @throws MappingException
122
-     * @throws \InvalidArgumentException
123
-     * @return Mapper
124
-     */
125
-    public function mapper($entity, $entityMap = null)
126
-    {
127
-        if ($entity instanceof Wrapper) {
128
-            throw new MappingException('Tried to instantiate mapper on wrapped Entity');
129
-        }
130
-
131
-        $entity = $this->resolveEntityClass($entity);
132
-
133
-        $entity = $this->getInverseMorphMap($entity);
134
-
135
-        // Return existing mapper instance if exists.
136
-        if (array_key_exists($entity, $this->mappers)) {
137
-            return $this->mappers[$entity];
138
-        } else {
139
-            return $this->buildMapper($entity, $entityMap);
140
-        }
141
-    }
142
-
143
-    /**
144
-     * This method resolve entity class from mappable instances or iterators
145
-     *
146
-     * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
147
-     * @return string
148
-     */
149
-    protected function resolveEntityClass($entity)
150
-    {
151
-        switch (true) {
152
-            case Support::isTraversable($entity):
153
-                if (!count($entity)) {
154
-                    throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
155
-                }
156
-
157
-                $firstEntityItem = ($entity instanceof \Iterator || $entity instanceof \IteratorAggregate)
158
-                    ? $entity->current()
159
-                    : current($entity);
160
-
161
-                return $this->resolveEntityClass($firstEntityItem);
162
-
163
-            case is_object($entity):
164
-                return get_class($entity);
165
-
166
-            case !is_string($entity):
167
-                throw new \InvalidArgumentException('Invalid mapper Entity type');
168
-                break;
169
-        }
170
-
171
-        return $entity;
172
-    }
173
-
174
-    /**
175
-     * @param string $key
176
-     * @return string
177
-     */
178
-    public function getInverseMorphMap($key)
179
-    {
180
-        return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
181
-    }
182
-
183
-    /**
184
-     * Build a new Mapper instance for a given Entity
185
-     *
186
-     * @param  string $entity
187
-     * @param         $entityMap
188
-     * @throws MappingException
189
-     * @return Mapper
190
-     */
191
-    protected function buildMapper($entity, $entityMap)
192
-    {
193
-        // If an EntityMap hasn't been manually registered by the user
194
-        // register it at runtime.
195
-        if (!$this->isRegisteredEntity($entity)) {
196
-            $this->register($entity, $entityMap);
197
-        }
198
-
199
-        $entityMap = $this->entityClasses[$entity];
200
-
201
-        $factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
202
-
203
-        $mapper = $factory->make($entity, $entityMap);
204
-
205
-        $this->mappers[$entity] = $mapper;
206
-
207
-        // At this point we can safely call the boot() method on the entityMap as
208
-        // the mapper is now instantiated & registered within the manager.
209
-
210
-        $mapper->getEntityMap()->boot();
211
-
212
-        return $mapper;
213
-    }
214
-
215
-    /**
216
-     * Check if the entity is already registered
217
-     *
218
-     * @param  string|object $entity
219
-     * @return boolean
220
-     */
221
-    public function isRegisteredEntity($entity)
222
-    {
223
-        if (!is_string($entity)) {
224
-            $entity = get_class($entity);
225
-        }
226
-
227
-        return array_key_exists($entity, $this->entityClasses);
228
-    }
229
-
230
-    /**
231
-     * Register an entity
232
-     *
233
-     * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
234
-     * @param  string|EntityMap              $entityMap map's class name
235
-     * @throws MappingException
236
-     * @return void
237
-     */
238
-    public function register($entity, $entityMap = null)
239
-    {
240
-        // If an object is provider, get the class name from it
241
-        if (!is_string($entity)) {
242
-            $entity = get_class($entity);
243
-        }
244
-
245
-        if ($this->isRegisteredEntity($entity)) {
246
-            throw new MappingException("Entity $entity is already registered.");
247
-        }
248
-
249
-        if (!class_exists($entity)) {
250
-            throw new MappingException("Class $entity does not exists");
251
-        }
252
-
253
-        if (is_null($entityMap)) {
254
-            $entityMap = $this->getEntityMapInstanceFor($entity);
255
-        }
256
-
257
-        if (is_string($entityMap)) {
258
-            $entityMap = new $entityMap;
259
-        }
260
-
261
-        if (!$entityMap instanceof EntityMap) {
262
-            throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
263
-        }
264
-
265
-        $entityMap->setClass($entity);
266
-
267
-        $entityMap->setManager($this);
268
-
269
-        $this->entityClasses[$entity] = $entityMap;
270
-    }
271
-
272
-    /**
273
-     * Get the entity map instance for a custom entity
274
-     *
275
-     * @param  string $entity
276
-     * @return \Analogue\ORM\Mappable
277
-     */
278
-    protected function getEntityMapInstanceFor($entity)
279
-    {
280
-        if (class_exists($entity . 'Map')) {
281
-            $map = $entity . 'Map';
282
-            $map = new $map;
283
-        } else {
284
-            // Generate an EntityMap object
285
-            $map = $this->getNewEntityMap();
286
-        }
287
-
288
-        return $map;
289
-    }
290
-
291
-    /**
292
-     * Dynamically create an entity map for a custom entity class
293
-     *
294
-     * @return EntityMap
295
-     */
296
-    protected function getNewEntityMap()
297
-    {
298
-        return new EntityMap;
299
-    }
300
-
301
-    /**
302
-     * Return the Singleton instance of the manager
303
-     *
304
-     * @return Manager
305
-     */
306
-    public static function getInstance()
307
-    {
308
-        return static::$instance;
309
-    }
310
-
311
-    /**
312
-     * Return the Driver Manager's instance
313
-     *
314
-     * @return \Analogue\ORM\Drivers\Manager
315
-     */
316
-    public function getDriverManager()
317
-    {
318
-        return $this->drivers;
319
-    }
320
-
321
-    /**
322
-     * Get the Repository instance for the given Entity
323
-     *
324
-     * @param  \Analogue\ORM\Mappable|string $entity
325
-     * @throws \InvalidArgumentException
326
-     * @throws MappingException
327
-     * @return \Analogue\ORM\Repository
328
-     */
329
-    public function repository($entity)
330
-    {
331
-        if (!is_string($entity)) {
332
-            $entity = get_class($entity);
333
-        }
334
-
335
-        // First we check if the repository is not already created.
336
-        if (array_key_exists($entity, $this->repositories)) {
337
-            return $this->repositories[$entity];
338
-        }
339
-
340
-        $this->repositories[$entity] = new Repository($this->mapper($entity));
341
-
342
-        return $this->repositories[$entity];
343
-    }
344
-
345
-    /**
346
-     * Return true is the object is registered as value object
347
-     *
348
-     * @param  mixed $object
349
-     * @return boolean
350
-     */
351
-    public function isValueObject($object)
352
-    {
353
-        if (!is_string($object)) {
354
-            $object = get_class($object);
355
-        }
356
-
357
-        return array_key_exists($object, $this->valueClasses);
358
-    }
359
-
360
-    /**
361
-     * Get the Value Map for a given Value Object Class
362
-     *
363
-     * @param  string $valueObject
364
-     * @throws MappingException
365
-     * @return \Analogue\ORM\ValueMap
366
-     */
367
-    public function getValueMap($valueObject)
368
-    {
369
-        if (!is_string($valueObject)) {
370
-            $valueObject = get_class($valueObject);
371
-        }
372
-
373
-        if (!array_key_exists($valueObject, $this->valueClasses)) {
374
-            $this->registerValueObject($valueObject);
375
-        }
376
-        $valueMap = new $this->valueClasses[$valueObject];
377
-
378
-        $valueMap->setClass($valueObject);
379
-
380
-        return $valueMap;
381
-    }
382
-
383
-    /**
384
-     * Register a Value Object
385
-     *
386
-     * @param  string $valueObject
387
-     * @param  string $valueMap
388
-     * @throws MappingException
389
-     * @return void
390
-     */
391
-    public function registerValueObject($valueObject, $valueMap = null)
392
-    {
393
-        if (!is_string($valueObject)) {
394
-            $valueObject = get_class($valueObject);
395
-        }
396
-
397
-        if (is_null($valueMap)) {
398
-            $valueMap = $valueObject . 'Map';
399
-        }
400
-
401
-        if (!class_exists($valueMap)) {
402
-            throw new MappingException("$valueMap doesn't exists");
403
-        }
404
-
405
-        $this->valueClasses[$valueObject] = $valueMap;
406
-    }
407
-
408
-    /**
409
-     * Instantiate a new Value Object instance
410
-     *
411
-     * @param  string $valueObject
412
-     * @return \Analogue\ORM\ValueObject
413
-     */
414
-    public function getValueObjectInstance($valueObject)
415
-    {
416
-        $prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
417
-
418
-        return $prototype;
419
-    }
420
-
421
-    /**
422
-     * Register Analogue Plugin
423
-     *
424
-     * @param  string $plugin class
425
-     * @return void
426
-     */
427
-    public function registerPlugin($plugin)
428
-    {
429
-        $plugin = new $plugin($this);
430
-
431
-        $this->events = array_merge($this->events, $plugin->getCustomEvents());
432
-
433
-        $plugin->register();
434
-    }
435
-
436
-    /**
437
-     * Register event listeners that will be fired regardless the type
438
-     * of the entity.
439
-     *
440
-     * @param  string   $event
441
-     * @param  \Closure $callback
442
-     * @throws \Exception
443
-     * @return void
444
-     */
445
-    public function registerGlobalEvent($event, $callback)
446
-    {
447
-        if (!in_array($event, $this->events)) {
448
-            throw new \Exception("Analogue : Event $event doesn't exist");
449
-        }
450
-        $this->eventDispatcher->listen("analogue.{$event}.*", $callback);
451
-    }
452
-
453
-    /**
454
-     * Shortcut to Mapper store
455
-     *
456
-     * @param  mixed $entity
457
-     * @throws MappingException
458
-     * @return mixed
459
-     */
460
-    public function store($entity)
461
-    {
462
-        return $this->mapper($entity)->store($entity);
463
-    }
464
-
465
-    /**
466
-     * Shortcut to Mapper delete
467
-     *
468
-     * @param  mixed $entity
469
-     * @throws MappingException
470
-     * @return \Illuminate\Support\Collection|null
471
-     */
472
-    public function delete($entity)
473
-    {
474
-        return $this->mapper($entity)->delete($entity);
475
-    }
476
-
477
-    /**
478
-     * Shortcut to Mapper query
479
-     *
480
-     * @param  mixed $entity
481
-     * @throws MappingException
482
-     * @return Query
483
-     */
484
-    public function query($entity)
485
-    {
486
-        return $this->mapper($entity)->query();
487
-    }
488
-
489
-    /**
490
-     * Shortcut to Mapper Global Query
491
-     *
492
-     * @param  mixed $entity
493
-     * @throws MappingException
494
-     * @return Query
495
-     */
496
-    public function globalQuery($entity)
497
-    {
498
-        return $this->mapper($entity)->globalQuery();
499
-    }
500
-
501
-    /**
502
-     * @param array $morphMap
503
-     * @return $this
504
-     */
505
-    public function morphMap(array $morphMap)
506
-    {
507
-        $this->morphMap = $morphMap;
508
-
509
-        return $this;
510
-    }
511
-
512
-    /**
513
-     * @param string $class
514
-     * @return mixed
515
-     */
516
-    public function getMorphMap($class)
517
-    {
518
-        $key = array_search($class, $this->morphMap);
519
-
520
-        return $key !== false ? $key : $class;
521
-    }
18
+	/**
19
+	 * Manager instance
20
+	 *
21
+	 * @var Manager
22
+	 */
23
+	protected static $instance;
24
+
25
+	/**
26
+	 * Driver Manager
27
+	 *
28
+	 * @var \Analogue\ORM\Drivers\Manager
29
+	 */
30
+	protected $drivers;
31
+
32
+	/**
33
+	 * Registered entity classes and corresponding map objects.
34
+	 *
35
+	 * @var array
36
+	 */
37
+	protected $entityClasses = [];
38
+
39
+	/**
40
+	 * Key value store of ValueObject Classes and corresponding map classes
41
+	 *
42
+	 * @var array
43
+	 */
44
+	protected $valueClasses = [];
45
+
46
+	/**
47
+	 * Morph map
48
+	 */
49
+	protected $morphMap = [];
50
+
51
+	/**
52
+	 * Loaded Mappers
53
+	 *
54
+	 * @var array
55
+	 */
56
+	protected $mappers = [];
57
+
58
+	/**
59
+	 * Loaded Repositories
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $repositories = [];
64
+
65
+	/**
66
+	 * Event dispatcher instance
67
+	 *
68
+	 * @var \Illuminate\Contracts\Events\Dispatcher
69
+	 */
70
+	protected $eventDispatcher;
71
+
72
+	/**
73
+	 * Available Analogue Events
74
+	 *
75
+	 * @var array
76
+	 */
77
+	protected $events = [
78
+		'initializing',
79
+		'initialized',
80
+		'store',
81
+		'stored',
82
+		'creating',
83
+		'created',
84
+		'updating',
85
+		'updated',
86
+		'deleting',
87
+		'deleted',
88
+	];
89
+
90
+	/**
91
+	 * @param \Analogue\ORM\Drivers\Manager $driverManager
92
+	 * @param Dispatcher                    $event
93
+	 */
94
+	public function __construct(DriverManager $driverManager, Dispatcher $event)
95
+	{
96
+		$this->drivers = $driverManager;
97
+
98
+		$this->eventDispatcher = $event;
99
+
100
+		static::$instance = $this;
101
+	}
102
+
103
+	/**
104
+	 * Create a mapper for a given entity (static alias)
105
+	 *
106
+	 * @param  \Analogue\ORM\Mappable|string $entity
107
+	 * @param  null|EntityMap                $entityMap
108
+	 * @throws MappingException
109
+	 * @return Mapper
110
+	 */
111
+	public static function getMapper($entity, $entityMap = null)
112
+	{
113
+		return static::$instance->mapper($entity, $entityMap);
114
+	}
115
+
116
+	/**
117
+	 * Create a mapper for a given entity
118
+	 *
119
+	 * @param  \Analogue\ORM\Mappable|string|array|\Traversable $entity
120
+	 * @param  mixed                                            $entityMap
121
+	 * @throws MappingException
122
+	 * @throws \InvalidArgumentException
123
+	 * @return Mapper
124
+	 */
125
+	public function mapper($entity, $entityMap = null)
126
+	{
127
+		if ($entity instanceof Wrapper) {
128
+			throw new MappingException('Tried to instantiate mapper on wrapped Entity');
129
+		}
130
+
131
+		$entity = $this->resolveEntityClass($entity);
132
+
133
+		$entity = $this->getInverseMorphMap($entity);
134
+
135
+		// Return existing mapper instance if exists.
136
+		if (array_key_exists($entity, $this->mappers)) {
137
+			return $this->mappers[$entity];
138
+		} else {
139
+			return $this->buildMapper($entity, $entityMap);
140
+		}
141
+	}
142
+
143
+	/**
144
+	 * This method resolve entity class from mappable instances or iterators
145
+	 *
146
+	 * @param \Analogue\ORM\Mappable|string|array|\Traversable $entity
147
+	 * @return string
148
+	 */
149
+	protected function resolveEntityClass($entity)
150
+	{
151
+		switch (true) {
152
+			case Support::isTraversable($entity):
153
+				if (!count($entity)) {
154
+					throw new \InvalidArgumentException('Length of Entity collection must be greater than 0');
155
+				}
156
+
157
+				$firstEntityItem = ($entity instanceof \Iterator || $entity instanceof \IteratorAggregate)
158
+					? $entity->current()
159
+					: current($entity);
160
+
161
+				return $this->resolveEntityClass($firstEntityItem);
162
+
163
+			case is_object($entity):
164
+				return get_class($entity);
165
+
166
+			case !is_string($entity):
167
+				throw new \InvalidArgumentException('Invalid mapper Entity type');
168
+				break;
169
+		}
170
+
171
+		return $entity;
172
+	}
173
+
174
+	/**
175
+	 * @param string $key
176
+	 * @return string
177
+	 */
178
+	public function getInverseMorphMap($key)
179
+	{
180
+		return array_key_exists($key, $this->morphMap) ? $this->morphMap[$key] : $key;
181
+	}
182
+
183
+	/**
184
+	 * Build a new Mapper instance for a given Entity
185
+	 *
186
+	 * @param  string $entity
187
+	 * @param         $entityMap
188
+	 * @throws MappingException
189
+	 * @return Mapper
190
+	 */
191
+	protected function buildMapper($entity, $entityMap)
192
+	{
193
+		// If an EntityMap hasn't been manually registered by the user
194
+		// register it at runtime.
195
+		if (!$this->isRegisteredEntity($entity)) {
196
+			$this->register($entity, $entityMap);
197
+		}
198
+
199
+		$entityMap = $this->entityClasses[$entity];
200
+
201
+		$factory = new MapperFactory($this->drivers, $this->eventDispatcher, $this);
202
+
203
+		$mapper = $factory->make($entity, $entityMap);
204
+
205
+		$this->mappers[$entity] = $mapper;
206
+
207
+		// At this point we can safely call the boot() method on the entityMap as
208
+		// the mapper is now instantiated & registered within the manager.
209
+
210
+		$mapper->getEntityMap()->boot();
211
+
212
+		return $mapper;
213
+	}
214
+
215
+	/**
216
+	 * Check if the entity is already registered
217
+	 *
218
+	 * @param  string|object $entity
219
+	 * @return boolean
220
+	 */
221
+	public function isRegisteredEntity($entity)
222
+	{
223
+		if (!is_string($entity)) {
224
+			$entity = get_class($entity);
225
+		}
226
+
227
+		return array_key_exists($entity, $this->entityClasses);
228
+	}
229
+
230
+	/**
231
+	 * Register an entity
232
+	 *
233
+	 * @param  string|\Analogue\ORM\Mappable $entity    entity's class name
234
+	 * @param  string|EntityMap              $entityMap map's class name
235
+	 * @throws MappingException
236
+	 * @return void
237
+	 */
238
+	public function register($entity, $entityMap = null)
239
+	{
240
+		// If an object is provider, get the class name from it
241
+		if (!is_string($entity)) {
242
+			$entity = get_class($entity);
243
+		}
244
+
245
+		if ($this->isRegisteredEntity($entity)) {
246
+			throw new MappingException("Entity $entity is already registered.");
247
+		}
248
+
249
+		if (!class_exists($entity)) {
250
+			throw new MappingException("Class $entity does not exists");
251
+		}
252
+
253
+		if (is_null($entityMap)) {
254
+			$entityMap = $this->getEntityMapInstanceFor($entity);
255
+		}
256
+
257
+		if (is_string($entityMap)) {
258
+			$entityMap = new $entityMap;
259
+		}
260
+
261
+		if (!$entityMap instanceof EntityMap) {
262
+			throw new MappingException(get_class($entityMap) . ' must be an instance of EntityMap.');
263
+		}
264
+
265
+		$entityMap->setClass($entity);
266
+
267
+		$entityMap->setManager($this);
268
+
269
+		$this->entityClasses[$entity] = $entityMap;
270
+	}
271
+
272
+	/**
273
+	 * Get the entity map instance for a custom entity
274
+	 *
275
+	 * @param  string $entity
276
+	 * @return \Analogue\ORM\Mappable
277
+	 */
278
+	protected function getEntityMapInstanceFor($entity)
279
+	{
280
+		if (class_exists($entity . 'Map')) {
281
+			$map = $entity . 'Map';
282
+			$map = new $map;
283
+		} else {
284
+			// Generate an EntityMap object
285
+			$map = $this->getNewEntityMap();
286
+		}
287
+
288
+		return $map;
289
+	}
290
+
291
+	/**
292
+	 * Dynamically create an entity map for a custom entity class
293
+	 *
294
+	 * @return EntityMap
295
+	 */
296
+	protected function getNewEntityMap()
297
+	{
298
+		return new EntityMap;
299
+	}
300
+
301
+	/**
302
+	 * Return the Singleton instance of the manager
303
+	 *
304
+	 * @return Manager
305
+	 */
306
+	public static function getInstance()
307
+	{
308
+		return static::$instance;
309
+	}
310
+
311
+	/**
312
+	 * Return the Driver Manager's instance
313
+	 *
314
+	 * @return \Analogue\ORM\Drivers\Manager
315
+	 */
316
+	public function getDriverManager()
317
+	{
318
+		return $this->drivers;
319
+	}
320
+
321
+	/**
322
+	 * Get the Repository instance for the given Entity
323
+	 *
324
+	 * @param  \Analogue\ORM\Mappable|string $entity
325
+	 * @throws \InvalidArgumentException
326
+	 * @throws MappingException
327
+	 * @return \Analogue\ORM\Repository
328
+	 */
329
+	public function repository($entity)
330
+	{
331
+		if (!is_string($entity)) {
332
+			$entity = get_class($entity);
333
+		}
334
+
335
+		// First we check if the repository is not already created.
336
+		if (array_key_exists($entity, $this->repositories)) {
337
+			return $this->repositories[$entity];
338
+		}
339
+
340
+		$this->repositories[$entity] = new Repository($this->mapper($entity));
341
+
342
+		return $this->repositories[$entity];
343
+	}
344
+
345
+	/**
346
+	 * Return true is the object is registered as value object
347
+	 *
348
+	 * @param  mixed $object
349
+	 * @return boolean
350
+	 */
351
+	public function isValueObject($object)
352
+	{
353
+		if (!is_string($object)) {
354
+			$object = get_class($object);
355
+		}
356
+
357
+		return array_key_exists($object, $this->valueClasses);
358
+	}
359
+
360
+	/**
361
+	 * Get the Value Map for a given Value Object Class
362
+	 *
363
+	 * @param  string $valueObject
364
+	 * @throws MappingException
365
+	 * @return \Analogue\ORM\ValueMap
366
+	 */
367
+	public function getValueMap($valueObject)
368
+	{
369
+		if (!is_string($valueObject)) {
370
+			$valueObject = get_class($valueObject);
371
+		}
372
+
373
+		if (!array_key_exists($valueObject, $this->valueClasses)) {
374
+			$this->registerValueObject($valueObject);
375
+		}
376
+		$valueMap = new $this->valueClasses[$valueObject];
377
+
378
+		$valueMap->setClass($valueObject);
379
+
380
+		return $valueMap;
381
+	}
382
+
383
+	/**
384
+	 * Register a Value Object
385
+	 *
386
+	 * @param  string $valueObject
387
+	 * @param  string $valueMap
388
+	 * @throws MappingException
389
+	 * @return void
390
+	 */
391
+	public function registerValueObject($valueObject, $valueMap = null)
392
+	{
393
+		if (!is_string($valueObject)) {
394
+			$valueObject = get_class($valueObject);
395
+		}
396
+
397
+		if (is_null($valueMap)) {
398
+			$valueMap = $valueObject . 'Map';
399
+		}
400
+
401
+		if (!class_exists($valueMap)) {
402
+			throw new MappingException("$valueMap doesn't exists");
403
+		}
404
+
405
+		$this->valueClasses[$valueObject] = $valueMap;
406
+	}
407
+
408
+	/**
409
+	 * Instantiate a new Value Object instance
410
+	 *
411
+	 * @param  string $valueObject
412
+	 * @return \Analogue\ORM\ValueObject
413
+	 */
414
+	public function getValueObjectInstance($valueObject)
415
+	{
416
+		$prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($valueObject), $valueObject));
417
+
418
+		return $prototype;
419
+	}
420
+
421
+	/**
422
+	 * Register Analogue Plugin
423
+	 *
424
+	 * @param  string $plugin class
425
+	 * @return void
426
+	 */
427
+	public function registerPlugin($plugin)
428
+	{
429
+		$plugin = new $plugin($this);
430
+
431
+		$this->events = array_merge($this->events, $plugin->getCustomEvents());
432
+
433
+		$plugin->register();
434
+	}
435
+
436
+	/**
437
+	 * Register event listeners that will be fired regardless the type
438
+	 * of the entity.
439
+	 *
440
+	 * @param  string   $event
441
+	 * @param  \Closure $callback
442
+	 * @throws \Exception
443
+	 * @return void
444
+	 */
445
+	public function registerGlobalEvent($event, $callback)
446
+	{
447
+		if (!in_array($event, $this->events)) {
448
+			throw new \Exception("Analogue : Event $event doesn't exist");
449
+		}
450
+		$this->eventDispatcher->listen("analogue.{$event}.*", $callback);
451
+	}
452
+
453
+	/**
454
+	 * Shortcut to Mapper store
455
+	 *
456
+	 * @param  mixed $entity
457
+	 * @throws MappingException
458
+	 * @return mixed
459
+	 */
460
+	public function store($entity)
461
+	{
462
+		return $this->mapper($entity)->store($entity);
463
+	}
464
+
465
+	/**
466
+	 * Shortcut to Mapper delete
467
+	 *
468
+	 * @param  mixed $entity
469
+	 * @throws MappingException
470
+	 * @return \Illuminate\Support\Collection|null
471
+	 */
472
+	public function delete($entity)
473
+	{
474
+		return $this->mapper($entity)->delete($entity);
475
+	}
476
+
477
+	/**
478
+	 * Shortcut to Mapper query
479
+	 *
480
+	 * @param  mixed $entity
481
+	 * @throws MappingException
482
+	 * @return Query
483
+	 */
484
+	public function query($entity)
485
+	{
486
+		return $this->mapper($entity)->query();
487
+	}
488
+
489
+	/**
490
+	 * Shortcut to Mapper Global Query
491
+	 *
492
+	 * @param  mixed $entity
493
+	 * @throws MappingException
494
+	 * @return Query
495
+	 */
496
+	public function globalQuery($entity)
497
+	{
498
+		return $this->mapper($entity)->globalQuery();
499
+	}
500
+
501
+	/**
502
+	 * @param array $morphMap
503
+	 * @return $this
504
+	 */
505
+	public function morphMap(array $morphMap)
506
+	{
507
+		$this->morphMap = $morphMap;
508
+
509
+		return $this;
510
+	}
511
+
512
+	/**
513
+	 * @param string $class
514
+	 * @return mixed
515
+	 */
516
+	public function getMorphMap($class)
517
+	{
518
+		$key = array_search($class, $this->morphMap);
519
+
520
+		return $key !== false ? $key : $class;
521
+	}
522 522
 }
Please login to merge, or discard this patch.
src/System/Support.php 1 patch
Indentation   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -7,14 +7,14 @@
 block discarded – undo
7 7
  */
8 8
 class Support
9 9
 {
10
-    /**
11
-     * Return true if an object is an array or iterator
12
-     *
13
-     * @param  mixed $argument
14
-     * @return boolean
15
-     */
16
-    public static function isTraversable($argument)
17
-    {
18
-        return $argument instanceof \Traversable || is_array($argument);
19
-    }
10
+	/**
11
+	 * Return true if an object is an array or iterator
12
+	 *
13
+	 * @param  mixed $argument
14
+	 * @return boolean
15
+	 */
16
+	public static function isTraversable($argument)
17
+	{
18
+		return $argument instanceof \Traversable || is_array($argument);
19
+	}
20 20
 }
Please login to merge, or discard this patch.
src/System/Mapper.php 1 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 (Support::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 (Support::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 (Support::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 (Support::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 (Support::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 (Support::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.