Completed
Pull Request — master (#1757)
by Maciej
15:59
created

ClassMetadataFactory::initialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
crap 1
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 function get_class;
22
use function get_class_methods;
23
use function in_array;
24
use function ucfirst;
25
26
/**
27
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
28
 * metadata mapping informations of a class which describes how a class should be mapped
29
 * to a document database.
30
 *
31
 */
32
class ClassMetadataFactory extends AbstractClassMetadataFactory
33
{
34
    protected $cacheSalt = '$MONGODBODMCLASSMETADATA';
35
36
    /** @var DocumentManager The DocumentManager instance */
37
    private $dm;
38
39
    /** @var Configuration The Configuration instance */
40
    private $config;
41
42
    /** @var MappingDriver The used metadata driver. */
43
    private $driver;
44
45
    /** @var EventManager The event manager instance */
46
    private $evm;
47
48
    /**
49
     * Sets the DocumentManager instance for this class.
50
     *
51
     * @param DocumentManager $dm The DocumentManager instance
52
     */
53 1619
    public function setDocumentManager(DocumentManager $dm)
54
    {
55 1619
        $this->dm = $dm;
56 1619
    }
57
58
    /**
59
     * Sets the Configuration instance
60
     *
61
     */
62 1619
    public function setConfiguration(Configuration $config)
63
    {
64 1619
        $this->config = $config;
65 1619
    }
66
67
    /**
68
     * Lazy initialization of this stuff, especially the metadata driver,
69
     * since these are not needed at all when a metadata cache is active.
70
     */
71 1364
    protected function initialize()
72
    {
73 1364
        $this->driver = $this->config->getMetadataDriverImpl();
74 1364
        $this->evm = $this->dm->getEventManager();
75 1364
        $this->initialized = true;
76 1364
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
82
    {
83
        return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
84
    }
85
86
    /**
87
     * {@inheritDoc}
88
     */
89 887
    protected function getDriver()
90
    {
91 887
        return $this->driver;
92
    }
93
94
    /**
95
     * {@inheritDoc}
96
     */
97 1360
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
98
    {
99 1360
    }
100
101
    /**
102
     * {@inheritDoc}
103
     */
104 1364
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
105
    {
106 1364
    }
107
108
    /**
109
     * {@inheritDoc}
110
     */
111 1360
    protected function isEntity(ClassMetadataInterface $class)
112
    {
113 1360
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument;
114
    }
115
116
    /**
117
     * {@inheritDoc}
118
     */
119 1364
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = [])
120
    {
121
        /** @var $class ClassMetadata */
122
        /** @var $parent ClassMetadata */
123 1364
        if ($parent) {
124 885
            $class->setInheritanceType($parent->inheritanceType);
125 885
            $class->setDiscriminatorField($parent->discriminatorField);
126 885
            $class->setDiscriminatorMap($parent->discriminatorMap);
127 885
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
128 885
            $class->setIdGeneratorType($parent->generatorType);
129 885
            $this->addInheritedFields($class, $parent);
130 885
            $this->addInheritedRelations($class, $parent);
131 885
            $this->addInheritedIndexes($class, $parent);
132 885
            $this->setInheritedShardKey($class, $parent);
133 885
            $class->setIdentifier($parent->identifier);
134 885
            $class->setVersioned($parent->isVersioned);
135 885
            $class->setVersionField($parent->versionField);
136 885
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
137 885
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
138 885
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
139 885
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
140 885
            $class->setWriteConcern($parent->writeConcern);
141 885
            if ($parent->isMappedSuperclass) {
142 822
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
143
            }
144
        }
145
146
        // Invoke driver
147
        try {
148 1364
            $this->driver->loadMetadataForClass($class->getName(), $class);
149 6
        } catch (\ReflectionException $e) {
150
            throw MappingException::reflectionFailure($class->getName(), $e);
151
        }
152
153 1360
        $this->validateIdentifier($class);
154
155 1360
        if ($parent && $rootEntityFound && $parent->generatorType === $class->generatorType) {
156 98
            if ($parent->generatorType) {
157 98
                $class->setIdGeneratorType($parent->generatorType);
158
            }
159 98
            if ($parent->generatorOptions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent->generatorOptions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
160
                $class->setIdGeneratorOptions($parent->generatorOptions);
161
            }
162 98
            if ($parent->idGenerator) {
163 98
                $class->setIdGenerator($parent->idGenerator);
164
            }
165
        } else {
166 1360
            $this->completeIdGeneratorMapping($class);
167
        }
168
169 1360
        if ($parent && $parent->isInheritanceTypeSingleCollection()) {
170 87
            $class->setDatabase($parent->getDatabase());
171 87
            $class->setCollection($parent->getCollection());
172
        }
173
174 1360
        $class->setParentClasses($nonSuperclassParents);
175
176 1360
        if (! $this->evm->hasListeners(Events::loadClassMetadata)) {
177 1358
            return;
178
        }
179
180 2
        $eventArgs = new LoadClassMetadataEventArgs($class, $this->dm);
181 2
        $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
182 2
    }
183
184
    /**
185
     * Validates the identifier mapping.
186
     *
187
     * @param ClassMetadata $class
188
     * @throws MappingException
189
     */
190 1360
    protected function validateIdentifier($class)
191
    {
192 1360
        if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) {
193
            throw MappingException::identifierRequired($class->name);
194
        }
195 1360
    }
196
197
    /**
198
     * Creates a new ClassMetadata instance for the given class name.
199
     *
200
     * @param string $className
201
     * @return ClassMetadata
202
     */
203 1364
    protected function newClassMetadataInstance($className)
204
    {
205 1364
        return new ClassMetadata($className);
206
    }
207
208 1360
    private function completeIdGeneratorMapping(ClassMetadata $class)
209
    {
210 1360
        $idGenOptions = $class->generatorOptions;
211 1360
        switch ($class->generatorType) {
212
            case ClassMetadata::GENERATOR_TYPE_AUTO:
213 1292
                $class->setIdGenerator(new AutoGenerator());
214 1292
                break;
215 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_INCREMENT:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
216 9
                $incrementGenerator = new IncrementGenerator();
217 9
                if (isset($idGenOptions['key'])) {
218
                    $incrementGenerator->setKey($idGenOptions['key']);
219
                }
220 9
                if (isset($idGenOptions['collection'])) {
221
                    $incrementGenerator->setCollection($idGenOptions['collection']);
222
                }
223 9
                if (isset($idGenOptions['startingId'])) {
224 1
                    $incrementGenerator->setStartingId((int) $idGenOptions['startingId']);
225
                }
226 9
                $class->setIdGenerator($incrementGenerator);
227 9
                break;
228
            case ClassMetadata::GENERATOR_TYPE_UUID:
229 4
                $uuidGenerator = new UuidGenerator();
230 4
                isset($idGenOptions['salt']) && $uuidGenerator->setSalt($idGenOptions['salt']);
231 4
                $class->setIdGenerator($uuidGenerator);
232 4
                break;
233 View Code Duplication
            case ClassMetadata::GENERATOR_TYPE_ALNUM:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
234 1
                $alnumGenerator = new AlnumGenerator();
235 1
                if (isset($idGenOptions['pad'])) {
236
                    $alnumGenerator->setPad($idGenOptions['pad']);
237
                }
238 1
                if (isset($idGenOptions['chars'])) {
239 1
                    $alnumGenerator->setChars($idGenOptions['chars']);
240
                } elseif (isset($idGenOptions['awkwardSafe'])) {
241
                    $alnumGenerator->setAwkwardSafeMode($idGenOptions['awkwardSafe']);
242
                }
243
244 1
                $class->setIdGenerator($alnumGenerator);
245 1
                break;
246
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
247
                if (empty($idGenOptions['class'])) {
248
                    throw MappingException::missingIdGeneratorClass($class->name);
249
                }
250
251
                $customGenerator = new $idGenOptions['class']();
252
                unset($idGenOptions['class']);
253
                if (! $customGenerator instanceof AbstractIdGenerator) {
254
                    throw MappingException::classIsNotAValidGenerator(get_class($customGenerator));
255
                }
256
257
                $methods = get_class_methods($customGenerator);
258
                foreach ($idGenOptions as $name => $value) {
259
                    $method = 'set' . ucfirst($name);
260
                    if (! in_array($method, $methods)) {
261
                        throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name);
262
                    }
263
264
                    $customGenerator->$method($value);
265
                }
266
                $class->setIdGenerator($customGenerator);
267
                break;
268
            case ClassMetadata::GENERATOR_TYPE_NONE;
269 130
                break;
270
            default:
271
                throw new MappingException('Unknown generator type: ' . $class->generatorType);
272
        }
273 1360
    }
