vendor/api-platform/core/src/Core/Bridge/Doctrine/Common/PropertyHelperTrait.php line 36

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\Bridge\Doctrine\Common;
  12. use Doctrine\DBAL\Types\Type;
  13. use Doctrine\Persistence\ManagerRegistry;
  14. use Doctrine\Persistence\Mapping\ClassMetadata;
  15. /**
  16. * Helper trait for getting information regarding a property using the resource metadata.
  17. *
  18. * @author Kévin Dunglas <dunglas@gmail.com>
  19. * @author Théo FIDRY <theo.fidry@gmail.com>
  20. * @author Alan Poulain <contact@alanpoulain.eu>
  21. */
  22. trait PropertyHelperTrait
  23. {
  24. abstract protected function getManagerRegistry(): ManagerRegistry;
  25. /**
  26. * Determines whether the given property is mapped.
  27. */
  28. protected function isPropertyMapped(string $property, string $resourceClass, bool $allowAssociation = false): bool
  29. {
  30. if ($this->isPropertyNested($property, $resourceClass)) {
  31. $propertyParts = $this->splitPropertyParts($property, $resourceClass);
  32. $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
  33. $property = $propertyParts['field'];
  34. } else {
  35. $metadata = $this->getClassMetadata($resourceClass);
  36. }
  37. return $metadata->hasField($property) || ($allowAssociation && $metadata->hasAssociation($property));
  38. }
  39. /**
  40. * Determines whether the given property is nested.
  41. */
  42. protected function isPropertyNested(string $property/* , string $resourceClass */): bool
  43. {
  44. if (\func_num_args() > 1) {
  45. $resourceClass = (string) func_get_arg(1);
  46. } else {
  47. if (__CLASS__ !== static::class) { // @phpstan-ignore-line
  48. $r = new \ReflectionMethod($this, __FUNCTION__);
  49. if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
  50. @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), \E_USER_DEPRECATED);
  51. }
  52. }
  53. $resourceClass = null;
  54. }
  55. $pos = strpos($property, '.');
  56. if (false === $pos) {
  57. return false;
  58. }
  59. return null !== $resourceClass && $this->getClassMetadata($resourceClass)->hasAssociation(substr($property, 0, $pos));
  60. }
  61. /**
  62. * Determines whether the given property is embedded.
  63. */
  64. protected function isPropertyEmbedded(string $property, string $resourceClass): bool
  65. {
  66. return str_contains($property, '.') && $this->getClassMetadata($resourceClass)->hasField($property);
  67. }
  68. /**
  69. * Splits the given property into parts.
  70. *
  71. * Returns an array with the following keys:
  72. * - associations: array of associations according to nesting order
  73. * - field: string holding the actual field (leaf node)
  74. */
  75. protected function splitPropertyParts(string $property/* , string $resourceClass */): array
  76. {
  77. $resourceClass = null;
  78. $parts = explode('.', $property);
  79. if (\func_num_args() > 1) {
  80. $resourceClass = func_get_arg(1);
  81. } elseif (__CLASS__ !== static::class) { // @phpstan-ignore-line
  82. $r = new \ReflectionMethod($this, __FUNCTION__);
  83. if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
  84. @trigger_error(sprintf('Method %s() will have a second `$resourceClass` argument in version API Platform 3.0. Not defining it is deprecated since API Platform 2.1.', __FUNCTION__), \E_USER_DEPRECATED);
  85. }
  86. }
  87. if (null === $resourceClass) {
  88. return [
  89. 'associations' => \array_slice($parts, 0, -1),
  90. 'field' => end($parts),
  91. ];
  92. }
  93. $metadata = $this->getClassMetadata($resourceClass);
  94. $slice = 0;
  95. foreach ($parts as $part) {
  96. if ($metadata->hasAssociation($part)) {
  97. $metadata = $this->getClassMetadata($metadata->getAssociationTargetClass($part));
  98. ++$slice;
  99. }
  100. }
  101. if (\count($parts) === $slice) {
  102. --$slice;
  103. }
  104. return [
  105. 'associations' => \array_slice($parts, 0, $slice),
  106. 'field' => implode('.', \array_slice($parts, $slice)),
  107. ];
  108. }
  109. /**
  110. * Gets the Doctrine Type of a given property/resourceClass.
  111. *
  112. * @return string|null
  113. */
  114. protected function getDoctrineFieldType(string $property, string $resourceClass)
  115. {
  116. $propertyParts = $this->splitPropertyParts($property, $resourceClass);
  117. $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
  118. return $metadata->getTypeOfField($propertyParts['field']);
  119. }
  120. /**
  121. * Gets nested class metadata for the given resource.
  122. *
  123. * @param string[] $associations
  124. */
  125. protected function getNestedMetadata(string $resourceClass, array $associations): ClassMetadata
  126. {
  127. $metadata = $this->getClassMetadata($resourceClass);
  128. foreach ($associations as $association) {
  129. if ($metadata->hasAssociation($association)) {
  130. $associationClass = $metadata->getAssociationTargetClass($association);
  131. $metadata = $this->getClassMetadata($associationClass);
  132. }
  133. }
  134. return $metadata;
  135. }
  136. /**
  137. * Gets class metadata for the given resource.
  138. */
  139. protected function getClassMetadata(string $resourceClass): ClassMetadata
  140. {
  141. return $this
  142. ->getManagerRegistry()
  143. ->getManagerForClass($resourceClass)
  144. ->getClassMetadata($resourceClass);
  145. }
  146. }