Passed
Pull Request — master (#33)
by Vincent
06:53
created

AttributesResolver::className()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Bdf\Prime\Entity\Hydrator\Generator;
4
5
use Bdf\Prime\Entity\Hydrator\Exception\HydratorGenerationException;
6
use Bdf\Prime\Entity\ImportableInterface;
7
use Bdf\Prime\Mapper\Mapper;
8
use Bdf\Prime\Mapper\SingleTableInheritanceMapper;
9
use Bdf\Prime\Relations\RelationInterface;
10
use Bdf\Prime\ServiceLocator;
11
12
/**
13
 * Resolve attributes and embedded objects from Mapper
14
 */
15
class AttributesResolver
16
{
17
    /**
18
     * @var Mapper
19
     */
20
    private $mapper;
21
22
    /**
23
     * @var ServiceLocator
24
     */
25
    private $prime;
26
27
    /**
28
     * List of all attributes of the entity
29
     *
30
     * @var AttributeInfo[]
31
     */
32
    private $attributes = [];
33
34
    /**
35
     * List of all embedded entities
36
     *
37
     * @var EmbeddedInfo[]
38
     */
39
    private $embeddeds = [];
40
41
    /**
42
     * @var AttributeInfo[]
43
     */
44
    private $rootAttributes = [];
45
46
    /**
47
     * @var EmbeddedInfo[]
48
     */
49
    private $rootEmbeddeds = [];
50
51
52
    /**
53
     * AttributesResolver constructor.
54
     *
55
     * @param Mapper $mapper
56
     * @param ServiceLocator $prime
57
     *
58
     * @thorws HydratorException
59
     */
60 113
    public function __construct(Mapper $mapper, ServiceLocator $prime)
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line before function; 2 found
Loading history...
61
    {
62 113
        $this->mapper = $mapper;
63 113
        $this->prime = $prime;
64
65 113
        $this->build();
66 113
    }
67
68
    /**
69
     * Get the root entity class name
70
     *
71
     * @return class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
introduced by
Expected "classstring" but found "class-string" for function return type
Loading history...
72
     */
73 1
    public function className(): string
74
    {
75 1
        return $this->mapper->getEntityClass();
76
    }
77
78
    /**
79
     * @return AttributeInfo[]
80
     */
81 27
    public function attributes()
82
    {
83 27
        return $this->attributes;
84
    }
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line after function; 2 found
Loading history...
85
86
87
    /**
88
     * @param string $name
89
     *
90
     * @return AttributeInfo
91
     */
92 2
    public function attribute($name)
93
    {
94 2
        return $this->attributes[$name];
95
    }
96
97
    /**
98
     * @return EmbeddedInfo[]
99
     */
100 26
    public function embeddeds()
101
    {
102 26
        return $this->embeddeds;
103
    }
104
105
    /**
106
     * Get the embedded entity info
107
     *
108
     * @param string $attribute The attribute path
109
     *
110
     * @return EmbeddedInfo
111
     */
112 113
    public function embedded($attribute)
113
    {
114 113
        return $this->embeddeds[$attribute];
115
    }
116
117
    /**
118
     * Get the embedded entity info
119
     *
120
     * @param string $attribute The attribute path
121
     *
122
     * @return EmbeddedInfo
123
     */
124 24
    public function rootEmbedded($attribute)
125
    {
126 24
        return $this->rootEmbeddeds[$attribute];
127
    }
128
129
    /**
130
     * Get all "root" embeddeds
131
     *
132
     * @return EmbeddedInfo[]
133
     */
134 28
    public function rootEmbeddeds()
135
    {
136 28
        return $this->rootEmbeddeds;
137
    }
138
139
    /**
140
     * Check if the root embedded exists
141
     *
142
     * @param string $attribute The attribute path
143
     *
144
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
145
     */
146 25
    public function hasRootEmbedded($attribute)
147
    {
148 25
        return isset($this->rootEmbeddeds[$attribute]);
149
    }
150
151
    /**
152
     * Get all the "root" attributes (not defined into an embedded, or the root embedded property)
153
     *
154
     * @return AttributeInfo[]
155
     */
156 28
    public function rootAttributes()
157
    {
158 28
        return $this->rootAttributes;
159
    }
160
161
    /**
162
     * Does the class is an entity
163
     * Means could have a hydrator
164
     *
165
     * @param string $class
166
     *
167
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
168
     */
169 113
    public function isEntity($class)
170
    {
171 113
        return $this->prime->mappers()->isEntity($class);
172
    }
173
174
    /**
175
     * Does the class implements importable interface
176
     *
177
     * @param string $class
178
     *
179
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
180
     */
181 46
    public function isImportable($class)
182
    {
183 46
        return is_subclass_of($class, ImportableInterface::class);
184
    }
185
186
    /**
187
     * Build the attributes and embedded info
188
     *
189
     * @thows HydratorException
190
     */
191 113
    private function build()
0 ignored issues
show
Coding Style introduced by
Private method name "AttributesResolver::build" must be prefixed with an underscore
Loading history...
192
    {
193 113
        $this->buildMetadataProperties();
194 113
        $this->buildRootAttributes();
195 113
        $this->buildRootEmbeddeds();
196 113
        $this->buildRelationEmbeddeds();
197 113
    }
198
199 113
    private function buildMetadataProperties()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function buildMetadataProperties()
Loading history...
Coding Style introduced by
Private method name "AttributesResolver::buildMetadataProperties" must be prefixed with an underscore
Loading history...
introduced by
Missing function doc comment
Loading history...
200
    {
201 113
        foreach ($this->mapper->metadata()->attributes() as $name => $meta) {
202 113
            $this->attributes[$name] = new AttributeInfo($name, $meta, $this);
203
        }
204
205 113
        foreach ($this->mapper->metadata()->embeddeds() as $name => $meta) {
206 113
            $this->embeddeds[$name] = new EmbeddedInfo($name, $meta, $this);
207
        }
208 113
    }
209
210 113
    private function buildRootAttributes()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function buildRootAttributes()
Loading history...
Coding Style introduced by
Private method name "AttributesResolver::buildRootAttributes" must be prefixed with an underscore
Loading history...
introduced by
Missing function doc comment
Loading history...
211
    {
212 113
        foreach ($this->attributes as $attribute) {
213 113
            if ($attribute->isEmbedded()) {
214
                //Find only the "root" embedded attribute
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// Find only the "root" embedded attribute" but found "//Find only the "root" embedded attribute"
Loading history...
215 113
                $rootAttribue = $attribute->embedded()->rootAttribute();
216
217 113
                if (isset($this->rootAttributes[$rootAttribue])) {
218 25
                    continue;
219
                }
220
221 113
                $attribute = $this->attributes[$rootAttribue] ?? new AttributeInfo($rootAttribue, [
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
222 113
                    'embedded' => $rootAttribue,
223
                    'root'     => true,
224 113
                ], $this);
225
            }
226
227 113
            $this->rootAttributes[$attribute->name()] = $attribute;
228
        }
229 113
    }
230
231 113
    private function buildRootEmbeddeds()
0 ignored issues
show
Coding Style introduced by
Private method name "AttributesResolver::buildRootEmbeddeds" must be prefixed with an underscore
Loading history...
introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Missing doc comment for function buildRootEmbeddeds()
Loading history...
232
    {
233 113
        foreach ($this->embeddeds as $embedded) {
234 113
            if ($embedded->isRoot() && ($embedded->isEntity() || $embedded->isImportable())) {
235 113
                $this->rootEmbeddeds[$embedded->path()] = $embedded;
236
            }
237
        }
238 113
    }
239
240
    /**
241
     * @throws HydratorGenerationException
242
     */
243 113
    private function buildRelationEmbeddeds()
0 ignored issues
show
Coding Style introduced by
Private method name "AttributesResolver::buildRelationEmbeddeds" must be prefixed with an underscore
Loading history...
244
    {
245 113
        foreach ($this->mapper->relations()->relations() as $name => $relation) {
246 110
            if (!empty($relation['detached'])) {
247 23
                unset($this->rootEmbeddeds[$name]);
248 23
                continue;
249
            }
250
251
            // "MANY" relations are stored as array, so cannot be hydrated
252 110
            if (!in_array($relation['type'], [RelationInterface::HAS_MANY, RelationInterface::BELONGS_TO_MANY])) {
253 110
                $this->rootEmbeddeds[$name] = new EmbeddedInfo($name, [
254 110
                    'parentPath' => 'root',
255 110
                    'path'       => $name,
256 110
                    'paths'      => [$name],
257 110
                    'classes'    => $this->resolveClassesFromRelation($relation, $name),
258 110
                ], $this);
259
            } elseif (
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
Coding Style introduced by
First condition of a multi-line IF statement must directly follow the opening parenthesis
Loading history...
260 4
                !empty($relation['wrapper'])
0 ignored issues
show
Coding Style introduced by
Each line in a multi-line IF statement must begin with a boolean operator
Loading history...
261 4
                && is_subclass_of($wrapperClass = $this->prime->repository($relation['entity'])->collectionFactory()->wrapperClass($relation['wrapper']), ImportableInterface::class)
0 ignored issues
show
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
262
            ) {
263 2
                $this->rootEmbeddeds[$name] = new EmbeddedInfo($name, [
264 2
                    'parentPath' => 'root',
265 2
                    'path'       => $name,
266 2
                    'paths'      => [$name],
267 2
                    'class'      => $wrapperClass,
268 2
                ], $this);
269
            } else {
270 2
                unset($this->rootEmbeddeds[$name]);
271
            }
272
273 110
            if (!isset($this->rootAttributes[$name])) {
274 31
                $attributeMetadata = ['root' => true];
275
276 31
                if (isset($this->rootEmbeddeds[$name])) {
277 31
                    $attributeMetadata['embedded'] = $name;
278
                }
279
280 110
                $this->rootAttributes[$name] = new AttributeInfo($name, $attributeMetadata, $this);
281
            }
282
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end foreach"
Loading history...
283 113
    }
284
285
    /**
286
     * Resolve entities classes from one relation
287
     *
288
     * @param array $relation The relation metadata
289
     * @param string $relationName The relation name
290
     *
291
     * @return string[] List of entities classes
292
     *
293
     * @throws HydratorGenerationException
294
     */
295 110
    private function resolveClassesFromRelation(array $relation, $relationName)
0 ignored issues
show
Coding Style introduced by
Private method name "AttributesResolver::resolveClassesFromRelation" must be prefixed with an underscore
Loading history...
296
    {
297 110
        switch ($relation['type']) {
298 110
            case RelationInterface::HAS_ONE:
299 110
            case RelationInterface::BELONGS_TO:
300 93
                return [$relation['entity']];
0 ignored issues
show
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
301
302 26
            case RelationInterface::MORPH_TO:
303 21
                $classes = [];
304
305 21
                foreach ($relation['map'] as $name => $entity) {
306 21
                    if (is_array($entity)) {
307 21
                        $classes[] = $entity['entity'];
308
                    } else {
309 21
                        $classes[] = explode('::', $entity, 2)[0];
310
                    }
311
                }
0 ignored issues
show
Coding Style introduced by
Blank line found after control structure
Loading history...
312
313 21
                return $classes;
0 ignored issues
show
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
314
315 5
            case RelationInterface::BY_INHERITANCE:
316 5
                if (!$this->mapper instanceof SingleTableInheritanceMapper) {
317
                    throw new HydratorGenerationException($this->mapper->getEntityClass(), "The mapper should be a subclass of SingleTableInheritanceMapper for use 'by inheritance' relation");
318
                }
319
320 5
                $classes = [];
321
322 5
                foreach ($this->mapper->getEntityMap() as $entityClass) {
323 5
                    $subMapper = $this->prime->mappers()->build($this->prime, $entityClass);
324 5
                    $classes = array_merge($classes, $this->resolveClassesFromRelation($subMapper->relations()->relations()[$relationName], $relationName));
325
                }
0 ignored issues
show
Coding Style introduced by
Blank line found after control structure
Loading history...
326
327 5
                return array_unique($classes);
0 ignored issues
show
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
328
329
            default:
330
                throw new HydratorGenerationException($this->mapper->getEntityClass(), 'Cannot handle relation type ' . $relation['type']);
0 ignored issues
show
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
331
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end switch"
Loading history...
332
    }
333
}
334