From 16b3d582425c94a04addedadb9fcf8c018a6d205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 2 Mar 2024 21:21:16 +0100 Subject: [PATCH] Implemented basic discriminator map for Attachment API access. Now we can properly create part attachments using a POST operation --- src/Entity/Attachments/Attachment.php | 5 +- src/Entity/Attachments/PartAttachment.php | 8 +++ src/Serializer/OverrideClassDenormalizer.php | 61 ++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/Serializer/OverrideClassDenormalizer.php diff --git a/src/Entity/Attachments/Attachment.php b/src/Entity/Attachments/Attachment.php index d98cb847..fda0e3a8 100644 --- a/src/Entity/Attachments/Attachment.php +++ b/src/Entity/Attachments/Attachment.php @@ -43,6 +43,7 @@ use App\Validator\Constraints\Selectable; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\SerializedName; +use Symfony\Component\Serializer\Attribute\DiscriminatorMap; use Symfony\Component\Validator\Constraints as Assert; use function in_array; use InvalidArgumentException; @@ -72,7 +73,7 @@ use LogicException; operations: [ new Get(security: 'is_granted("read", object)'), new GetCollection(security: 'is_granted("@attachments.list_attachments")'), - //new Post(securityPostDenormalize: 'is_granted("create", object)'), + new Post(securityPostDenormalize: 'is_granted("create", object)'), new Patch(security: 'is_granted("edit", object)'), new Delete(security: 'is_granted("delete", object)'), ], @@ -88,6 +89,7 @@ use LogicException; #[ApiFilter(EntityFilter::class, properties: ["attachment_type"])] #[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] +#[DiscriminatorMap(typeProperty: '_type', mapping: ['part' => PartAttachment::class])] abstract class Attachment extends AbstractNamedDBElement { /** @@ -143,6 +145,7 @@ abstract class Attachment extends AbstractNamedDBElement * @phpstan-param T|null $element */ #[Groups(['attachment:read:standalone', 'attachment:write:standalone'])] + #[ApiProperty(writableLink: false)] protected ?AttachmentContainingDBElement $element = null; #[ORM\Column(type: Types::BOOLEAN)] diff --git a/src/Entity/Attachments/PartAttachment.php b/src/Entity/Attachments/PartAttachment.php index f9ca43b3..dbda482c 100644 --- a/src/Entity/Attachments/PartAttachment.php +++ b/src/Entity/Attachments/PartAttachment.php @@ -23,8 +23,14 @@ declare(strict_types=1); namespace App\Entity\Attachments; use App\Entity\Parts\Part; +use App\Serializer\OverrideClassDenormalizer; +use App\Serializer\TypeOverride\TypeOverridableSerializer; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\Serializer\Attribute\Context; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; /** * A attachment attached to a part element. @@ -40,5 +46,7 @@ class PartAttachment extends Attachment */ #[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'attachments')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] + // Set the correct type for the element property to use + #[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => Part::class])] protected ?AttachmentContainingDBElement $element = null; } diff --git a/src/Serializer/OverrideClassDenormalizer.php b/src/Serializer/OverrideClassDenormalizer.php new file mode 100644 index 00000000..e97778c7 --- /dev/null +++ b/src/Serializer/OverrideClassDenormalizer.php @@ -0,0 +1,61 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Serializer; + +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; + +/** + * The idea of this denormalizer is to allow to override the type of the object created using a certain context key. + * This is required to resolve the issue of the serializer/API platform not correctly being able to determine the type + * of the "element" properties of the Attachment and Parameter subclasses. + */ +class OverrideClassDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface +{ + use DenormalizerAwareTrait; + + public const CONTEXT_KEY = '__override_type__'; + + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []) + { + //Deserialize the data with the overridden type + $overrideType = $context[self::CONTEXT_KEY]; + unset($context[self::CONTEXT_KEY]); + + return $this->denormalizer->denormalize($data, $overrideType, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool + { + return isset($context[self::CONTEXT_KEY]); + } + + public function getSupportedTypes(?string $format): array + { + return [ + '*' => false, + ]; + } +} \ No newline at end of file