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\DocumentManager; |
14
|
|
|
use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs; |
15
|
|
|
use Doctrine\ODM\MongoDB\Events; |
16
|
|
|
use Doctrine\ODM\MongoDB\Id\AbstractIdGenerator; |
17
|
|
|
use Doctrine\ODM\MongoDB\Id\AlnumGenerator; |
18
|
|
|
use Doctrine\ODM\MongoDB\Id\AutoGenerator; |
19
|
|
|
use Doctrine\ODM\MongoDB\Id\IncrementGenerator; |
20
|
|
|
use Doctrine\ODM\MongoDB\Id\UuidGenerator; |
21
|
|
|
use ReflectionException; |
22
|
|
|
use function get_class; |
23
|
|
|
use function get_class_methods; |
24
|
|
|
use function in_array; |
25
|
|
|
use function ucfirst; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the |
29
|
|
|
* metadata mapping informations of a class which describes how a class should be mapped |
30
|
|
|
* to a document database. |
31
|
|
|
*/ |
32
|
|
|
class ClassMetadataFactory extends AbstractClassMetadataFactory |
33
|
|
|
{ |
34
|
|
|
/** @var string */ |
35
|
|
|
protected $cacheSalt = '$MONGODBODMCLASSMETADATA'; |
36
|
|
|
|
37
|
|
|
/** @var DocumentManager The DocumentManager instance */ |
38
|
|
|
private $dm; |
39
|
|
|
|
40
|
|
|
/** @var Configuration The Configuration instance */ |
41
|
|
|
private $config; |
42
|
|
|
|
43
|
|
|
/** @var MappingDriver The used metadata driver. */ |
44
|
|
|
private $driver; |
45
|
|
|
|
46
|
|
|
/** @var EventManager The event manager instance */ |
47
|
|
|
private $evm; |
48
|
|
|
|
49
|
1698 |
|
public function setDocumentManager(DocumentManager $dm) : void |
50
|
|
|
{ |
51
|
1698 |
|
$this->dm = $dm; |
52
|
1698 |
|
} |
53
|
|
|
|
54
|
1698 |
|
public function setConfiguration(Configuration $config) : void |
55
|
|
|
{ |
56
|
1698 |
|
$this->config = $config; |
57
|
1698 |
|
} |
58
|
|
|
|
59
|
1434 |
|
public function getMetadataFor($className) |
60
|
|
|
{ |
61
|
1434 |
|
return parent::getMetadataFor($this->dm->getClassNameResolver()->getRealClass($className)); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Lazy initialization of this stuff, especially the metadata driver, |
66
|
|
|
* since these are not needed at all when a metadata cache is active. |
67
|
|
|
*/ |
68
|
1433 |
|
protected function initialize() : void |
69
|
|
|
{ |
70
|
1433 |
|
$this->driver = $this->config->getMetadataDriverImpl(); |
71
|
1433 |
|
$this->evm = $this->dm->getEventManager(); |
72
|
1433 |
|
$this->initialized = true; |
73
|
1433 |
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* {@inheritDoc} |
77
|
|
|
*/ |
78
|
|
|
protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string |
79
|
|
|
{ |
80
|
|
|
return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* {@inheritDoc} |
85
|
|
|
*/ |
86
|
918 |
|
protected function getDriver() |
87
|
|
|
{ |
88
|
918 |
|
return $this->driver; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* {@inheritDoc} |
93
|
|
|
*/ |
94
|
1429 |
|
protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void |
95
|
|
|
{ |
96
|
1429 |
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* {@inheritDoc} |
100
|
|
|
*/ |
101
|
1433 |
|
protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void |
102
|
|
|
{ |
103
|
1433 |
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* {@inheritDoc} |
107
|
|
|
*/ |
108
|
1429 |
|
protected function isEntity(ClassMetadataInterface $class) : bool |
109
|
|
|
{ |
110
|
1429 |
|
return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* {@inheritDoc} |
115
|
|
|
*/ |
116
|
1433 |
|
protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []) : void |
117
|
|
|
{ |
118
|
|
|
/** @var $class ClassMetadata */ |
119
|
|
|
/** @var $parent ClassMetadata */ |
120
|
1433 |
|
if ($parent) { |
121
|
916 |
|
$class->setInheritanceType($parent->inheritanceType); |
122
|
916 |
|
$class->setDiscriminatorField($parent->discriminatorField); |
123
|
916 |
|
$class->setDiscriminatorMap($parent->discriminatorMap); |
124
|
916 |
|
$class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue); |
125
|
916 |
|
$class->setIdGeneratorType($parent->generatorType); |
126
|
916 |
|
$this->addInheritedFields($class, $parent); |
127
|
916 |
|
$this->addInheritedRelations($class, $parent); |
128
|
916 |
|
$this->addInheritedIndexes($class, $parent); |
129
|
916 |
|
$this->setInheritedShardKey($class, $parent); |
130
|
916 |
|
$class->setIdentifier($parent->identifier); |
131
|
916 |
|
$class->setVersioned($parent->isVersioned); |
132
|
916 |
|
$class->setVersionField($parent->versionField); |
133
|
916 |
|
$class->setLifecycleCallbacks($parent->lifecycleCallbacks); |
134
|
916 |
|
$class->setAlsoLoadMethods($parent->alsoLoadMethods); |
135
|
916 |
|
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy); |
136
|
916 |
|
$class->setReadPreference($parent->readPreference, $parent->readPreferenceTags); |
137
|
916 |
|
$class->setWriteConcern($parent->writeConcern); |
138
|
|
|
|
139
|
916 |
|
if ($parent->isMappedSuperclass) { |
140
|
852 |
|
$class->setCustomRepositoryClass($parent->customRepositoryClassName); |
141
|
|
|
} |
142
|
|
|
|
143
|
916 |
|
if ($parent->isFile) { |
144
|
1 |
|
$class->isFile = true; |
145
|
1 |
|
$class->setBucketName($parent->bucketName); |
146
|
|
|
|
147
|
1 |
|
if ($parent->chunkSizeBytes !== null) { |
148
|
1 |
|
$class->setChunkSizeBytes($parent->chunkSizeBytes); |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
// Invoke driver |
154
|
|
|
try { |
155
|
1433 |
|
$this->driver->loadMetadataForClass($class->getName(), $class); |
156
|
6 |
|
} catch (ReflectionException $e) { |
157
|
|
|
throw MappingException::reflectionFailure($class->getName(), $e); |
158
|
|
|
} |
159
|
|
|
|
160
|
1429 |
|
$this->validateIdentifier($class); |
161
|
|
|
|
162
|
1429 |
|
if ($parent && $rootEntityFound && $parent->generatorType === $class->generatorType) { |
163
|
121 |
|
if ($parent->generatorType) { |
164
|
121 |
|
$class->setIdGeneratorType($parent->generatorType); |
165
|
|
|
} |
166
|
121 |
|
if ($parent->generatorOptions) { |
|
|
|
|
167
|
|
|
$class->setIdGeneratorOptions($parent->generatorOptions); |
168
|
|
|
} |
169
|
121 |
|
if ($parent->idGenerator) { |
170
|
121 |
|
$class->setIdGenerator($parent->idGenerator); |
171
|
|
|
} |
172
|
|
|
} else { |
173
|
1429 |
|
$this->completeIdGeneratorMapping($class); |
174
|
|
|
} |
175
|
|
|
|
176
|
1429 |
|
if ($parent && $parent->isInheritanceTypeSingleCollection()) { |
177
|
107 |
|
$class->setDatabase($parent->getDatabase()); |
178
|
107 |
|
$class->setCollection($parent->getCollection()); |
179
|
|
|
} |
180
|
|
|
|
181
|
1429 |
|
$class->setParentClasses($nonSuperclassParents); |
182
|
|
|
|
183
|
1429 |
|
if (! $this->evm->hasListeners(Events::loadClassMetadata)) { |
184
|
1427 |
|
return; |
185
|
|
|
} |
186
|
|
|
|
187
|
2 |
|
$eventArgs = new LoadClassMetadataEventArgs($class, $this->dm); |
188
|
2 |
|
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); |
189
|
2 |
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Validates the identifier mapping. |
193
|
|
|
* |
194
|
|
|
* @throws MappingException |
195
|
|
|
*/ |
196
|
1429 |
|
protected function validateIdentifier(ClassMetadata $class) : void |
197
|
|
|
{ |
198
|
1429 |
|
if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) { |
|
|
|
|
199
|
|
|
throw MappingException::identifierRequired($class->name); |
200
|
|
|
} |
201
|
1429 |
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* {@inheritdoc} |
205
|
|
|
*/ |
206
|
1433 |
|
protected function newClassMetadataInstance($className) : ClassMetadata |
207
|
|
|
{ |
208
|
1433 |
|
return new ClassMetadata($className); |
209
|
|
|
} |
210
|
|
|
|
211
|
1429 |
|
private function completeIdGeneratorMapping(ClassMetadata $class) : void |
212
|
|
|
{ |
213
|
1429 |
|
$idGenOptions = $class->generatorOptions; |
214
|
1429 |
|
switch ($class->generatorType) { |
215
|
|
|
case ClassMetadata::GENERATOR_TYPE_AUTO: |
216
|
1361 |
|
$class->setIdGenerator(new AutoGenerator()); |
217
|
1361 |
|
break; |
218
|
|
View Code Duplication |
case ClassMetadata::GENERATOR_TYPE_INCREMENT: |
|
|
|
|
219
|
9 |
|
$incrementGenerator = new IncrementGenerator(); |
220
|
9 |
|
if (isset($idGenOptions['key'])) { |
221
|
|
|
$incrementGenerator->setKey((string) $idGenOptions['key']); |
222
|
|
|
} |
223
|
9 |
|
if (isset($idGenOptions['collection'])) { |
224
|
|
|
$incrementGenerator->setCollection((string) $idGenOptions['collection']); |
225
|
|
|
} |
226
|
9 |
|
if (isset($idGenOptions['startingId'])) { |
227
|
1 |
|
$incrementGenerator->setStartingId((int) $idGenOptions['startingId']); |
228
|
|
|
} |
229
|
9 |
|
$class->setIdGenerator($incrementGenerator); |
230
|
9 |
|
break; |
231
|
|
|
case ClassMetadata::GENERATOR_TYPE_UUID: |
232
|
4 |
|
$uuidGenerator = new UuidGenerator(); |
233
|
4 |
|
isset($idGenOptions['salt']) && $uuidGenerator->setSalt((string) $idGenOptions['salt']); |
234
|
4 |
|
$class->setIdGenerator($uuidGenerator); |
235
|
4 |
|
break; |
236
|
|
View Code Duplication |
case ClassMetadata::GENERATOR_TYPE_ALNUM: |
|
|
|
|
237
|
1 |
|
$alnumGenerator = new AlnumGenerator(); |
238
|
1 |
|
if (isset($idGenOptions['pad'])) { |
239
|
|
|
$alnumGenerator->setPad((int) $idGenOptions['pad']); |
240
|
|
|
} |
241
|
1 |
|
if (isset($idGenOptions['chars'])) { |
242
|
1 |
|
$alnumGenerator->setChars((string) $idGenOptions['chars']); |
243
|
|
|
} elseif (isset($idGenOptions['awkwardSafe'])) { |
244
|
|
|
$alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']); |
245
|
|
|
} |
246
|
|
|
|
247
|
1 |
|
$class->setIdGenerator($alnumGenerator); |
248
|
1 |
|
break; |
249
|
|
|
case ClassMetadata::GENERATOR_TYPE_CUSTOM: |
250
|
|
|
if (empty($idGenOptions['class'])) { |
251
|
|
|
throw MappingException::missingIdGeneratorClass($class->name); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
$customGenerator = new $idGenOptions['class'](); |
255
|
|
|
unset($idGenOptions['class']); |
256
|
|
|
if (! $customGenerator instanceof AbstractIdGenerator) { |
257
|
|
|
throw MappingException::classIsNotAValidGenerator(get_class($customGenerator)); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
$methods = get_class_methods($customGenerator); |
261
|
|
|
foreach ($idGenOptions as $name => $value) { |
262
|
|
|
$method = 'set' . ucfirst($name); |
263
|
|
|
if (! in_array($method, $methods)) { |
264
|
|
|
throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
$customGenerator->$method($value); |
268
|
|
|
} |
269
|
|
|
$class->setIdGenerator($customGenerator); |
270
|
|
|
break; |
271
|
|
|
case ClassMetadata::GENERATOR_TYPE_NONE: |
272
|
150 |
|
break; |
273
|
|
|
default: |
274
|
|
|
throw new MappingException('Unknown generator type: ' . $class->generatorType); |
275
|
|
|
} |
276
|
1429 |
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Adds inherited fields to the subclass mapping. |
280
|
|
|
*/ |
281
|
916 |
|
private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
282
|
|
|
{ |
283
|
916 |
|
foreach ($parentClass->fieldMappings as $fieldName => $mapping) { |
284
|
139 |
View Code Duplication |
if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { |
|
|
|
|
285
|
133 |
|
$mapping['inherited'] = $parentClass->name; |
286
|
|
|
} |
287
|
139 |
|
if (! isset($mapping['declared'])) { |
288
|
139 |
|
$mapping['declared'] = $parentClass->name; |
289
|
|
|
} |
290
|
139 |
|
$subClass->addInheritedFieldMapping($mapping); |
291
|
|
|
} |
292
|
916 |
|
foreach ($parentClass->reflFields as $name => $field) { |
293
|
139 |
|
$subClass->reflFields[$name] = $field; |
294
|
|
|
} |
295
|
916 |
|
} |
296
|
|
|
|
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* Adds inherited association mappings to the subclass mapping. |
300
|
|
|
* |
301
|
|
|
* @throws MappingException |
302
|
|
|
*/ |
303
|
916 |
|
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
304
|
|
|
{ |
305
|
916 |
|
foreach ($parentClass->associationMappings as $field => $mapping) { |
306
|
89 |
|
if ($parentClass->isMappedSuperclass) { |
307
|
4 |
|
$mapping['sourceDocument'] = $subClass->name; |
308
|
|
|
} |
309
|
|
|
|
310
|
89 |
View Code Duplication |
if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { |
|
|
|
|
311
|
85 |
|
$mapping['inherited'] = $parentClass->name; |
312
|
|
|
} |
313
|
89 |
|
if (! isset($mapping['declared'])) { |
314
|
89 |
|
$mapping['declared'] = $parentClass->name; |
315
|
|
|
} |
316
|
89 |
|
$subClass->addInheritedAssociationMapping($mapping); |
317
|
|
|
} |
318
|
916 |
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Adds inherited indexes to the subclass mapping. |
322
|
|
|
*/ |
323
|
916 |
|
private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
324
|
|
|
{ |
325
|
916 |
|
foreach ($parentClass->indexes as $index) { |
326
|
65 |
|
$subClass->addIndex($index['keys'], $index['options']); |
327
|
|
|
} |
328
|
916 |
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Adds inherited shard key to the subclass mapping. |
332
|
|
|
*/ |
333
|
916 |
|
private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
334
|
|
|
{ |
335
|
916 |
|
if (! $parentClass->isSharded()) { |
336
|
911 |
|
return; |
337
|
|
|
} |
338
|
|
|
|
339
|
5 |
|
$subClass->setShardKey( |
340
|
5 |
|
$parentClass->shardKey['keys'], |
341
|
5 |
|
$parentClass->shardKey['options'] |
342
|
|
|
); |
343
|
5 |
|
} |
344
|
|
|
} |
345
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.