. */ namespace App\Serializer; use App\Entity\Base\AbstractStructuralDBElement; use App\Repository\StructuralDBElementRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; /** * @see \App\Tests\Serializer\StructuralElementDenormalizerTest */ class StructuralElementDenormalizer implements DenormalizerInterface { private array $object_cache = []; public function __construct( private readonly EntityManagerInterface $entityManager, #[Autowire(service: ObjectNormalizer::class)] private readonly DenormalizerInterface $denormalizer) { } public function supportsDenormalization($data, string $type, string $format = null, array $context = []): bool { //Only denormalize if we are doing a file import operation if (!($context['partdb_import'] ?? false)) { return false; } return is_array($data) && is_subclass_of($type, AbstractStructuralDBElement::class) //Only denormalize if we are doing a file import operation && in_array('import', $context['groups'] ?? [], true); } public function denormalize($data, string $type, string $format = null, array $context = []): ?AbstractStructuralDBElement { /** @var AbstractStructuralDBElement $deserialized_entity */ $deserialized_entity = $this->denormalizer->denormalize($data, $type, $format, $context); //Check if we already have the entity in the database (via path) /** @var StructuralDBElementRepository $repo */ $repo = $this->entityManager->getRepository($type); $path = $deserialized_entity->getFullPath(AbstractStructuralDBElement::PATH_DELIMITER_ARROW); $db_elements = $repo->getEntityByPath($path, AbstractStructuralDBElement::PATH_DELIMITER_ARROW); if ($db_elements !== []) { //We already have the entity in the database, so we can return it return end($db_elements); } //Check if we have created the entity in this request before (so we don't create multiple entities for the same path) //Entities get saved in the cache by type and path //We use a different cache for this then the objects created by a string value (saved in repo). However, that should not be a problem //unless the user data has mixed structure between json data and a string path if (isset($this->object_cache[$type][$path])) { return $this->object_cache[$type][$path]; } //Save the entity in the cache $this->object_cache[$type][$path] = $deserialized_entity; //We don't have the entity in the database, so we have to persist it $this->entityManager->persist($deserialized_entity); return $deserialized_entity; } public function getSupportedTypes(): array { //Must be false, because we use in_array in supportsDenormalization return [ AbstractStructuralDBElement::class => false, ]; } }