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