Completed
Pull Request — master (#1803)
by Andreas
15:44
created

ClassMetadataFactory::doLoadMetadata()   D

Complexity

Conditions 15
Paths 259

Size

Total Lines 73

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 46
CRAP Score 15.0163

Importance

Changes 0
Metric Value
dl 0
loc 73
ccs 46
cts 48
cp 0.9583
rs 4.0657
c 0
b 0
f 0
cc 15
nc 259
nop 4
crap 15.0163

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 assert;
23
use function get_class;
24
use function get_class_methods;
25
use function in_array;
26
use function ucfirst;
27
28
/**
29
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
30
 * metadata mapping informations of a class which describes how a class should be mapped
31
 * to a document database.
32
 */
33
class ClassMetadataFactory extends AbstractClassMetadataFactory
34
{
35
    /** @var string */
36
    protected $cacheSalt = '$MONGODBODMCLASSMETADATA';
37
38
    /** @var DocumentManager The DocumentManager instance */
39
    private $dm;
40
41
    /** @var Configuration The Configuration instance */
42
    private $config;
43
44
    /** @var MappingDriver The used metadata driver. */
45
    private $driver;
46
47
    /** @var EventManager The event manager instance */
48
    private $evm;
49
50 1697
    public function setDocumentManager(DocumentManager $dm) : void
51
    {
52 1697
        $this->dm = $dm;
53 1697
    }
54
55 1697
    public function setConfiguration(Configuration $config) : void
56
    {
57 1697
        $this->config = $config;
58 1697
    }
59
60 1433
    public function getMetadataFor($className)
61
    {
62 1433
        return parent::getMetadataFor($this->dm->getClassNameResolver()->getRealClass($className));
63
    }
64
65
    /**
66
     * Lazy initialization of this stuff, especially the metadata driver,
67
     * since these are not needed at all when a metadata cache is active.
68
     */
69 1432
    protected function initialize() : void
70
    {
71 1432
        $this->driver      = $this->config->getMetadataDriverImpl();
72 1432
        $this->evm         = $this->dm->getEventManager();
73 1432
        $this->initialized = true;
74 1432
    }
75
76
    /**
77
     * {@inheritDoc}
78
     */
79
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) : string
80
    {
81
        return $this->config->getDocumentNamespace($namespaceAlias) . '\\' . $simpleClassName;
82
    }
83
84
    /**
85
     * {@inheritDoc}
86
     */
87 915
    protected function getDriver()
88
    {
89 915
        return $this->driver;
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95 1428
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
96
    {
97 1428
    }
98
99
    /**
100
     * {@inheritDoc}
101
     */
102 1432
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) : void
103
    {
104 1432
    }
105
106
    /**
107
     * {@inheritDoc}
108
     */
109 1428
    protected function isEntity(ClassMetadataInterface $class) : bool
110
    {
111 1428
        assert($class instanceof ClassMetadata);
112 1428
        return ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument;
113
    }
114
115
    /**
116
     * {@inheritDoc}
117
     */
118 1432
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents = []) : void
119
    {
120 1432
        assert($class instanceof ClassMetadata);
121 1432
        if ($parent instanceof ClassMetadata) {
122 913
            $class->setInheritanceType($parent->inheritanceType);
123 913
            $class->setDiscriminatorField($parent->discriminatorField);
124 913
            $class->setDiscriminatorMap($parent->discriminatorMap);
125 913
            $class->setDefaultDiscriminatorValue($parent->defaultDiscriminatorValue);
126 913
            $class->setIdGeneratorType($parent->generatorType);
127 913
            $this->addInheritedFields($class, $parent);
128 913
            $this->addInheritedRelations($class, $parent);
129 913
            $this->addInheritedIndexes($class, $parent);
130 913
            $this->setInheritedShardKey($class, $parent);
131 913
            $class->setIdentifier($parent->identifier);
132 913
            $class->setVersioned($parent->isVersioned);
133 913
            $class->setVersionField($parent->versionField);
134 913
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
135 913
            $class->setAlsoLoadMethods($parent->alsoLoadMethods);
136 913
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
137 913
            $class->setReadPreference($parent->readPreference, $parent->readPreferenceTags);
138 913
            $class->setWriteConcern($parent->writeConcern);
139
140 913
            if ($parent->isMappedSuperclass) {
141 849
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
142
            }
143
144 913
            if ($parent->isFile) {
145 1
                $class->isFile = true;
146 1
                $class->setBucketName($parent->bucketName);
147
148 1
                if ($parent->chunkSizeBytes !== null) {
149 1
                    $class->setChunkSizeBytes($parent->chunkSizeBytes);
150
                }
151
            }
152
        }
153
154
        // Invoke driver
155
        try {
156 1432
            $this->driver->loadMetadataForClass($class->getName(), $class);
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
157 6
        } catch (ReflectionException $e) {
158
            throw MappingException::reflectionFailure($class->getName(), $e);
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
159
        }
160
161 1428
        $this->validateIdentifier($class);
162
163 1428
        if ($parent instanceof ClassMetadata && $rootEntityFound && $parent->generatorType === $class->generatorType) {
164 121
            if ($parent->generatorType) {
165 121
                $class->setIdGeneratorType($parent->generatorType);
166
            }
167 121
            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...
168
                $class->setIdGeneratorOptions($parent->generatorOptions);
169
            }
170 121
            if ($parent->idGenerator) {
171 121
                $class->setIdGenerator($parent->idGenerator);
172
            }
173
        } else {
174 1428
            $this->completeIdGeneratorMapping($class);
175
        }
