Completed
Pull Request — master (#15)
by Jonathan
05:36
created

ClassMetadata::getAssociationTargetClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\SkeletonMapper\Mapping;
6
7
use InvalidArgumentException;
8
use ReflectionClass;
9
use ReflectionProperty;
10
use function array_keys;
11
use function call_user_func_array;
12
use function get_class;
13
use function in_array;
14
use function sprintf;
15
16
/**
17
 * Class used to hold metadata about mapped classes.
18
 */
19
class ClassMetadata implements ClassMetadataInterface
20
{
21
    /** @var string */
22
    public $name;
23
24
    /** @var mixed[] */
25
    public $identifier = [];
26
27
    /** @var string[] */
28
    public $identifierFieldNames = [];
29
30
    /** @var mixed[][] */
31
    public $fieldMappings = [];
32
33
    /** @var mixed[][] */
34
    public $associationMappings = [];
35
36
    /** @var string[][] */
37
    public $lifecycleCallbacks = [];
38
39
    /** @var ReflectionClass */
40
    public $reflClass;
41
42
    /** @var ReflectionProperty[] */
43
    public $reflFields = [];
44
45 54
    public function __construct(string $className)
46
    {
47 54
        $this->name      = $className;
48 54
        $this->reflClass = new ReflectionClass($className);
49 54
    }
50
51
    /**
52
     * @param mixed[] $identifier
53
     */
54 22
    public function setIdentifier(array $identifier) : void
55
    {
56 22
        $this->identifier = $identifier;
57 22
    }
58
59
    /**
60
     * @param string[] $identifierFieldNames
61
     */
62 22
    public function setIdentifierFieldNames(array $identifierFieldNames) : void
63
    {
64 22
        $this->identifierFieldNames = $identifierFieldNames;
65 22
    }
66
67
    /**
68
     * {@inheritDoc}
69
     */
70 35
    public function mapField(array $mapping) : void
71
    {
72 35
        if (! isset($mapping['name'])) {
73 35
            $mapping['name'] = $mapping['fieldName'];
74
        }
75
76 35
        if (isset($mapping['type']) && isset($mapping['targetObject'])) {
77 5
            $this->associationMappings[$mapping['fieldName']] = $mapping;
78
        } else {
79 30
            $this->fieldMappings[$mapping['fieldName']] = $mapping;
80
        }
81
82 35
        $this->initReflField($mapping);
83 35
    }
84
85
    /**
86
     * Gets the fully-qualified class name of this persistent class.
87
     */
88 3
    public function getName() : string
89
    {
90 3
        return $this->name;
91
    }
92
93
    /**
94
     * Gets the mapped identifier field name.
95
     *
96
     * The returned structure is an array of the identifier field names.
97
     *
98
     * @return mixed[]
99
     */
100 27
    public function getIdentifier() : array
101
    {
102 27
        return $this->identifier;
103
    }
104
105
    /**
106
     * Gets the ReflectionClass instance for this mapped class.
107
     */
108 3
    public function getReflectionClass() : ReflectionClass
109
    {
110 3
        return $this->reflClass;
111
    }
112
113
    /**
114
     * {@inheritDoc}
115
     */
116 3
    public function isIdentifier($fieldName) : bool
117
    {
118 3
        return in_array($fieldName, $this->getIdentifierFieldNames(), true);
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124 4
    public function hasField($fieldName) : bool
125
    {
126 4
        return isset($this->fieldMappings[$fieldName]);
127
    }
128
129
    /**
130
     * A numerically indexed list of field names of this persistent class.
131
     *
132
     * This array includes identifier fields if present on this class.
133
     *
134
     * @return string[]
135
     */
136 3
    public function getFieldNames() : array
137
    {
138 3
        return array_keys($this->fieldMappings);
139
    }
140
141
    /**
142
     * An array of field mappings for this persistent class indexed by field name.
143
     *
144
     * @return mixed[][]
145
     */
146 2
    public function getFieldMappings() : array
147
    {
148 2
        return $this->fieldMappings;
149
    }
150
151
    /**
152
     * {@inheritDoc}
153
     */
154 2
    public function getAssociationNames() : array
155
    {
156 2
        return array_keys($this->associationMappings);
157
    }
158
159
    /**
160
     * {@inheritDoc}
161
     */
162 2
    public function getTypeOfField($fieldName) : ?string
163
    {
164 2
        return $this->fieldMappings[$fieldName]['type'] ?? null;
165
    }
166
167
    /**
168
     * {@inheritDoc}
169
     */
170 2
    public function getAssociationTargetClass($assocName) : string
171
    {
172 2
        if (! isset($this->associationMappings[$assocName])) {
173 1
            throw new InvalidArgumentException(
174 1
                sprintf("Association name expected, '%s' is not an association.", $assocName)
175
            );
176
        }
177
178 1
        return $this->associationMappings[$assocName]['targetObject'];
179
    }
180
181
    /**
182
     * {@inheritDoc}
183
     */
184 3
    public function getIdentifierValues($object) : array
185
    {
186 3
        $identifier = [];
187 3
        foreach ($this->identifierFieldNames as $identifierFieldName) {
188 3
            $identifier[$this->fieldMappings[$identifierFieldName]['name']] = $this->reflFields[$identifierFieldName]->getValue($object);
189
        }
190
191 3
        return $identifier;
192
    }
193
194
    /**
195
     * {@inheritDoc}
196
     *
197
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
198
     */
199 3
    public function hasAssociation($fieldName) : bool
200
    {
201 3
        return isset($this->associationMappings[$fieldName]);
202
    }
203
204
    /**
205
     * {@inheritDoc}
206
     *
207
     * Checks whether the class has a mapped reference or embed for the specified field and
208
     * is a single valued association.
209
     */
210 2
    public function isSingleValuedAssociation($fieldName) : bool
211
    {
212 2
        return isset($this->associationMappings[$fieldName]['type']) &&
213 2
            $this->associationMappings[$fieldName]['type'] === 'one';
214
    }
215
216
    /**
217
     * {@inheritDoc}
218
     *
219
     * Checks whether the class has a mapped reference or embed for the specified field and
220
     * is a collection valued association.
221
     */
222 2
    public function isCollectionValuedAssociation($fieldName) : bool
223
    {
224 2
        return isset($this->associationMappings[$fieldName]['type']) &&
225 2
            $this->associationMappings[$fieldName]['type'] === 'many';
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 23
    public function invokeLifecycleCallbacks(string $event, $object, ?array $arguments = null) : void
232
    {
233 23
        if (! $object instanceof $this->name) {
234 1
            throw new InvalidArgumentException(
235 1
                sprintf('Expected class "%s"; found: "%s"', $this->name, get_class($object))
236
            );
237
        }
238
239 22
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
240 22
            if ($arguments !== null) {
241
                /** @var callable $callable */
242 21
                $callable = [$object, $callback];
243
244 21
                call_user_func_array($callable, $arguments);
245
            } else {
246 22
                $object->$callback();
247
            }
248
        }
249 22
    }
250
251
    /**
252
     * Checks whether the class has callbacks registered for a lifecycle event.
253
     *
254
     * @param string $event Lifecycle event
255
     *
256
     */
257 23
    public function hasLifecycleCallbacks(string $event) : bool
258
    {
259 23
        return isset($this->lifecycleCallbacks[$event]);
260
    }
261
262
    /**
263
     * Gets the registered lifecycle callbacks for an event.
264
     *
265
     *
266
     * @return string[]
267
     */
268 1
    public function getLifecycleCallbacks(string $event) : array
269
    {
270 1
        return $this->lifecycleCallbacks[$event] ?? [];
271
    }
272
273
    /**
274
     * Adds a lifecycle callback for objects of this class.
275
     *
276
     * If the callback is already registered, this is a NOOP.
277
     */
278 23
    public function addLifecycleCallback(string $callback, string $event) : void
279
    {
280 23
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) {
281 1
            return;
282
        }
283
284 23
        $this->lifecycleCallbacks[$event][] = $callback;
285 23
    }
286
287
    /**
288
     * Sets the lifecycle callbacks for objects of this class.
289
     *
290
     * Any previously registered callbacks are overwritten.
291
     *
292
     * @param string[][] $callbacks
293
     */
294 1
    public function setLifecycleCallbacks(array $callbacks) : void
295
    {
296 1
        $this->lifecycleCallbacks = $callbacks;
297 1
    }
298
299
    /**
300
     * Returns an array of identifier field names numerically indexed.
301
     *
302
     * @return string[]
303
     */
304 4
    public function getIdentifierFieldNames() : array
305
    {
306 4
        return $this->identifierFieldNames;
307
    }
308
309
    /**
310
     * {@inheritDoc}
311
     */
312 1
    public function getAssociationMappedByTargetField($fieldName)
313
    {
314 1
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
315
    }
316
317
    /**
318
     * {@inheritDoc}
319
     */
320 1
    public function isAssociationInverseSide($fieldName)
321
    {
322 1
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
323
    }
324
325
    /**
326
     * @param mixed[] $mapping
327
     */
328 35
    private function initReflField(array $mapping) : void
329
    {
330 35
        if (! $this->reflClass->hasProperty($mapping['fieldName'])) {
331 9
            return;
332
        }
333
334 27
        $reflProp = $this->reflClass->getProperty($mapping['fieldName']);
335 27
        $reflProp->setAccessible(true);
336 27
        $this->reflFields[$mapping['fieldName']] = $reflProp;
337 27
    }
338
}
339