Completed
Push — master ( e74e5e...a7cccd )
by Maciej
12s
created

ClassMetadataFactory::doLoadMetadata()   D

Complexity

Conditions 15
Paths 259

Size

Total Lines 74

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 15.0173

Importance

Changes 0
Metric Value
dl 0
loc 74
ccs 45
cts 47
cp 0.9574
rs 4.0439
c 0
b 0
f 0
cc 15
nc 259
nop 4
crap 15.0173

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 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) {
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...
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) {
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...
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:
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...
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:
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...
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) {
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...
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) {
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 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