1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ODM\MongoDB\Mapping; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\EventManager; |
8
|
|
|
use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; |
9
|
|
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; |
10
|
|
|
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; |
11
|
|
|
use Doctrine\Common\Persistence\Mapping\ReflectionService; |
12
|
|
|
use Doctrine\ODM\MongoDB\Configuration; |
13
|
|
|
use Doctrine\ODM\MongoDB\ConfigurationException; |
14
|
|
|
use Doctrine\ODM\MongoDB\DocumentManager; |
15
|
|
|
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs; |
16
|
|
|
use Doctrine\ODM\MongoDB\Event\OnClassMetadataNotFoundEventArgs; |
17
|
|
|
use Doctrine\ODM\MongoDB\Events; |
18
|
|
|
use Doctrine\ODM\MongoDB\Id\AbstractIdGenerator; |
19
|
|
|
use Doctrine\ODM\MongoDB\Id\AlnumGenerator; |
20
|
|
|
use Doctrine\ODM\MongoDB\Id\AutoGenerator; |
21
|
|
|
use Doctrine\ODM\MongoDB\Id\IncrementGenerator; |
22
|
|
|
use Doctrine\ODM\MongoDB\Id\UuidGenerator; |
23
|
|
|
use ReflectionException; |
24
|
|
|
use const E_USER_DEPRECATED; |
25
|
|
|
use function assert; |
26
|
|
|
use function get_class; |
27
|
|
|
use function get_class_methods; |
28
|
|
|
use function in_array; |
29
|
|
|
use function sprintf; |
30
|
|
|
use function trigger_error; |
31
|
|
|
use function ucfirst; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the |
35
|
|
|
* metadata mapping informations of a class which describes how a class should be mapped |
36
|
|
|
* to a document database. |
37
|
|
|
* |
38
|
|
|
* @internal |
39
|
|
|
* |
40
|
|
|
* @final |
41
|
|
|
*/ |
42
|
|
|
class ClassMetadataFactory extends AbstractClassMetadataFactory |
43
|
|
|
{ |
44
|
|
|
/** @var string */ |
45
|
|
|
protected $cacheSalt = '$MONGODBODMCLASSMETADATA'; |
46
|
|
|
|
47
|
|
|
/** @var DocumentManager The DocumentManager instance */ |
48
|
|
|
private $dm; |
49
|
|
|
|
50
|
|
|
/** @var Configuration The Configuration instance */ |
51
|
|
|
private $config; |
52
|
|
|
|
53
|
|
|
/** @var MappingDriver The used metadata driver. */ |
54
|
|
|
private $driver; |
55
|
|
|
|
56
|
|
|
/** @var EventManager The event manager instance */ |
57
|
|
|
private $evm; |
58
|
|
|
|
59
|
1762 |
View Code Duplication |
public function __construct() |
|
|
|
|
60
|
|
|
{ |
61
|
1762 |
|
if (self::class === static::class) { |
62
|
1762 |
|
return; |
63
|
|
|
} |
64
|
|
|
|
65
|
1 |
|
@trigger_error(sprintf('The class "%s" extends "%s" which will be final in MongoDB ODM 2.0.', static::class, self::class), E_USER_DEPRECATED); |
|
|
|
|
66
|
1 |
|
} |
67
|
|
|
|
68
|
1762 |
|
public function setDocumentManager(DocumentManager $dm) : void |
69
|
|
|
{ |
70
|
1762 |
|
$this->dm = $dm; |
71
|
1762 |
|
} |
72
|
|
|
|
73
|
1762 |
|
public function setConfiguration(Configuration $config) : void |
74
|
|
|
{ |
75
|
1762 |
|
$this->config = $config; |
76
|
1762 |
|
} |
77
|
|
|
|
78
|
1489 |
|
public function getMetadataFor($className) |
79
|
|
|
{ |
80
|
1489 |
|
return parent::getMetadataFor($this->dm->getClassNameResolver()->getRealClass($className)); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Lazy initialization of this stuff, especially the metadata driver, |
85
|
|
|
* since these are not needed at all when a metadata cache is active. |
86
|
|
|
*/ |
87
|
1488 |
|
protected function initialize() : void |
88
|
|
|
{ |
89
|
1488 |
|
$driver = $this->config->getMetadataDriverImpl(); |
90
|
1488 |
|
if ($driver === null) { |
91
|
|
|
throw ConfigurationException::noMetadataDriverConfigured(); |
92
|
|
|
} |
93
|
|
|
|
94
|
1488 |
|
$this->driver = $driver; |
95
|
1488 |
|
$this->evm = $this->dm->getEventManager(); |
96
|
1488 |
|
$this->initialized = true; |
97
|
1488 |
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* {@inheritDoc} |
101
|
|
|
*/ |
102
|
16 |
|
protected function onNotFoundMetadata($className) |
103
|
|
|
{ |
104
|
16 |
|
if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) { |
105
|
13 |
|
return null; |
106
|
|
|
} |
107
|
|
|
|
108
|
3 |
|
$eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->dm); |
109
|
|
|
|
110
|
3 |
|
$this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); |
111
|
|
|
|
112
|
3 |
|
return $eventArgs->getFoundMetadata(); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* {@inheritDoc} |
117
|
|
|
*/ |
118
|
|
|
protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string |
119
|
|
|
{ |
120
|
|
|
return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* {@inheritDoc} |
125
|
|
|
*/ |
126
|
959 |
|
protected function getDriver() |
127
|
|
|
{ |
128
|
959 |
|
return $this->driver; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* {@inheritDoc} |
133
|
|
|
*/ |
134
|
1483 |
|
protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void |
135
|
|
|
{ |
136
|
1483 |
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* {@inheritDoc} |
140
|
|
|
*/ |
141
|
1488 |
|
protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void |
142
|
|
|
{ |
143
|
1488 |
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* {@inheritDoc} |
147
|
|
|
*/ |
148
|
1483 |
|
protected function isEntity(ClassMetadataInterface $class) : bool |
149
|
|
|
{ |
150
|
1483 |
|
assert($class instanceof ClassMetadata); |
151
|
1483 |
|
return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* {@inheritDoc} |
156
|
|
|
*/ |
157
|
1488 |
|
protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []) : void |
158
|
|
|
{ |
159
|
1488 |
|
assert($class instanceof ClassMetadata); |
160
|
1488 |
|
if ($parent instanceof ClassMetadata) { |
161
|
954 |
|
$class->setInheritanceType($parent->inheritanceType); |
162
|
954 |
|
$class->setDiscriminatorField($parent->discriminatorField); |
163
|
954 |
|
$class->setDiscriminatorMap($parent->discriminatorMap); |
164
|
954 |
|
$class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue); |
165
|
954 |
|
$class->setIdGeneratorType($parent->generatorType); |
166
|
954 |
|
$this->addInheritedFields($class, $parent); |
167
|
954 |
|
$this->addInheritedRelations($class, $parent); |
168
|
954 |
|
$this->addInheritedIndexes($class, $parent); |
169
|
954 |
|
$this->setInheritedShardKey($class, $parent); |
170
|
954 |
|
$class->setIdentifier($parent->identifier); |
171
|
954 |
|
$class->setVersioned($parent->isVersioned); |
172
|
954 |
|
$class->setVersionField($parent->versionField); |
173
|
954 |
|
$class->setLifecycleCallbacks($parent->lifecycleCallbacks); |
174
|
954 |
|
$class->setAlsoLoadMethods($parent->alsoLoadMethods); |
175
|
954 |
|
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy); |
176
|
954 |
|
$class->setReadPreference($parent->readPreference, $parent->readPreferenceTags); |
177
|
954 |
|
$class->setWriteConcern($parent->writeConcern); |
178
|
|
|
|
179
|
954 |
|
if ($parent->isMappedSuperclass) { |
180
|
890 |
|
$class->setCustomRepositoryClass($parent->customRepositoryClassName); |
181
|
|
|
} |
182
|
|
|
|
183
|
954 |
|
if ($parent->isFile) { |
184
|
1 |
|
$class->isFile = true; |
185
|
1 |
|
$class->setBucketName($parent->bucketName); |
186
|
|
|
|
187
|
1 |
|
if ($parent->chunkSizeBytes !== null) { |
188
|
1 |
|
$class->setChunkSizeBytes($parent->chunkSizeBytes); |
189
|
|
|
} |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
// Invoke driver |
194
|
|
|
try { |
195
|
1488 |
|
$this->driver->loadMetadataForClass($class->getName(), $class); |
|
|
|
|
196
|
8 |
|
} catch (ReflectionException $e) { |
197
|
|
|
throw MappingException::reflectionFailure($class->getName(), $e); |
|
|
|
|
198
|
|
|
} |
199
|
|
|
|
200
|
1483 |
|
$this->validateIdentifier($class); |
201
|
|
|
|
202
|
1483 |
|
if ($parent instanceof ClassMetadata && $rootEntityFound && $parent->generatorType === $class->generatorType) { |
203
|
164 |
|
if ($parent->generatorType) { |
204
|
164 |
|
$class->setIdGeneratorType($parent->generatorType); |
205
|
|
|
} |
206
|
164 |
|
if ($parent->generatorOptions) { |
|
|
|
|
207
|
|
|
$class->setIdGeneratorOptions($parent->generatorOptions); |
208
|
|
|
} |
209
|
164 |
|
if ($parent->idGenerator) { |
210
|
164 |
|
$class->setIdGenerator($parent->idGenerator); |
211
|
|
|
} |
212
|
|
|
} else { |
213
|
1483 |
|
$this->completeIdGeneratorMapping($class); |
214
|
|
|
} |
215
|
|
|
|
216
|
1483 |
|
if ($parent instanceof ClassMetadata && $parent->isInheritanceTypeSingleCollection()) { |
217
|
150 |
|
$class->setDatabase($parent->getDatabase()); |
218
|
150 |
|
$class->setCollection($parent->getCollection()); |
219
|
|
|
} |
220
|
|
|
|
221
|
1483 |
|
$class->setParentClasses($nonSuperclassParents); |
222
|
|
|
|
223
|
1483 |
|
if (! $this->evm->hasListeners(Events::loadClassMetadata)) { |
224
|
1479 |
|
return; |
225
|
|
|
} |
226
|
|
|
|
227
|
4 |
|
$eventArgs = new LoadClassMetadataEventArgs($class, $this->dm); |
228
|
4 |
|
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); |
229
|
4 |
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Validates the identifier mapping. |
233
|
|
|
* |
234
|
|
|
* @throws MappingException |
235
|
|
|
*/ |
236
|
1483 |
|
protected function validateIdentifier(ClassMetadata $class) : void |
237
|
|
|
{ |
238
|
1483 |
|
if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) { |
|
|
|
|
239
|
|
|
throw MappingException::identifierRequired($class->name); |
240
|
|
|
} |
241
|
1483 |
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* {@inheritdoc} |
245
|
|
|
*/ |
246
|
1488 |
|
protected function newClassMetadataInstance($className) : ClassMetadata |
247
|
|
|
{ |
248
|
1488 |
|
return new ClassMetadata($className); |
249
|
|
|
} |
250
|
|
|
|
251
|
1483 |
|
private function completeIdGeneratorMapping(ClassMetadata $class) : void |
252
|
|
|
{ |
253
|
1483 |
|
$idGenOptions = $class->generatorOptions; |
254
|
1483 |
|
switch ($class->generatorType) { |
255
|
|
|
case ClassMetadata::GENERATOR_TYPE_AUTO: |
256
|
1415 |
|
$class->setIdGenerator(new AutoGenerator()); |
257
|
1415 |
|
break; |
258
|
|
View Code Duplication |
case ClassMetadata::GENERATOR_TYPE_INCREMENT: |
|
|
|
|
259
|
9 |
|
$incrementGenerator = new IncrementGenerator(); |
260
|
9 |
|
if (isset($idGenOptions['key'])) { |
261
|
|
|
$incrementGenerator->setKey((string) $idGenOptions['key']); |
262
|
|
|
} |
263
|
9 |
|
if (isset($idGenOptions['collection'])) { |
264
|
|
|
$incrementGenerator->setCollection((string) $idGenOptions['collection']); |
265
|
|
|
} |
266
|
9 |
|
if (isset($idGenOptions['startingId'])) { |
267
|
1 |
|
$incrementGenerator->setStartingId((int) $idGenOptions['startingId']); |
268
|
|
|
} |
269
|
9 |
|
$class->setIdGenerator($incrementGenerator); |
270
|
9 |
|
break; |
271
|
|
|
case ClassMetadata::GENERATOR_TYPE_UUID: |
272
|
4 |
|
$uuidGenerator = new UuidGenerator(); |
273
|
4 |
|
if (isset($idGenOptions['salt'])) { |
274
|
2 |
|
$uuidGenerator->setSalt((string) $idGenOptions['salt']); |
275
|
|
|
} |
276
|
4 |
|
$class->setIdGenerator($uuidGenerator); |
277
|
4 |
|
break; |
278
|
|
View Code Duplication |
case ClassMetadata::GENERATOR_TYPE_ALNUM: |
|
|
|
|
279
|
1 |
|
$alnumGenerator = new AlnumGenerator(); |
280
|
1 |
|
if (isset($idGenOptions['pad'])) { |
281
|
|
|
$alnumGenerator->setPad((int) $idGenOptions['pad']); |
282
|
|
|
} |
283
|
1 |
|
if (isset($idGenOptions['chars'])) { |
284
|
1 |
|
$alnumGenerator->setChars((string) $idGenOptions['chars']); |
285
|
|
|
} elseif (isset($idGenOptions['awkwardSafe'])) { |
286
|
|
|
$alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']); |
287
|
|
|
} |
288
|
|
|
|
289
|
1 |
|
$class->setIdGenerator($alnumGenerator); |
290
|
1 |
|
break; |
291
|
|
|
case ClassMetadata::GENERATOR_TYPE_CUSTOM: |
292
|
|
|
if (empty($idGenOptions['class'])) { |
293
|
|
|
throw MappingException::missingIdGeneratorClass($class->name); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
$customGenerator = new $idGenOptions['class'](); |
297
|
|
|
unset($idGenOptions['class']); |
298
|
|
|
if (! $customGenerator instanceof AbstractIdGenerator) { |
299
|
|
|
throw MappingException::classIsNotAValidGenerator(get_class($customGenerator)); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
$methods = get_class_methods($customGenerator); |
303
|
|
|
foreach ($idGenOptions as $name => $value) { |
304
|
|
|
$method = 'set' . ucfirst($name); |
305
|
|
|
if (! in_array($method, $methods)) { |
306
|
|
|
throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
$customGenerator->$method($value); |
310
|
|
|
} |
311
|
|
|
$class->setIdGenerator($customGenerator); |
312
|
|
|
break; |
313
|
|
|
case ClassMetadata::GENERATOR_TYPE_NONE: |
314
|
193 |
|
break; |
315
|
|
|
default: |
316
|
|
|
throw new MappingException('Unknown generator type: ' . $class->generatorType); |
317
|
|
|
} |
318
|
1483 |
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Adds inherited fields to the subclass mapping. |
322
|
|
|
*/ |
323
|
954 |
|
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
324
|
|
|
{ |
325
|
954 |
|
foreach ($parentClass->fieldMappings as $fieldName => $mapping) { |
326
|
182 |
View Code Duplication |
if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { |
|
|
|
|
327
|
176 |
|
$mapping['inherited'] = $parentClass->name; |
328
|
|
|
} |
329
|
182 |
|
if (! isset($mapping['declared'])) { |
330
|
182 |
|
$mapping['declared'] = $parentClass->name; |
331
|
|
|
} |
332
|
182 |
|
$subClass->addInheritedFieldMapping($mapping); |
333
|
|
|
} |
334
|
954 |
|
foreach ($parentClass->reflFields as $name => $field) { |
335
|
182 |
|
$subClass->reflFields[$name] = $field; |
336
|
|
|
} |
337
|
954 |
|
} |
338
|
|
|
|
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Adds inherited association mappings to the subclass mapping. |
342
|
|
|
* |
343
|
|
|
* @throws MappingException |
344
|
|
|
*/ |
345
|
954 |
|
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
346
|
|
|
{ |
347
|
954 |
|
foreach ($parentClass->associationMappings as $field => $mapping) { |
348
|
132 |
|
if ($parentClass->isMappedSuperclass) { |
349
|
4 |
|
$mapping['sourceDocument'] = $subClass->name; |
350
|
|
|
} |
351
|
|
|
|
352
|
132 |
View Code Duplication |
if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { |
|
|
|
|
353
|
128 |
|
$mapping['inherited'] = $parentClass->name; |
354
|
|
|
} |
355
|
132 |
|
if (! isset($mapping['declared'])) { |
356
|
132 |
|
$mapping['declared'] = $parentClass->name; |
357
|
|
|
} |
358
|
132 |
|
$subClass->addInheritedAssociationMapping($mapping); |
359
|
|
|
} |
360
|
954 |
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Adds inherited indexes to the subclass mapping. |
364
|
|
|
*/ |
365
|
954 |
|
private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
366
|
|
|
{ |
367
|
954 |
|
foreach ($parentClass->indexes as $index) { |
368
|
108 |
|
$subClass->addIndex($index['keys'], $index['options']); |
369
|
|
|
} |
370
|
954 |
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Adds inherited shard key to the subclass mapping. |
374
|
|
|
*/ |
375
|
954 |
|
private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
376
|
|
|
{ |
377
|
954 |
|
if (! $parentClass->isSharded()) { |
378
|
949 |
|
return; |
379
|
|
|
} |
380
|
|
|
|
381
|
5 |
|
$subClass->setShardKey( |
382
|
5 |
|
$parentClass->shardKey['keys'], |
383
|
5 |
|
$parentClass->shardKey['options'] |
384
|
|
|
); |
385
|
5 |
|
} |
386
|
|
|
} |
387
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.