|
19 | 19 | use ApiPlatform\Metadata\Util\ResourceClassInfoTrait;
|
20 | 20 | use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
|
21 | 21 | use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface as SerializerClassMetadataFactoryInterface;
|
| 22 | +use Symfony\Component\TypeInfo\Type; |
| 23 | +use Symfony\Component\TypeInfo\Type\CollectionType; |
| 24 | +use Symfony\Component\TypeInfo\Type\CompositeTypeInterface; |
| 25 | +use Symfony\Component\TypeInfo\Type\ObjectType; |
| 26 | +use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; |
22 | 27 |
|
23 | 28 | /**
|
24 | 29 | * Populates read/write and link status using serialization groups.
|
@@ -60,17 +65,24 @@ public function create(string $resourceClass, string $property, array $options =
|
60 | 65 | }
|
61 | 66 |
|
62 | 67 | $propertyMetadata = $this->transformReadWrite($propertyMetadata, $resourceClass, $property, $normalizationGroups, $denormalizationGroups, $ignoredAttributes);
|
63 |
| - $types = $propertyMetadata->getBuiltinTypes() ?? []; |
64 | 68 |
|
65 |
| - if (!$this->isResourceClass($resourceClass) && $types) { |
66 |
| - foreach ($types as $builtinType) { |
67 |
| - if ($builtinType->isCollection()) { |
68 |
| - return $propertyMetadata->withReadableLink(true)->withWritableLink(true); |
69 |
| - } |
| 69 | + $type = $propertyMetadata->getPhpType(); |
| 70 | + if ($type && !$this->isResourceClass($resourceClass)) { |
| 71 | + $typeIsCollection = static function (Type $type) use (&$typeIsCollection): bool { |
| 72 | + return match (true) { |
| 73 | + $type instanceof CollectionType => true, |
| 74 | + $type instanceof WrappingTypeInterface => $type->wrappedTypeIsSatisfiedBy($typeIsCollection), |
| 75 | + $type instanceof CompositeTypeInterface => $type->composedTypesAreSatisfiedBy($typeIsCollection), |
| 76 | + default => false, |
| 77 | + }; |
| 78 | + }; |
| 79 | + |
| 80 | + if ($type->isSatisfiedBy($typeIsCollection)) { |
| 81 | + return $propertyMetadata->withReadableLink(true)->withWritableLink(true); |
70 | 82 | }
|
71 | 83 | }
|
72 | 84 |
|
73 |
| - return $this->transformLinkStatus($propertyMetadata, $normalizationGroups, $denormalizationGroups, $types); |
| 85 | + return $this->transformLinkStatus($propertyMetadata, $normalizationGroups, $denormalizationGroups, $type); |
74 | 86 | }
|
75 | 87 |
|
76 | 88 | /**
|
@@ -112,45 +124,47 @@ private function transformReadWrite(ApiProperty $propertyMetadata, string $resou
|
112 | 124 | * @param string[]|null $normalizationGroups
|
113 | 125 | * @param string[]|null $denormalizationGroups
|
114 | 126 | */
|
115 |
| - private function transformLinkStatus(ApiProperty $propertyMetadata, ?array $normalizationGroups = null, ?array $denormalizationGroups = null, ?array $types = null): ApiProperty |
| 127 | + private function transformLinkStatus(ApiProperty $propertyMetadata, ?array $normalizationGroups = null, ?array $denormalizationGroups = null, ?Type $type = null): ApiProperty |
116 | 128 | {
|
117 | 129 | // No need to check link status if property is not readable and not writable
|
118 | 130 | if (false === $propertyMetadata->isReadable() && false === $propertyMetadata->isWritable()) {
|
119 | 131 | return $propertyMetadata;
|
120 | 132 | }
|
121 | 133 |
|
122 |
| - foreach ($types as $type) { |
123 |
| - if ( |
124 |
| - $type->isCollection() |
125 |
| - && $collectionValueType = $type->getCollectionValueTypes()[0] ?? null |
126 |
| - ) { |
127 |
| - $relatedClass = $collectionValueType->getClassName(); |
128 |
| - } else { |
129 |
| - $relatedClass = $type->getClassName(); |
130 |
| - } |
131 |
| - |
132 |
| - // if property is not a resource relation, don't set link status (as it would have no meaning) |
133 |
| - if (null === $relatedClass || !$this->isResourceClass($relatedClass)) { |
134 |
| - continue; |
135 |
| - } |
| 134 | + if (!$type) { |
| 135 | + return $propertyMetadata; |
| 136 | + } |
136 | 137 |
|
137 |
| - // find the resource class |
138 |
| - // this prevents serializer groups on non-resource child class from incorrectly influencing the decision |
139 |
| - if (null !== $this->resourceClassResolver) { |
140 |
| - $relatedClass = $this->resourceClassResolver->getResourceClass(null, $relatedClass); |
141 |
| - } |
| 138 | + /** @var class-string|null $className */ |
| 139 | + $className = null; |
| 140 | + $typeIsResourceClass = function (Type $type) use (&$typeIsResourceClass, &$className): bool { |
| 141 | + return match (true) { |
| 142 | + $type instanceof CollectionType => $type->getCollectionValueType()->isSatisfiedBy($typeIsResourceClass), |
| 143 | + $type instanceof WrappingTypeInterface => $type->wrappedTypeIsSatisfiedBy($typeIsResourceClass), |
| 144 | + $type instanceof CompositeTypeInterface => $type->composedTypesAreSatisfiedBy($typeIsResourceClass), |
| 145 | + default => $type instanceof ObjectType && $this->isResourceClass($className = $type->getClassName()), |
| 146 | + }; |
| 147 | + }; |
| 148 | + |
| 149 | + // if property is not a resource relation, don't set link status (as it would have no meaning) |
| 150 | + if (!$type->isSatisfiedBy($typeIsResourceClass)) { |
| 151 | + return $propertyMetadata; |
| 152 | + } |
142 | 153 |
|
143 |
| - $relatedGroups = $this->getClassSerializerGroups($relatedClass); |
| 154 | + // find the resource class |
| 155 | + // this prevents serializer groups on non-resource child class from incorrectly influencing the decision |
| 156 | + if (null !== $this->resourceClassResolver) { |
| 157 | + $className = $this->resourceClassResolver->getResourceClass(null, $className); |
| 158 | + } |
144 | 159 |
|
145 |
| - if (null === $propertyMetadata->isReadableLink()) { |
146 |
| - $propertyMetadata = $propertyMetadata->withReadableLink(null !== $normalizationGroups && !empty(array_intersect($normalizationGroups, $relatedGroups))); |
147 |
| - } |
| 160 | + $relatedGroups = $this->getClassSerializerGroups($className); |
148 | 161 |
|
149 |
| - if (null === $propertyMetadata->isWritableLink()) { |
150 |
| - $propertyMetadata = $propertyMetadata->withWritableLink(null !== $denormalizationGroups && !empty(array_intersect($denormalizationGroups, $relatedGroups))); |
151 |
| - } |
| 162 | + if (null === $propertyMetadata->isReadableLink()) { |
| 163 | + $propertyMetadata = $propertyMetadata->withReadableLink(null !== $normalizationGroups && !empty(array_intersect($normalizationGroups, $relatedGroups))); |
| 164 | + } |
152 | 165 |
|
153 |
| - return $propertyMetadata; |
| 166 | + if (null === $propertyMetadata->isWritableLink()) { |
| 167 | + $propertyMetadata = $propertyMetadata->withWritableLink(null !== $denormalizationGroups && !empty(array_intersect($denormalizationGroups, $relatedGroups))); |
154 | 168 | }
|
155 | 169 |
|
156 | 170 | return $propertyMetadata;
|
|
0 commit comments