176
177 1428
        if ($parent instanceof ClassMetadata && $parent->isInheritanceTypeSingleCollection()) {
178 107
            $class->setDatabase($parent->getDatabase());
179 107
            $class->setCollection($parent->getCollection());
180
        }
181
182 1428
        $class->setParentClasses($nonSuperclassParents);
183
184 1428
        if (! $this->evm->hasListeners(Events::loadClassMetadata)) {
185 1426
            return;
186
        }
187
188 2
        $eventArgs = new LoadClassMetadataEventArgs($class, $this->dm);
189 2
        $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
190 2
    }
191
192
    /**
193
     * Validates the identifier mapping.
194
     *
195
     * @throws MappingException
196
     */
197 1428
    protected function validateIdentifier(ClassMetadata $class) : void
198
    {
199 1428
        if (! $class->identifier && ! $class->isMappedSuperclass && ! $class->isEmbeddedDocument && ! $class->isQueryResultDocument) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->identifier of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
200
            throw MappingException::identifierRequired($class->name);
201
        }
202 1428
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207 1432
    protected function newClassMetadataInstance($className) : ClassMetadata
208
    {
209 1432
        return new ClassMetadata($className);
210
    }
211
212 1428
    private function completeIdGeneratorMapping(ClassMetadata $class) : void
