Completed
Pull Request — master (#1036)
by Hector
03:00
created

ItemNormalizer::getIdentifiersFromItem()   C

Complexity

Conditions 9
Paths 10

Size

Total Lines 69
Code Lines 45

Duplication

Lines 56
Ratio 81.16 %

Importance

Changes 0
Metric Value
dl 56
loc 69
rs 6.2192
c 0
b 0
f 0
cc 9
eloc 45
nc 10
nop 1

How to fix   Long Method   

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
/*
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
namespace ApiPlatform\Core\JsonApi\Serializer;
13
14
use ApiPlatform\Core\Api\IriConverterInterface;
15
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
16
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
17
use ApiPlatform\Core\Exception\InvalidArgumentException;
18
use ApiPlatform\Core\Exception\RuntimeException;
19
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
20
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
21
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
22
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
23
use ApiPlatform\Core\Serializer\AbstractItemNormalizer;
24
use ApiPlatform\Core\Serializer\ContextTrait;
25
use ApiPlatform\Core\Util\ClassInfoTrait;
26
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
27
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
28
29
/**
30
 * Converts between objects and array.
31
 *
32
 * @author Kévin Dunglas <[email protected]>
33
 * @author Amrouche Hamza <[email protected]>
34
 */
35
final class ItemNormalizer extends AbstractItemNormalizer
36
{
37
    use ContextTrait;
38
    use ClassInfoTrait;
39
40
    const FORMAT = 'jsonapi';
41
42
    private $componentsCache = [];
43
44
    private $resourceMetadataFactory;
45
46
    private $itemDataProvider;
47
48 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
49
        PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory,
50
        PropertyMetadataFactoryInterface $propertyMetadataFactory,
51
        IriConverterInterface $iriConverter,
52
        ResourceClassResolverInterface $resourceClassResolver,
53
        PropertyAccessorInterface $propertyAccessor = null,
54
        NameConverterInterface $nameConverter = null,
55
        ResourceMetadataFactoryInterface $resourceMetadataFactory,
56
        ItemDataProviderInterface $itemDataProvider
57
    ) {
58
        parent::__construct(
59
            $propertyNameCollectionFactory,
60
            $propertyMetadataFactory,
61
            $iriConverter,
62
            $resourceClassResolver,
63
            $propertyAccessor,
64
            $nameConverter
65
        );
66
67
        $this->resourceMetadataFactory = $resourceMetadataFactory;
68
        $this->itemDataProvider = $itemDataProvider;
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function supportsNormalization($data, $format = null)
75
    {
76
        return self::FORMAT === $format && parent::supportsNormalization($data, $format);
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function normalize($object, $format = null, array $context = [])
83
    {
84
        $context['cache_key'] = $this->getCacheKey($format, $context);
85
86
        // Get and populate attributes data
87
        $objectAttributesData = parent::normalize($object, $format, $context);
88
89
        if (!is_array($objectAttributesData)) {
90
            return $objectAttributesData;
91
        }
92
93
        // Get and populate identifier if existent
94
        $identifier = $this->getItemIdentifierValue($object, $context, $objectAttributesData);
95
96
        // Get and populate item type
97
        $resourceClass = $this->resourceClassResolver->getResourceClass(
98
            $object,
99
            $context['resource_class'] ?? null,
100
            true
101
        );
102
        $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
103
104
        // Get and populate relations
105
        $components = $this->getComponents($object, $format, $context);
106
        $objectRelationshipsData = $this->getPopulatedRelations(
107
            $object,
108
            $format,
109
            $context,
110
            $components
111
        );
112
113
        // TODO: Pending population of links
114
        // $item = $this->populateRelation($item, $object, $format, $context, $components, 'links');
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
115
116
        $item = [
117
            // The id attribute must be a string
118
            // See: http://jsonapi.org/format/#document-resource-object-identification
119
            'id' => (string) $identifier,
120
            'type' => $resourceMetadata->getShortName(),
121
        ];
122
123
        if ($objectAttributesData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $objectAttributesData 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...
124
            $item['attributes'] = $objectAttributesData;
125
        }
126
127
        if ($objectRelationshipsData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $objectRelationshipsData 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...
128
            $item['relationships'] = $objectRelationshipsData;
129
        }
130
131
        return ['data' => $item];
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function supportsDenormalization($data, $type, $format = null)
138
    {
139
        return self::FORMAT === $format && parent::supportsDenormalization($data, $type, $format);
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function denormalize($data, $class, $format = null, array $context = [])
146
    {
147
        // TODO: Test what is this about
148
        // Avoid issues with proxies if we populated the object
149
        // if (isset($data['data']['id']) && !isset($context['object_to_populate'])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
83% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
150
        //     if (isset($context['api_allow_update']) && true !== $context['api_allow_update']) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
151
        //         throw new InvalidArgumentException('Update is not allowed for this operation.');
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
152
        //     }
153
154
        //     $context['object_to_populate'] = $this->iriConverter->getItemFromIri(
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
155
        //         $data['data']['id'],
0 ignored issues
show
Unused Code Comprehensibility introduced by
89% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
156
        //         $context + ['fetch_data' => false]
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
157
        //     );
158
        // }
159
160
        // Approach #1
161
        // Merge attributes and relations previous to apply parents denormalizing
162
        $dataToDenormalize = array_merge(
163
            isset($data['data']['attributes']) ?
164
                $data['data']['attributes'] : [],
165
            isset($data['data']['relationships']) ?
166
                $data['data']['relationships'] : []
167
        );
168
169
        return parent::denormalize(
170
            $dataToDenormalize,
171
            $class,
172
            $format,
173
            $context
174
        );
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    protected function getAttributes($object, $format, array $context)
181
    {
182
        return $this->getComponents($object, $format, $context)['attributes'];
183
    }
184
185
    /**
186
     * Gets JSON API components of the resource: attributes, relationships, meta and links.
187
     *
188
     * @param object      $object
189
     * @param string|null $format
190
     * @param array       $context
191
     *
192
     * @return array
193
     */
194
    private function getComponents($object, string $format = null, array $context)
195
    {
196
        if (isset($this->componentsCache[$context['cache_key']])) {
197
            return $this->componentsCache[$context['cache_key']];
198
        }
199
200
        $attributes = parent::getAttributes($object, $format, $context);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getAttributes() instead of getComponents()). Are you sure this is correct? If so, you might want to change this to $this->getAttributes().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
201
202
        $options = $this->getFactoryOptions($context);
203
204
        $shortName = $className = '';
205
206
        $components = [
207
            'links' => [],
208
            'relationships' => [],
209
            'attributes' => [],
210
            'meta' => [],
211
        ];
212
213
        foreach ($attributes as $attribute) {
214
            $propertyMetadata = $this
215
                ->propertyMetadataFactory
216
                ->create($context['resource_class'], $attribute, $options);
217
218
            $type = $propertyMetadata->getType();
219
            $isOne = $isMany = false;
220
221
            if (null !== $type) {
222 View Code Duplication
                if ($type->isCollection()) {
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...
223
                    $valueType = $type->getCollectionValueType();
224
225
                    $isMany = null !== $valueType
226
                        && ($className = $valueType->getClassName())
227
                        && $this->resourceClassResolver->isResourceClass($className);
228
                } else {
229
                    $className = $type->getClassName();
230
231
                    $isOne = null !== $className
232
                        && $this->resourceClassResolver->isResourceClass($className);
233
                }
234
235
                $shortName =
236
                    (
237
                        null !== $className
238
                            && $this->resourceClassResolver->isResourceClass($className)
239
                                ? $this->resourceMetadataFactory->create($className)->getShortName() :
240
                                ''
241
                    );
242
            }
243
244
            if (!$isOne && !$isMany) {
245
                $components['attributes'][] = $attribute;
246
247
                continue;
248
            }
249
250
            $relation = [
251
                'name' => $attribute,
252
                'type' => $shortName,
253
                'cardinality' => $isOne ? 'one' : 'many',
254
            ];
255
256
            $components['relationships'][] = $relation;
257
        }
258
259
        return $this->componentsCache[$context['cache_key']] = $components;
260
    }
261
262
    /**
263
     * Populates links and relationships keys.
264
     *
265
     * @param array       $data
0 ignored issues
show
Bug introduced by
There is no parameter named $data. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
266
     * @param object      $object
267
     * @param string|null $format
268
     * @param array       $context
269
     * @param array       $components
270
     * @param string      $type
271
     *
272
     * @return array
273
     */
274
    private function getPopulatedRelations(
275
        $object,
276
        string $format = null,
277
        array $context,
278
        array $components,
279
        string $type = 'relationships'
280
    ): array {
281
        $data = [];
282
283
        $identifier = '';
284
        foreach ($components[$type] as $relation) {
285
            $attributeValue = $this->getAttributeValue(
286
                $object,
287
                $relation['name'],
288
                $format,
289
                $context
290
            );
291
292
            if (!$attributeValue) {
293
                continue;
294
            }
295
296
            $data[$relation['name']] = [
297
                // TODO: Pending review
298
                // 'links' => ['self' => $this->iriConverter->getIriFromItem($object)],
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
299
                'data' => [],
300
            ];
301
302
            // Many to one relationship
303
            if ('one' === $relation['cardinality']) {
304
                // TODO: Pending review
305
                // if ('links' === $type) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
306
                //     $data[$relation['name']]['data'][] = ['id' => $this->getRelationIri($attributeValue)];
0 ignored issues
show
Unused Code Comprehensibility introduced by
76% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
307
308
                //     continue;
309
                // }
310
311
                $data[$relation['name']] = $attributeValue;
312
313
                continue;
314
            }
315
316
            // TODO: Pending review
317
            // Many to many relationship
318
            foreach ($attributeValue as $rel) {
319
                if ('links' === $type) {
320
                    $rel = $this->getRelationIri($rel);
321
                }
322
                $id = ['id' => $rel];
323
324
                if (!is_string($rel)) {
325 View Code Duplication
                    foreach ($rel as $property => $value) {
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...
326
                        $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $property);
327
                        if ($propertyMetadata->isIdentifier()) {
328
                            $identifier = $rel[$property];
329
                        }
330
                    }
331
                    $id = ['id' => $identifier] + $rel;
332
                }
333
334
                if ($relation['type']) {
335
                    $data[$relation['name']]['data'][] = $id + ['type' => $relation['type']];
336
                } else {
337
                    $data[$relation['name']]['data'][] = $id;
338
                }
339
            }
340
        }
341
342
        return $data;
343
    }
344
345
    /**
346
     * Gets the IRI of the given relation.
347
     *
348
     * @param array|string $rel
349
     *
350
     * @return string
351
     */
352
    private function getRelationIri($rel): string
353
    {
354
        return isset($rel['links']['self']) ? $rel['links']['self'] : $rel;
355
    }
356
357
    /**
358
     * Gets the cache key to use.
359
     *
360
     * @param string|null $format
361
     * @param array       $context
362
     *
363
     * @return bool|string
364
     */
365 View Code Duplication
    private function getCacheKey(string $format = null, array $context)
0 ignored issues
show
Bug introduced by
Consider using a different method name as you override a private method of the parent class.

Overwriting private methods is generally fine as long as you also use private visibility. It might still be preferable for understandability to use a different method name.

Loading history...
Duplication introduced by
This method seems to be duplicated in 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...
366
    {
367
        try {
368
            return md5($format.serialize($context));
369
        } catch (\Exception $exception) {
370
            // The context cannot be serialized, skip the cache
371
            return false;
372
        }
373
    }
374
375
    /**
376
     * Denormalizes a resource linkage relation.
377
     *
378
     * See: http://jsonapi.org/format/#document-resource-object-linkage
379
     *
380
     * @param string           $attributeName    [description]
381
     * @param PropertyMetadata $propertyMetadata [description]
382
     * @param string           $className        [description]
383
     * @param [type]           $data             [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
384
     * @param string|null      $format           [description]
385
     * @param array            $context          [description]
386
     *
387
     * @return [type] [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
388
     */
389
    protected function denormalizeRelation(
390
        string $attributeName,
391
        PropertyMetadata $propertyMetadata,
392
        string $className,
393
        $data,
394
        string $format = null,
395
        array $context
396
    ) {
397
        // Null is allowed for empty to-one relationships, see
398
        // http://jsonapi.org/format/#document-resource-object-linkage
399
        if (null === $data['data']) {
400
            return;
401
        }
402
403
        // TODO: Add tests
404
        // An empty array is allowed for empty to-many relationships, see
405
        // http://jsonapi.org/format/#document-resource-object-linkage
406
        if ([] === $data['data']) {
407
            return;
408
        }
409
410
        if (!isset($data['data'])) {
411
            throw new InvalidArgumentException(
412
                'Key \'data\' expected. Only resource linkage currently supported, see: http://jsonapi.org/format/#document-resource-object-linkage'
413
            );
414
        }
415
416
        $data = $data['data'];
417
418
        if (!is_array($data) || 2 !== count($data)) {
419
            throw new InvalidArgumentException(
420
                'Only resource linkage supported currently supported, see: http://jsonapi.org/format/#document-resource-object-linkage'
421
            );
422
        }
423
424
        if (!isset($data['id'])) {
425
            throw new InvalidArgumentException(
426
                'Only resource linkage supported currently supported, see: http://jsonapi.org/format/#document-resource-object-linkage'
427
            );
428
        }
429
430
        return $this->itemDataProvider->getItem(
431
            $this->resourceClassResolver->getResourceClass(null, $className),
432
            $data['id']
433
        );
434
    }
435
436
    /**
437
     * Normalizes a relation as resource linkage relation.
438
     *
439
     * See: http://jsonapi.org/format/#document-resource-object-linkage
440
     *
441
     * For example, it may return the following array:
442
     *
443
     * [
444
     *     'data' => [
445
     *         'type' => 'dummy',
446
     *         'id' => '1'
447
     *     ]
448
     * ]
449
     *
450
     * @param PropertyMetadata $propertyMetadata
451
     * @param mixed            $relatedObject
452
     * @param string           $resourceClass
453
     * @param string|null      $format
454
     * @param array            $context
455
     *
456
     * @return string|array
457
     */
458
    protected function normalizeRelation(
459
        PropertyMetadata $propertyMetadata,
460
        $relatedObject,
461
        string $resourceClass,
462
        string $format = null,
463
        array $context
464
    ) {
465
        $resourceClass = $this->resourceClassResolver->getResourceClass(
466
            $relatedObject,
467
            null,
468
            true
469
        );
470
471
        $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
472
473
        $identifiers = $this->getIdentifiersFromItem($relatedObject);
474
475
        if (count($identifiers) > 1) {
476
            throw new RuntimeException(sprintf(
477
                'Multiple identifiers are not supported during serialization of relationships (Entity: \'%s\')',
478
                $resourceClass
479
            ));
480
        }
481
482
        return ['data' => [
483
            'type' => $resourceMetadata->getShortName(),
484
            'id' => (string) reset($identifiers),
485
        ]];
486
    }
487
488
    private function getItemIdentifierValue($object, $context, $objectAttributesData)
489
    {
490
        $resourceClass = $this->resourceClassResolver->getResourceClass(
491
            $object,
492
            $context['resource_class'] ?? null,
493
            true
494
        );
495
496
        foreach ($objectAttributesData as $attributeName => $value) {
497
            $propertyMetadata = $this
498
                ->propertyMetadataFactory
499
                ->create($resourceClass, $attributeName);
500
501
            if ($propertyMetadata->isIdentifier()) {
502
                return $objectAttributesData[$attributeName];
503
            }
504
        }
505
506
        return null;
507
    }
508
509
    /**
510
     * Find identifiers from an Item (Object).
511
     *
512
     * Taken from ApiPlatform\Core\Bridge\Symfony\Routing\IriConverter
513
     *
514
     * TODO: Review if this would be useful if defined somewhere else
515
     *
516
     * @param object $item
517
     *
518
     * @throws RuntimeException
519
     *
520
     * @return array
521
     */
522 View Code Duplication
    private function getIdentifiersFromItem($item): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
523
    {
524
        $identifiers = [];
525
        $resourceClass = $this->getObjectClass($item);
526
527
        foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
528
            $propertyMetadata = $this
529
                ->propertyMetadataFactory
530
                ->create($resourceClass, $propertyName);
531
532
            $identifier = $propertyMetadata->isIdentifier();
533
            if (null === $identifier || false === $identifier) {
534
                continue;
535
            }
536
537
            $identifiers[$propertyName] = $this
538
                ->propertyAccessor
539
                ->getValue($item, $propertyName);
540
541
            if (!is_object($identifiers[$propertyName])) {
542
                continue;
543
            }
544
545
            $relatedResourceClass = $this->getObjectClass($identifiers[$propertyName]);
546
            $relatedItem = $identifiers[$propertyName];
547
548
            unset($identifiers[$propertyName]);
549
550
            foreach (
551
                $this
552
                    ->propertyNameCollectionFactory
553
                    ->create($relatedResourceClass)
554
                    as $relatedPropertyName
555
            ) {
556
                $propertyMetadata = $this
557
                    ->propertyMetadataFactory
558
                    ->create($relatedResourceClass, $relatedPropertyName);
559
560
                if ($propertyMetadata->isIdentifier()) {
561
                    if (isset($identifiers[$propertyName])) {
562
                        throw new RuntimeException(sprintf(
563
                            'Composite identifiers not supported in "%s" through relation "%s" of "%s" used as identifier',
564
                            $relatedResourceClass,
565
                            $propertyName,
566
                            $resourceClass
567
                        ));
568
                    }
569
570
                    $identifiers[$propertyName] = $this
571
                        ->propertyAccessor
572
                        ->getValue(
573
                            $relatedItem,
574
                            $relatedPropertyName
575
                        );
576
                }
577
            }
578
579
            if (!isset($identifiers[$propertyName])) {
580
                throw new RuntimeException(sprintf(
581
                    'No identifier found in "%s" through relation "%s" of "%s" used as identifier',
582
                    $relatedResourceClass,
583
                    $propertyName,
584
                    $resourceClass
585
                ));
586
            }
587
        }
588
589
        return $identifiers;
590
    }
591
}
592