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\Serializer; |
15
|
|
|
|
16
|
|
|
use ApiPlatform\Core\Api\IriConverterInterface; |
17
|
|
|
use ApiPlatform\Core\Api\OperationType; |
18
|
|
|
use ApiPlatform\Core\Api\ResourceClassResolverInterface; |
19
|
|
|
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; |
20
|
|
|
use ApiPlatform\Core\Exception\InvalidArgumentException; |
21
|
|
|
use ApiPlatform\Core\Exception\ItemNotFoundException; |
22
|
|
|
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; |
23
|
|
|
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; |
24
|
|
|
use ApiPlatform\Core\Metadata\Property\PropertyMetadata; |
25
|
|
|
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; |
26
|
|
|
use Symfony\Component\PropertyAccess\PropertyAccess; |
27
|
|
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface; |
28
|
|
|
use Symfony\Component\PropertyInfo\Type; |
29
|
|
|
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; |
30
|
|
|
use Symfony\Component\Serializer\NameConverter\NameConverterInterface; |
31
|
|
|
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Base item normalizer. |
35
|
|
|
* |
36
|
|
|
* @author Kévin Dunglas <[email protected]> |
37
|
|
|
*/ |
38
|
|
|
abstract class AbstractItemNormalizer extends AbstractObjectNormalizer |
39
|
|
|
{ |
40
|
|
|
use ContextTrait; |
41
|
|
|
|
42
|
|
|
protected $propertyNameCollectionFactory; |
43
|
|
|
protected $propertyMetadataFactory; |
44
|
|
|
protected $iriConverter; |
45
|
|
|
protected $resourceClassResolver; |
46
|
|
|
protected $propertyAccessor; |
47
|
|
|
protected $localCache = []; |
48
|
|
|
protected $itemDataProvider; |
49
|
|
|
protected $allowPlainIdentifiers; |
50
|
|
|
|
51
|
|
|
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false) |
52
|
|
|
{ |
53
|
|
|
parent::__construct($classMetadataFactory, $nameConverter); |
54
|
|
|
|
55
|
|
|
$this->propertyNameCollectionFactory = $propertyNameCollectionFactory; |
56
|
|
|
$this->propertyMetadataFactory = $propertyMetadataFactory; |
57
|
|
|
$this->iriConverter = $iriConverter; |
58
|
|
|
$this->resourceClassResolver = $resourceClassResolver; |
59
|
|
|
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); |
60
|
|
|
$this->itemDataProvider = $itemDataProvider; |
61
|
|
|
$this->allowPlainIdentifiers = $allowPlainIdentifiers; |
62
|
|
|
|
63
|
|
|
$this->setCircularReferenceHandler(function ($object) { |
64
|
|
|
return $this->iriConverter->getIriFromItem($object); |
65
|
|
|
}); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* {@inheritdoc} |
70
|
|
|
*/ |
71
|
|
|
public function supportsNormalization($data, $format = null) |
72
|
|
|
{ |
73
|
|
|
if (!is_object($data)) { |
74
|
|
|
return false; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
try { |
78
|
|
|
$this->resourceClassResolver->getResourceClass($data); |
79
|
|
|
} catch (InvalidArgumentException $e) { |
80
|
|
|
return false; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
return true; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* {@inheritdoc} |
88
|
|
|
*/ |
89
|
|
|
public function normalize($object, $format = null, array $context = []) |
90
|
|
|
{ |
91
|
|
|
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null, true); |
92
|
|
|
$context = $this->initContext($resourceClass, $context); |
93
|
|
|
$context['api_normalize'] = true; |
94
|
|
|
|
95
|
|
|
if (isset($context['resources'])) { |
96
|
|
|
$resource = $context['iri'] ?? $this->iriConverter->getIriFromItem($object); |
97
|
|
|
$context['resources'][$resource] = $resource; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
return parent::normalize($object, $format, $context); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* {@inheritdoc} |
105
|
|
|
*/ |
106
|
|
|
public function supportsDenormalization($data, $type, $format = null) |
107
|
|
|
{ |
108
|
|
|
return $this->localCache[$type] ?? $this->localCache[$type] = $this->resourceClassResolver->isResourceClass($type); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* {@inheritdoc} |
113
|
|
|
*/ |
114
|
|
|
public function denormalize($data, $class, $format = null, array $context = []) |
115
|
|
|
{ |
116
|
|
|
$context['api_denormalize'] = true; |
117
|
|
|
if (!isset($context['resource_class'])) { |
118
|
|
|
$context['resource_class'] = $class; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
return parent::denormalize($data, $class, $format, $context); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* {@inheritdoc} |
126
|
|
|
* |
127
|
|
|
* Unused in this context. |
128
|
|
|
*/ |
129
|
|
|
protected function extractAttributes($object, $format = null, array $context = []) |
130
|
|
|
{ |
131
|
|
|
return []; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* {@inheritdoc} |
136
|
|
|
*/ |
137
|
|
|
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false) |
138
|
|
|
{ |
139
|
|
|
$options = $this->getFactoryOptions($context); |
140
|
|
|
$propertyNames = $this->propertyNameCollectionFactory->create($context['resource_class'], $options); |
141
|
|
|
|
142
|
|
|
$allowedAttributes = []; |
143
|
|
|
foreach ($propertyNames as $propertyName) { |
144
|
|
|
$propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $propertyName, $options); |
145
|
|
|
|
146
|
|
|
if ( |
147
|
|
|
$this->isAllowedAttribute($classOrObject, $propertyName, null, $context) && |
148
|
|
|
((isset($context['api_normalize']) && $propertyMetadata->isReadable()) || |
149
|
|
|
(isset($context['api_denormalize']) && $propertyMetadata->isWritable())) |
150
|
|
|
) { |
151
|
|
|
$allowedAttributes[] = $propertyName; |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
return $allowedAttributes; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* {@inheritdoc} |
160
|
|
|
*/ |
161
|
|
|
protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = []) |
162
|
|
|
{ |
163
|
|
|
$propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context)); |
164
|
|
|
$type = $propertyMetadata->getType(); |
165
|
|
|
|
166
|
|
|
if (null === $type) { |
167
|
|
|
// No type provided, blindly set the value |
168
|
|
|
$this->setValue($object, $attribute, $value); |
169
|
|
|
|
170
|
|
|
return; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
if (null === $value && $type->isNullable()) { |
174
|
|
|
$this->setValue($object, $attribute, $value); |
175
|
|
|
|
176
|
|
|
return; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
if ( |
180
|
|
|
$type->isCollection() && |
181
|
|
|
null !== ($collectionValueType = $type->getCollectionValueType()) && |
182
|
|
|
null !== $className = $collectionValueType->getClassName() |
183
|
|
|
) { |
184
|
|
|
$this->setValue( |
185
|
|
|
$object, |
186
|
|
|
$attribute, |
187
|
|
|
$this->denormalizeCollection($attribute, $propertyMetadata, $type, $className, $value, $format, $context) |
188
|
|
|
); |
189
|
|
|
|
190
|
|
|
return; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
if (null !== $className = $type->getClassName()) { |
194
|
|
|
$this->setValue( |
195
|
|
|
$object, |
196
|
|
|
$attribute, |
197
|
|
|
$this->denormalizeRelation($attribute, $propertyMetadata, $className, $value, $format, $this->createChildContext($context, $attribute)) |
198
|
|
|
); |
199
|
|
|
|
200
|
|
|
return; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
$this->validateType($attribute, $type, $value, $format); |
204
|
|
|
$this->setValue($object, $attribute, $value); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* Validates the type of the value. Allows using integers as floats for JSON formats. |
209
|
|
|
* |
210
|
|
|
* @param string $attribute |
211
|
|
|
* @param Type $type |
212
|
|
|
* @param mixed $value |
213
|
|
|
* @param string|null $format |
214
|
|
|
* |
215
|
|
|
* @throws InvalidArgumentException |
216
|
|
|
*/ |
217
|
|
|
protected function validateType(string $attribute, Type $type, $value, string $format = null) |
218
|
|
|
{ |
219
|
|
|
$builtinType = $type->getBuiltinType(); |
220
|
|
|
if (Type::BUILTIN_TYPE_FLOAT === $builtinType && null !== $format && false !== strpos($format, 'json')) { |
221
|
|
|
$isValid = is_float($value) || is_int($value); |
222
|
|
|
} else { |
223
|
|
|
$isValid = call_user_func('is_'.$builtinType, $value); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
if (!$isValid) { |
227
|
|
|
throw new InvalidArgumentException(sprintf( |
228
|
|
|
'The type of the "%s" attribute must be "%s", "%s" given.', $attribute, $builtinType, gettype($value) |
229
|
|
|
)); |
230
|
|
|
} |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Denormalizes a collection of objects. |
235
|
|
|
* |
236
|
|
|
* @param string $attribute |
237
|
|
|
* @param PropertyMetadata $propertyMetadata |
238
|
|
|
* @param Type $type |
239
|
|
|
* @param string $className |
240
|
|
|
* @param mixed $value |
241
|
|
|
* @param string|null $format |
242
|
|
|
* @param array $context |
243
|
|
|
* |
244
|
|
|
* @throws InvalidArgumentException |
245
|
|
|
* |
246
|
|
|
* @return array |
247
|
|
|
*/ |
248
|
|
|
protected function denormalizeCollection(string $attribute, PropertyMetadata $propertyMetadata, Type $type, string $className, $value, string $format = null, array $context): array |
249
|
|
|
{ |
250
|
|
|
if (!is_array($value)) { |
251
|
|
|
throw new InvalidArgumentException(sprintf( |
252
|
|
|
'The type of the "%s" attribute must be "array", "%s" given.', $attribute, gettype($value) |
253
|
|
|
)); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$collectionKeyType = $type->getCollectionKeyType(); |
257
|
|
|
$collectionKeyBuiltinType = null === $collectionKeyType ? null : $collectionKeyType->getBuiltinType(); |
258
|
|
|
|
259
|
|
|
$values = []; |
260
|
|
|
foreach ($value as $index => $obj) { |
261
|
|
|
if (null !== $collectionKeyBuiltinType && !call_user_func('is_'.$collectionKeyBuiltinType, $index)) { |
262
|
|
|
throw new InvalidArgumentException(sprintf( |
263
|
|
|
'The type of the key "%s" must be "%s", "%s" given.', |
264
|
|
|
$index, $collectionKeyBuiltinType, gettype($index)) |
265
|
|
|
); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
$values[$index] = $this->denormalizeRelation($attribute, $propertyMetadata, $className, $obj, $format, $this->createChildContext($context, $attribute)); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
return $values; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Denormalizes a relation. |
276
|
|
|
* |
277
|
|
|
* @param string $attributeName |
278
|
|
|
* @param PropertyMetadata $propertyMetadata |
279
|
|
|
* @param string $className |
280
|
|
|
* @param mixed $value |
281
|
|
|
* @param string|null $format |
282
|
|
|
* @param array $context |
283
|
|
|
* |
284
|
|
|
* @throws InvalidArgumentException |
285
|
|
|
* |
286
|
|
|
* @return object|null |
287
|
|
|
*/ |
288
|
|
|
protected function denormalizeRelation(string $attributeName, PropertyMetadata $propertyMetadata, string $className, $value, string $format = null, array $context) |
289
|
|
|
{ |
290
|
|
|
if (is_string($value)) { |
291
|
|
|
try { |
292
|
|
|
return $this->iriConverter->getItemFromIri($value, $context + ['fetch_data' => true]); |
293
|
|
|
} catch (ItemNotFoundException $e) { |
294
|
|
|
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); |
295
|
|
|
} catch (InvalidArgumentException $e) { |
296
|
|
|
// Give a chance to other normalizers (e.g.: DateTimeNormalizer) |
297
|
|
|
} |
298
|
|
|
} |
299
|
|
|
|
300
|
|
View Code Duplication |
if (!$this->resourceClassResolver->isResourceClass($className) || $propertyMetadata->isWritableLink()) { |
|
|
|
|
301
|
|
|
$context['resource_class'] = $className; |
302
|
|
|
|
303
|
|
|
return $this->serializer->denormalize($value, $className, $format, $context); |
|
|
|
|
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
if (!is_array($value)) { |
307
|
|
|
// repeat the code so that IRIs keep working with the json format |
308
|
|
|
if (true === $this->allowPlainIdentifiers && $this->itemDataProvider) { |
309
|
|
|
try { |
310
|
|
|
return $this->itemDataProvider->getItem($className, $value, null, $context + ['fetch_data' => true]); |
311
|
|
|
} catch (ItemNotFoundException $e) { |
312
|
|
|
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); |
313
|
|
|
} catch (InvalidArgumentException $e) { |
314
|
|
|
// Give a chance to other normalizers (e.g.: DateTimeNormalizer) |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
throw new InvalidArgumentException(sprintf( |
319
|
|
|
'Expected IRI or nested document for attribute "%s", "%s" given.', $attributeName, gettype($value) |
320
|
|
|
)); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
throw new InvalidArgumentException(sprintf('Nested documents for attribute "%s" are not allowed. Use IRIs instead.', $attributeName)); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Sets a value of the object using the PropertyAccess component. |
328
|
|
|
* |
329
|
|
|
* @param object $object |
330
|
|
|
* @param string $attributeName |
331
|
|
|
* @param mixed $value |
332
|
|
|
*/ |
333
|
|
|
private function setValue($object, string $attributeName, $value) |
334
|
|
|
{ |
335
|
|
|
try { |
336
|
|
|
$this->propertyAccessor->setValue($object, $attributeName, $value); |
337
|
|
|
} catch (NoSuchPropertyException $exception) { |
338
|
|
|
// Properties not found are ignored |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Gets a valid context for property metadata factories. |
344
|
|
|
* |
345
|
|
|
* @see https://github.com/symfony/symfony/blob/master/src/Symfony/Component/PropertyInfo/Extractor/SerializerExtractor.php |
346
|
|
|
* |
347
|
|
|
* @param array $context |
348
|
|
|
* |
349
|
|
|
* @return array |
350
|
|
|
*/ |
351
|
|
|
protected function getFactoryOptions(array $context): array |
352
|
|
|
{ |
353
|
|
|
$options = []; |
354
|
|
|
|
355
|
|
|
if (isset($context['groups'])) { |
356
|
|
|
$options['serializer_groups'] = $context['groups']; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
if (isset($context['collection_operation_name'])) { |
360
|
|
|
$options['collection_operation_name'] = $context['collection_operation_name']; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
if (isset($context['item_operation_name'])) { |
364
|
|
|
$options['item_operation_name'] = $context['item_operation_name']; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
return $options; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* Creates the context to use when serializing a relation. |
372
|
|
|
* |
373
|
|
|
* @param string $resourceClass |
374
|
|
|
* @param array $context |
375
|
|
|
* |
376
|
|
|
* @return array |
377
|
|
|
* |
378
|
|
|
* @deprecated since version 2.1, to be removed in 3.0. |
379
|
|
|
*/ |
380
|
|
|
protected function createRelationSerializationContext(string $resourceClass, array $context): array |
|
|
|
|
381
|
|
|
{ |
382
|
|
|
@trigger_error(sprintf('The method %s() is deprecated since 2.1 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED); |
383
|
|
|
|
384
|
|
|
return $context; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* {@inheritdoc} |
389
|
|
|
* |
390
|
|
|
* @throws NoSuchPropertyException |
391
|
|
|
*/ |
392
|
|
|
protected function getAttributeValue($object, $attribute, $format = null, array $context = []) |
393
|
|
|
{ |
394
|
|
|
$propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context)); |
395
|
|
|
|
396
|
|
|
try { |
397
|
|
|
$attributeValue = $this->propertyAccessor->getValue($object, $attribute); |
398
|
|
|
} catch (NoSuchPropertyException $e) { |
399
|
|
|
if (null === $propertyMetadata->isChildInherited()) { |
400
|
|
|
throw $e; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
$attributeValue = null; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
$type = $propertyMetadata->getType(); |
407
|
|
|
|
408
|
|
|
if ( |
409
|
|
|
(is_array($attributeValue) || $attributeValue instanceof \Traversable) && |
410
|
|
|
$type && |
411
|
|
|
$type->isCollection() && |
412
|
|
|
($collectionValueType = $type->getCollectionValueType()) && |
413
|
|
|
($className = $collectionValueType->getClassName()) && |
414
|
|
|
$this->resourceClassResolver->isResourceClass($className) |
415
|
|
|
) { |
416
|
|
|
if (isset($context['graphql'])) { |
417
|
|
|
return []; |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
$value = []; |
421
|
|
|
foreach ($attributeValue as $index => $obj) { |
422
|
|
|
$value[$index] = $this->normalizeRelation($propertyMetadata, $obj, $className, $format, $this->createChildContext($context, $attribute)); |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
return $value; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
if ( |
429
|
|
|
$type && |
430
|
|
|
($className = $type->getClassName()) && |
431
|
|
|
$this->resourceClassResolver->isResourceClass($className) |
432
|
|
|
) { |
433
|
|
|
if (isset($context['graphql'])) { |
434
|
|
|
return false; |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
return $this->normalizeRelation($propertyMetadata, $attributeValue, $className, $format, $this->createChildContext($context, $attribute)); |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
unset($context['resource_class']); |
441
|
|
|
|
442
|
|
|
return $this->serializer->normalize($attributeValue, $format, $context); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* Normalizes a relation as an object if is a Link or as an URI. |
447
|
|
|
* |
448
|
|
|
* @param PropertyMetadata $propertyMetadata |
449
|
|
|
* @param mixed $relatedObject |
450
|
|
|
* @param string $resourceClass |
451
|
|
|
* @param string|null $format |
452
|
|
|
* @param array $context |
453
|
|
|
* |
454
|
|
|
* @return string|array |
455
|
|
|
*/ |
456
|
|
|
protected function normalizeRelation(PropertyMetadata $propertyMetadata, $relatedObject, string $resourceClass, string $format = null, array $context) |
457
|
|
|
{ |
458
|
|
|
// On a subresource, we know the value of the identifiers. |
459
|
|
|
// If attributeValue is null, meaning that it hasn't been returned by the DataProvider, get the item Iri |
460
|
|
|
if (null === $relatedObject && isset($context['operation_type']) && OperationType::SUBRESOURCE === $context['operation_type'] && isset($context['subresource_resources'][$resourceClass])) { |
461
|
|
|
return $this->iriConverter->getItemIriFromResourceClass($resourceClass, $context['subresource_resources'][$resourceClass]); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
if (null === $relatedObject || $propertyMetadata->isReadableLink()) { |
465
|
|
|
if (null === $relatedObject) { |
466
|
|
|
unset($context['resource_class']); |
467
|
|
|
} else { |
468
|
|
|
$context['resource_class'] = $resourceClass; |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
return $this->serializer->normalize($relatedObject, $format, $context); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
$iri = $this->iriConverter->getIriFromItem($relatedObject); |
475
|
|
|
if (isset($context['resources'])) { |
476
|
|
|
$context['resources'][$iri] = $iri; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
return $iri; |
480
|
|
|
} |
481
|
|
|
} |
482
|
|
|
|
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.