DoctrineExtractor   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 71
dl 0
loc 138
rs 9.1199
c 0
b 0
f 0
wmc 41

7 Methods

Rating   Name   Duplication   Size   Complexity  
D getPhpType() 0 28 19
A __construct() 0 3 1
A getProperties() 0 7 2
A isWritable() 0 11 5
B getTypes() 0 48 11
A getMetadata() 0 6 2
A isReadable() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like DoctrineExtractor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DoctrineExtractor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\PropertyInfo;
15
16
use Doctrine\Common\Collections\Collection;
17
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
18
use Doctrine\Common\Persistence\Mapping\MappingException;
19
use Doctrine\Common\Persistence\ObjectManager;
20
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as MongoDbClassMetadata;
21
use Doctrine\ODM\MongoDB\Types\Type as MongoDbType;
22
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
23
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
24
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
25
use Symfony\Component\PropertyInfo\Type;
26
27
/**
28
 * Extracts data using Doctrine MongoDB ODM metadata.
29
 *
30
 * @experimental
31
 *
32
 * @author Kévin Dunglas <[email protected]>
33
 * @author Alan Poulain <[email protected]>
34
 */
35
final class DoctrineExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface
36
{
37
    private $objectManager;
38
39
    public function __construct(ObjectManager $objectManager)
40
    {
41
        $this->objectManager = $objectManager;
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function getProperties($class, array $context = [])
48
    {
49
        if (null === $metadata = $this->getMetadata($class)) {
50
            return null;
51
        }
52
53
        return $metadata->getFieldNames();
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function getTypes($class, $property, array $context = [])
60
    {
61
        if (null === $metadata = $this->getMetadata($class)) {
62
            return null;
63
        }
64
65
        if ($metadata->hasAssociation($property)) {
66
            $class = $metadata->getAssociationTargetClass($property);
67
68
            if ($metadata->isSingleValuedAssociation($property)) {
69
                $nullable = $metadata instanceof MongoDbClassMetadata && $metadata->isNullable($property);
70
71
                return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class)];
72
            }
73
74
            $collectionKeyType = Type::BUILTIN_TYPE_INT;
75
76
            return [
77
                new Type(
78
                    Type::BUILTIN_TYPE_OBJECT,
79
                    false,
80
                    Collection::class,
81
                    true,
82
                    new Type($collectionKeyType),
83
                    new Type(Type::BUILTIN_TYPE_OBJECT, false, $class)
84
                ),
85
            ];
86
        }
87
88
        if ($metadata->hasField($property)) {
89
            $typeOfField = $metadata->getTypeOfField($property);
90
            $nullable = $metadata instanceof MongoDbClassMetadata && $metadata->isNullable($property);
91
92
            switch ($typeOfField) {
93
                case MongoDbType::DATE:
94
                    return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')];
95
                case MongoDbType::HASH:
96
                    return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)];
97
                case MongoDbType::COLLECTION:
98
                    return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT))];
99
                default:
100
                    $builtinType = $this->getPhpType($typeOfField);
101
102
                    return $builtinType ? [new Type($builtinType, $nullable)] : null;
103
            }
104
        }
105
106
        return null;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function isReadable($class, $property, array $context = []): ?bool
113
    {
114
        return null;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function isWritable($class, $property, array $context = []): ?bool
121
    {
122
        if (
123
            null === ($metadata = $this->getMetadata($class))
124
            || $metadata instanceof MongoDbClassMetadata && MongoDbClassMetadata::GENERATOR_TYPE_NONE === $metadata->generatorType
125
            || !\in_array($property, $metadata->getIdentifierFieldNames(), true)
126
        ) {
127
            return null;
128
        }
129
130
        return false;
131
    }
132
133
    private function getMetadata(string $class): ?ClassMetadata
134
    {
135
        try {
136
            return $this->objectManager->getClassMetadata($class);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->objectMana...etClassMetadata($class) returns the type Doctrine\Persistence\Mapping\ClassMetadata which includes types incompatible with the type-hinted return Doctrine\Common\Persiste...ping\ClassMetadata|null.
Loading history...
137
        } catch (MappingException $exception) {
138
            return null;
139
        }
140
    }
141
142
    /**
143
     * Gets the corresponding built-in PHP type.
144
     */
145
    private function getPhpType(string $doctrineType): ?string
146
    {
147
        switch ($doctrineType) {
148
            case MongoDbType::INTEGER:
149
            case MongoDbType::INT:
150
            case MongoDbType::INTID:
151
            case MongoDbType::KEY:
152
                return Type::BUILTIN_TYPE_INT;
153
            case MongoDbType::FLOAT:
154
                return Type::BUILTIN_TYPE_FLOAT;
155
            case MongoDbType::STRING:
156
            case MongoDbType::ID:
157
            case MongoDbType::OBJECTID:
158
            case MongoDbType::TIMESTAMP:
159
            case MongoDbType::BINDATA:
160
            case MongoDbType::BINDATABYTEARRAY:
161
            case MongoDbType::BINDATACUSTOM:
162
            case MongoDbType::BINDATAFUNC:
163
            case MongoDbType::BINDATAMD5:
164
            case MongoDbType::BINDATAUUID:
165
            case MongoDbType::BINDATAUUIDRFC4122:
166
                return Type::BUILTIN_TYPE_STRING;
167
            case MongoDbType::BOOLEAN:
168
            case MongoDbType::BOOL:
169
                return Type::BUILTIN_TYPE_BOOL;
170
        }
171
172
        return null;
173
    }
174
}
175