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

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. * @author Antoine Bluchet <soyuka@gmail.com>
  21. */
  22. final class IdentifiersExtractor implements IdentifiersExtractorInterface
  23. {
  24. use ResourceClassInfoTrait;
  25. private $propertyNameCollectionFactory;
  26. private $propertyMetadataFactory;
  27. private $propertyAccessor;
  28. public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, PropertyAccessorInterface $propertyAccessor = null, ResourceClassResolverInterface $resourceClassResolver = null, bool $metadataBackwardCompatibilityLayer = null)
  29. {
  30. $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
  31. $this->propertyMetadataFactory = $propertyMetadataFactory;
  32. $this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
  33. $this->resourceClassResolver = $resourceClassResolver;
  34. if (null === $this->resourceClassResolver) {
  35. @trigger_error(sprintf('Not injecting %s in the IdentifiersExtractor might introduce cache issues with object identifiers.', ResourceClassResolverInterface::class), \E_USER_DEPRECATED);
  36. }
  37. if ($metadataBackwardCompatibilityLayer) {
  38. trigger_deprecation('api-platform/core', '2.7', sprintf('The service "%s" is deprecated, use %s instead.', self::class, NewIdentifiersExtractor::class));
  39. }
  40. }
  41. public function getIdentifiersFromResourceClass(string $resourceClass): array
  42. {
  43. $identifiers = [];
  44. foreach ($properties = $this->propertyNameCollectionFactory->create($resourceClass) as $property) {
  45. if ($this->propertyMetadataFactory->create($resourceClass, $property)->isIdentifier() ?? false) {
  46. $identifiers[] = $property;
  47. }
  48. }
  49. if (!$identifiers) {
  50. if (\in_array('id', iterator_to_array($properties), true)) {
  51. return ['id'];
  52. }
  53. 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));
  54. }
  55. return $identifiers;
  56. }
  57. public function getIdentifiersFromItem($item): array
  58. {
  59. $identifiers = [];
  60. $resourceClass = $this->getResourceClass($item, true);
  61. $identifierProperties = $this->getIdentifiersFromResourceClass($resourceClass);
  62. foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) {
  63. if (!\in_array($propertyName, $identifierProperties, true)) {
  64. continue;
  65. }
  66. $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
  67. $identifier = $identifiers[$propertyName] = $this->propertyAccessor->getValue($item, $propertyName);
  68. if (!\is_object($identifier)) {
  69. continue;
  70. }
  71. if (null === $relatedResourceClass = $this->getResourceClass($identifier)) {
  72. continue;
  73. }
  74. $relatedItem = $identifier;
  75. unset($identifiers[$propertyName]);
  76. foreach ($this->propertyNameCollectionFactory->create($relatedResourceClass) as $relatedPropertyName) {
  77. $propertyMetadata = $this->propertyMetadataFactory->create($relatedResourceClass, $relatedPropertyName);
  78. if ($propertyMetadata->isIdentifier()) {
  79. if (isset($identifiers[$propertyName])) {
  80. throw new RuntimeException(sprintf('Composite identifiers not supported in "%s" through relation "%s" of "%s" used as identifier', $relatedResourceClass, $propertyName, $resourceClass));
  81. }
  82. $identifiers[$propertyName] = $this->propertyAccessor->getValue($relatedItem, $relatedPropertyName);
  83. }
  84. }
  85. if (!isset($identifiers[$propertyName])) {
  86. throw new RuntimeException(sprintf('No identifier found in "%s" through relation "%s" of "%s" used as identifier', $relatedResourceClass, $propertyName, $resourceClass));
  87. }
  88. }
  89. return $identifiers;
  90. }
  91. }