213
    {
214 1428
        $idGenOptions = $class->generatorOptions;
215 1428
        switch ($class->generatorType) {
216
            case ClassMetadata::GENERATOR_TYPE_AUTO:
217 1360
                $class->setIdGenerator(new AutoGenerator());
218 1360
                break;
219 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...
220 9
                $incrementGenerator = new IncrementGenerator();
221 9
                if (isset($idGenOptions['key'])) {
222
                    $incrementGenerator->setKey((string) $idGenOptions['key']);
223
                }
224 9
                if (isset($idGenOptions['collection'])) {
225
                    $incrementGenerator->setCollection((string) $idGenOptions['collection']);
226
                }
227 9
                if (isset($idGenOptions['startingId'])) {
228 1
                    $incrementGenerator->setStartingId((int) $idGenOptions['startingId']);
229
                }
230 9
                $class->setIdGenerator($incrementGenerator);
231 9
                break;
232
            case ClassMetadata::GENERATOR_TYPE_UUID:
233 4
                $uuidGenerator = new UuidGenerator();
234 4
                if (isset($idGenOptions['salt'])) {
235 2
                    $uuidGenerator->setSalt((string) $idGenOptions['salt']);
236
                }
237 4
                $class->setIdGenerator($uuidGenerator);
238 4
                break;
239 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...
240 1
                $alnumGenerator = new AlnumGenerator();
241 1
                if (isset($idGenOptions['pad'])) {
242
                    $alnumGenerator->setPad((int) $idGenOptions['pad']);
243
                }
244 1
                if (isset($idGenOptions['chars'])) {
245 1
                    $alnumGenerator->setChars((string) $idGenOptions['chars']);
246
                } elseif (isset($idGenOptions['awkwardSafe'])) {
247
                    $alnumGenerator->setAwkwardSafeMode((bool) $idGenOptions['awkwardSafe']);
248
                }
249
250 1
                $class->setIdGenerator($alnumGenerator);
251 1
                break;
252
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
253
                if (empty($idGenOptions['class'])) {
254
                    throw MappingException::missingIdGeneratorClass($class->name);
255
                }
256
257
                $customGenerator = new $idGenOptions['class']();
258
                unset($idGenOptions['class']);
259
                if (! $customGenerator instanceof AbstractIdGenerator) {
260
                    throw MappingException::classIsNotAValidGenerator(get_class($customGenerator));
261
                }
262
263
                $methods = get_class_methods($customGenerator);
264
                foreach ($idGenOptions as $name => $value) {
265
                    $method = 'set' . ucfirst($name);
266
                    if (! in_array($method, $methods)) {
267
                        throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name);
268
                    }
269
270
                    $customGenerator->$method($value);
271
                }
272
                $class->setIdGenerator($customGenerator);
273
                break;
274
            case ClassMetadata::GENERATOR_TYPE_NONE:
275 150
                break;
276
            default:
277
                throw new MappingException('Unknown generator type: ' . $class->generatorType);
278
        }
279 1428
    }
280
281
    /**
282
     * Adds inherited fields to the subclass mapping.
283
     */
284 913
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void
285
    {
286 913
        foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
287 139 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...
288 133
                $mapping['inherited'] = $parentClass->name;
289
            }
290 139
            if (! isset($mapping['declared'])) {
291 139
                $mapping['declared'] = $parentClass->name;
292
            }
293 139
            $subClass->addInheritedFieldMapping($mapping);
294
        }
295 913
        foreach ($parentClass->reflFields as $name => $field) {
296 139
            $subClass->reflFields[$name] = $field;
297
        }
298 913
    }
299
300
301
    /**
302
     * Adds inherited association mappings to the subclass mapping.
303
     *
304
     * @throws MappingException
305
     */
306 913
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void
307
    {
308 913
        foreach ($parentClass->associationMappings as $field => $mapping) {
309 89
            if ($parentClass->isMappedSuperclass) {
310 4
                $mapping['sourceDocument'] = $subClass->name;
311
            }
312
313 89 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...
314 85
                $mapping['inherited'] = $parentClass->name;
315
            }
316 89
            if (! isset($mapping['declared'])) {
317 89
                $mapping['declared'] = $parentClass->name;
318
            }
319 89
            $subClass->addInheritedAssociationMapping($mapping);
320
        }
321 913
    }
322
323
    /**
324
     * Adds inherited indexes to the subclass mapping.
325
     */
326 913
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void
327
    {
328 913
        foreach ($parentClass->indexes as $index) {
329 65
            $subClass->addIndex($index['keys'], $index['options']);
330
        }
331 913
    }
332
333
    /**
334
     * Adds inherited shard key to the subclass mapping.
335
     */
336 913
    private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass) : void
337
    {
338 913
        if (! $parentClass->isSharded()) {
339 908
            return;
340
        }
341
342 5
        $subClass->setShardKey(
343 5
            $parentClass->shardKey['keys'],
344 5
            $parentClass->shardKey['options']
345
        );
346 5
    }
347
}
348