Automatically detect the discriminator type of attachments and parameters based on the chosen element IRI

Related to issue #502
This commit is contained in:
Jan Böhmer 2024-03-02 23:14:40 +01:00
parent e843286ea7
commit f3bea68011
26 changed files with 152 additions and 40 deletions

View file

@ -22,7 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\Category; use App\Entity\Parts\Category;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\PriceInformations\Currency; use App\Entity\PriceInformations\Currency;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\Footprint; use App\Entity\Parts\Footprint;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\UserSystem\Group; use App\Entity\UserSystem\Group;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -42,7 +42,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\LabelSystem\LabelProfile; use App\Entity\LabelSystem\LabelProfile;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Manufacturer;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -22,9 +22,8 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\MeasurementUnit;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,14 +23,11 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use App\Serializer\TypeOverride\TypeOverridableSerializer; use App\Serializer\TypeOverride\TypeOverridableSerializer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Attribute\Context; 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. * A attachment attached to a part element.

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\Project;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\StorageLocation; use App\Entity\Parts\StorageLocation;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\Parts\Supplier; use App\Entity\Parts\Supplier;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use App\Entity\UserSystem\User; use App\Entity\UserSystem\User;
use App\Serializer\OverrideClassDenormalizer; use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Attachments\AttachmentType; use App\Entity\Attachments\AttachmentType;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\Category; use App\Entity\Parts\Category;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\PriceInformations\Currency; use App\Entity\PriceInformations\Currency;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\Footprint; use App\Entity\Parts\Footprint;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\UserSystem\Group; use App\Entity\UserSystem\Group;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Manufacturer;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\MeasurementUnit;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\Project;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\StorageLocation; use App\Entity\Parts\StorageLocation;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -41,10 +41,10 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use App\Repository\ParameterRepository;
use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\Supplier; use App\Entity\Parts\Supplier;
use App\Serializer\OverrideClassDenormalizer; use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\Context;

View file

@ -0,0 +1,116 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Serializer\APIPlatform;
use ApiPlatform\Api\IriConverterInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use App\Entity\Attachments\Attachment;
use App\Entity\Parameters\AbstractParameter;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
/**
* The purpose of this normalizer is to automatically add the _type discriminator field for the Attachment and AbstractParameter classes
* based on the element IRI.
* So that for a request pointing for a part element, an PartAttachment is automatically created.
* This highly improves UX and is the expected behavior.
*/
class DetermineTypeFromElementIRIDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface
{
const SUPPORTED_CLASSES = [
Attachment::class,
AbstractParameter::class
];
use DenormalizerAwareTrait;
public function __construct(private readonly IriConverterInterface $iriConverter, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory)
{
}
/**
* This functions add the _type discriminator to the input array if necessary automatically from the given element IRI.
* @param array $input
* @param Operation $operation
* @return array
* @throws \ApiPlatform\Metadata\Exception\ResourceClassNotFoundException
*/
private function addTypeDiscriminatorIfNecessary(array $input, Operation $operation): array
{
//We only want to modify POST requests
if (!$operation instanceof Post) {
return $input;
}
//Ignore if the _type variable is already set
if (isset($input['_type'])) {
return $input;
}
if (!isset($input['element']) || !is_string($input['element'])) {
return $input;
}
//Retrieve the element
$element = $this->iriConverter->getResourceFromIri($input['element']);
//Retrieve the short name of the operation
$type = $this->resourceMetadataCollectionFactory->create($element::class)->getOperation()->getShortName();
$input['_type'] = $type;
return $input;
}
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): object
{
//If we are on API platform, we want to add the type discriminator if necessary
if (!isset($data['_type']) && isset($context['operation'])) {
$data = $this->addTypeDiscriminatorIfNecessary($data, $context['operation']);
}
return $this->denormalizer->denormalize($data, $type, $format, $context);
}
public function supportsDenormalization(mixed $data, string $type, ?string $format = null)
{
//Only denormalize if the _type discriminator is not set and the class is supported
return is_array($data) && !isset($data['_type']) && in_array($type, self::SUPPORTED_CLASSES, true);
}
public function getSupportedTypes(?string $format): array
{
$tmp = [];
foreach (self::SUPPORTED_CLASSES as $class) {
$tmp[$class] = false;
}
return $tmp;
}
}

View file

@ -21,7 +21,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Serializer; namespace App\Serializer\APIPlatform;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;