Failed Conditions
Push — master ( a3e53b...559253 )
by Guilherme
14:58
created

AssociationMetadataBuilder   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Test Coverage

Coverage 75%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 55
dl 0
loc 167
ccs 45
cts 60
cp 0.75
rs 10
c 1
b 0
f 0
wmc 19

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A buildCache() 0 4 2
A withFieldName() 0 7 1
A withCacheAnnotation() 0 9 2
A getCascade() 0 20 3
A getFetchMode() 0 9 2
A getTargetEntity() 0 17 4
A createLazyDataTypeResolver() 0 27 2
A withComponentMetadata() 0 7 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Builder;
6
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\ORM\Annotation;
9
use Doctrine\ORM\Mapping;
10
use RuntimeException;
11
use function array_diff;
12
use function array_intersect;
13
use function array_map;
14
use function class_exists;
15
use function constant;
16
use function count;
17
use function defined;
18
use function in_array;
19
use function interface_exists;
20
use function sprintf;
21
22
abstract class AssociationMetadataBuilder
23
{
24
    /** @var Mapping\ClassMetadataBuildingContext */
25
    protected $metadataBuildingContext;
26
27
    /** @var CacheMetadataBuilder */
28
    protected $cacheMetadataBuilder;
29
30
    /** @var Mapping\ClassMetadata */
31
    protected $componentMetadata;
32
33
    /** @var string */
34
    protected $fieldName;
35
36
    /** @var Annotation\Cache|null */
37
    protected $cacheAnnotation;
38
39 408
    public function __construct(
40
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext,
41
        ?CacheMetadataBuilder $cacheMetadataBuilder = null
42
    ) {
43 408
        $this->metadataBuildingContext = $metadataBuildingContext;
44 408
        $this->cacheMetadataBuilder    = $cacheMetadataBuilder ?: new CacheMetadataBuilder($metadataBuildingContext);
45 408
    }
46
47 407
    public function withComponentMetadata(Mapping\ClassMetadata $componentMetadata) : AssociationMetadataBuilder
48
    {
49 407
        $this->componentMetadata = $componentMetadata;
50
51 407
        $this->cacheMetadataBuilder->withComponentMetadata($componentMetadata);
52
53 407
        return $this;
54
    }
55
56 407
    public function withFieldName(string $fieldName) : AssociationMetadataBuilder
57
    {
58 407
        $this->fieldName = $fieldName;
59
60 407
        $this->cacheMetadataBuilder->withFieldName($fieldName);
61
62 407
        return $this;
63
    }
64
65 390
    public function withCacheAnnotation(?Annotation\Cache $cacheAnnotation) : AssociationMetadataBuilder
66
    {
67 390
        $this->cacheAnnotation = $cacheAnnotation;
68
69 390
        if ($cacheAnnotation !== null) {
70 15
            $this->cacheMetadataBuilder->withCacheAnnotation($cacheAnnotation);
71
        }
72
73 390
        return $this;
74
    }
75
76 268
    protected function buildCache(Mapping\AssociationMetadata $associationMetadata) : void
77
    {
78 268
        if ($this->cacheAnnotation !== null) {
79 15
            $associationMetadata->setCache($this->cacheMetadataBuilder->build());
80
        }
81 268
    }
82
83
    /**
84
     * Attempts to resolve target entity.
85
     *
86
     * @param string $targetEntity The proposed target entity
87
     *
88
     * @return string The processed target entity
89
     *
90
     * @throws Mapping\MappingException If a target entity is not valid.
91
     */
92 268
    protected function getTargetEntity(string $targetEntity) : string
93
    {
94
        // Validate if target entity is defined
95 268
        if (! $targetEntity) {
96
            throw Mapping\MappingException::missingTargetEntity($this->fieldName);
97
        }
98
99
        // Validate that target entity exists
100 268
        if (! (class_exists($targetEntity) || interface_exists($targetEntity))) {
101
            throw Mapping\MappingException::invalidTargetEntityClass(
102
                $targetEntity,
103
                $this->componentMetadata->getClassName(),
104
                $this->fieldName
105
            );
106
        }
107
108 268
        return $targetEntity;
109
    }
110
111
    /**
112
     * Attempts to resolve the cascade modes.
113
     *
114
     * @param string[] $originalCascades The original unprocessed field cascades.
115
     *
116
     * @return string[] The processed field cascades.
117
     *
118
     * @throws Mapping\MappingException If a cascade option is not valid.
119
     */
120 268
    protected function getCascade(array $originalCascades) : array
121
    {
122 268
        $cascadeTypes = ['remove', 'persist', 'refresh'];
123 268
        $cascades     = array_map('strtolower', $originalCascades);
124
125 268
        if (in_array('all', $cascades, true)) {
126 29
            $cascades = $cascadeTypes;
127
        }
128
129 268
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
130
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
131
132
            throw Mapping\MappingException::invalidCascadeOption(
133
                $diffCascades,
134
                $this->componentMetadata->getClassName(),
135
                $this->fieldName
136
            );
137
        }
138
139 268
        return $cascades;
140
    }
