vendor/api-platform/core/src/Core/Api/IdentifiersExtractor.php line 37

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the API Platform project.
  4. *
  5. * (c) Kévin Dunglas <dunglas@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Core\Api;
  12. use ApiPlatform\Api\IdentifiersExtractor as NewIdentifiersExtractor;
  13. use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
  14. use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
  15. use ApiPlatform\Exception\RuntimeException;
  16. use ApiPlatform\Util\ResourceClassInfoTrait;
  17. use Symfony\Component\PropertyAccess\PropertyAccess;
  18. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  19. /**
  20. * {@inheritdoc}
  21. *
  22. * @author Antoine Bluchet <soyuka@gmail.com>
  23. */
  24. final class IdentifiersExtractor implements IdentifiersExtractorInterface
  25. {
  26. use ResourceClassInfoTrait;
  27. private $propertyNameCollectionFactory;
  28. private $propertyMetadataFactory;
  29. private $propertyAccessor;
  30. public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, PropertyAccessorInterface $propertyAccessor = null, ResourceClassResolverInterface $resourceClassResolver = null, bool $metadataBackwardCompatibilityLayer = null)
  31. {
  32. $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
  33. $this->propertyMetadataFactory = $propertyMetadataFactory;
  34. $this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
  35. $this->resourceClassResolver = $resourceClassResolver;
  36. if (null === $this->resourceClassResolver) {
  37. @trigger_error(sprintf('Not injecting %s in the IdentifiersExtractor might introduce cache issues with object identifiers.', ResourceClassResolverInterface::class), \E_USER_DEPRECATED);
  38. }
  39. if ($metadataBackwardCompatibilityLayer) {
  40. trigger_deprecation('api-platform/core', '2.7', sprintf('The service "%s" is deprecated, use %s instead.', self::class, NewIdentifiersExtractor::class));
  41. }
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function getIdentifiersFromResourceClass(string $resourceClass): array
  47. {
  48. $identifiers = [];
  49. foreach ($properties = $this->propertyNameCollectionFactory->create($resourceClass) as $property) {
  50. if ($this->propertyMetadataFactory->create($resourceClass, $property)->isIdentifier() ?? false) {
  51. $identifiers[] = $property;
  52. }
  53. }
  54. if (!$identifiers) {
  55. if (\in_array('id', iterator_to_array($properties), true)) {
  56. return ['id'];
  57. }
  58. throw new RuntimeException(sprintf('No identifier defined in "%s". You should add #[\ApiPlatform\Core\Annotation\ApiProperty(identifier: true)]" on the property identifying the resource."', $resourceClass));
  59. }
  60. return $identifiers;
  61. }
  62. /**
  63. * {@inheritdoc}
  64. */
  65. public function getIdentifiersFromItem($item): array
  66. {
  67. $identifiers = [];
  68. $resourceClass = $this->getResourceClass($item, true);
  69. $identifierProperties = $this->getIdentifiersFromResourceClass($resourceClass);
  70. foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
  71. if (!\in_array($propertyName, $identifierProperties, true)) {
  72. continue;
  73. }
  74. $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
  75. $identifier = $identifiers[$propertyName] = $this->propertyAccessor->getValue($item, $propertyName);
  76. if (!\is_object($identifier)) {
  77. continue;
  78. }
  79. if (null === $relatedResourceClass = $this->getResourceClass($identifier)) {
  80. continue;
  81. }
  82. $relatedItem = $identifier;
  83. unset($identifiers[$propertyName]);
  84. foreach ($this->propertyNameCollectionFactory->create($relatedResourceClass) as $relatedPropertyName) {
  85. $propertyMetadata = $this->propertyMetadataFactory->create($relatedResourceClass, $relatedPropertyName);
  86. if ($propertyMetadata->isIdentifier()) {
  87. if (isset($identifiers[$propertyName])) {
  88. throw new RuntimeException(sprintf('Composite identifiers not supported in "%s" through relation "%s" of "%s" used as identifier', $relatedResourceClass, $propertyName, $resourceClass));
  89. }
  90. $identifiers[$propertyName] = $this->propertyAccessor->getValue($relatedItem, $relatedPropertyName);
  91. }
  92. }
  93. if (!isset($identifiers[$propertyName])) {
  94. throw new RuntimeException(sprintf('No identifier found in "%s" through relation "%s" of "%s" used as identifier', $relatedResourceClass, $propertyName, $resourceClass));
  95. }
  96. }
  97. return $identifiers;
  98. }
  99. }