274
275
    /**
276
     * Adds inherited fields to the subclass mapping.
277
     *
278
     */
279 885
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
280
    {
281 885
        foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
282 116 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
283 110
                $mapping['inherited'] = $parentClass->name;
284
            }
285 116
            if (! isset($mapping['declared'])) {
286 116
                $mapping['declared'] = $parentClass->name;
287
            }
288 116
            $subClass->addInheritedFieldMapping($mapping);
289
        }
290 885
        foreach ($parentClass->reflFields as $name => $field) {
291 116
            $subClass->reflFields[$name] = $field;
292
        }
293 885
    }
294
295
296
    /**
297
     * Adds inherited association mappings to the subclass mapping.
298
     *
299
     *
300
     *
301
     * @throws MappingException
302
     */
303 885
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
304
    {
305 885
        foreach ($parentClass->associationMappings as $field => $mapping) {
306 67
            if ($parentClass->isMappedSuperclass) {
307 4
                $mapping['sourceDocument'] = $subClass->name;
308
            }
309
310 67 View Code Duplication
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
311 63
                $mapping['inherited'] = $parentClass->name;
312
            }
313 67
            if (! isset($mapping['declared'])) {
314 67
                $mapping['declared'] = $parentClass->name;
315
            }
316 67
            $subClass->addInheritedAssociationMapping($mapping);
317
        }
318 885
    }
319
320
    /**
321
     * Adds inherited indexes to the subclass mapping.
322
     *
323
     */
324 885
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
325
    {
326 885
        foreach ($parentClass->indexes as $index) {
327 45
            $subClass->addIndex($index['keys'], $index['options']);
328
        }
329 885
    }
330
331
    /**
332
     * Adds inherited shard key to the subclass mapping.
333
     *
334
     */
335 885
    private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass)
336
    {
337 885
        if (! $parentClass->isSharded()) {
338 880
            return;
339
        }
340
341 5
        $subClass->setShardKey(
342 5
            $parentClass->shardKey['keys'],
343 5
            $parentClass->shardKey['options']
344
        );
345 5
    }
346
}
347