141
142
    /**
143
     * Attempts to resolve the fetch mode.
144
     *
145
     * @param string $fetchMode The fetch mode.
146
     *
147
     * @return string The fetch mode as defined in ClassMetadata.
148
     *
149
     * @throws Mapping\MappingException If the fetch mode is not valid.
150
     */
151 268
    protected function getFetchMode($fetchMode) : string
152
    {
153 268
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
154
155 268
        if (! defined($fetchModeConstant)) {
156
            throw Mapping\MappingException::invalidFetchMode($this->componentMetadata->getClassName(), $fetchMode);
157
        }
158
159 268
        return constant($fetchModeConstant);
160
    }
161
162 257
    protected function createLazyDataTypeResolver(
163
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext,
164
        Mapping\AssociationMetadata $associationMetadata,
165
        Mapping\JoinColumnMetadata $joinColumnMetadata,
166
        string $targetEntity
167
    ) : Mapping\LazyDataType {
168 257
        return Mapping\LazyDataType::create(
169
            static function () use ($metadataBuildingContext, $associationMetadata, $joinColumnMetadata, $targetEntity) : Type {
170 156
                $classMetadataFactory = $metadataBuildingContext->getClassMetadataFactory();
171 156
                $targetClassMetadata  = $classMetadataFactory->getMetadataFor($targetEntity);
172 156
                $targetColumnMetadata = $targetClassMetadata->getColumn($joinColumnMetadata->getReferencedColumnName());
0 ignored issues
show
Bug introduced by
It seems like $joinColumnMetadata->getReferencedColumnName() can also be of type null; however, parameter $columnName of Doctrine\ORM\Mapping\ClassMetadata::getColumn() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

172
                $targetColumnMetadata = $targetClassMetadata->getColumn(/** @scrutinizer ignore-type */ $joinColumnMetadata->getReferencedColumnName());
Loading history...
173
174 156
                if (! $targetColumnMetadata) {
175
                    throw new RuntimeException(sprintf(
176
                        'Could not resolve type of column "%s" of class "%s"',
177
                        $joinColumnMetadata->getReferencedColumnName(),
178
                        $associationMetadata->getDeclaringClass()->getClassName()
179
                    ));
180
                }
181
182 156
                $resolvedType = $targetColumnMetadata->getType();
183
184
                // Performance optimization: Once LazyDataType is resolved,
185
                // replace the column type (lazy) with resolved column type.
186 156
                $joinColumnMetadata->setType($resolvedType);
0 ignored issues
show
Bug introduced by
It seems like $resolvedType can also be of type null; however, parameter $type of Doctrine\ORM\Mapping\ColumnMetadata::setType() does only seem to accept Doctrine\DBAL\Types\Type, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
                $joinColumnMetadata->setType(/** @scrutinizer ignore-type */ $resolvedType);
Loading history...
187
188 156
                return $resolvedType;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $resolvedType could return the type null which is incompatible with the type-hinted return Doctrine\DBAL\Types\Type. Consider adding an additional type-check to rule them out.
Loading history...
189 257
            }
190
        );
191
    }
192
}
193