From a7ea12d07d3e50a81d1adee08d5b2ead2dbd6803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 2 Apr 2023 00:55:20 +0200 Subject: [PATCH] Fixed import errors and reuse existing datastructrues from DB while importing complex data Also now imports should not create duplicate instances of the same data elements. This fixes issue #101. --- .../StructuralElementDenormalizer.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/Serializer/StructuralElementDenormalizer.php diff --git a/src/Serializer/StructuralElementDenormalizer.php b/src/Serializer/StructuralElementDenormalizer.php new file mode 100644 index 00000000..6c5b4062 --- /dev/null +++ b/src/Serializer/StructuralElementDenormalizer.php @@ -0,0 +1,92 @@ +. + */ + +namespace App\Serializer; + +use App\Entity\Base\AbstractStructuralDBElement; +use App\Repository\StructuralDBElementRepository; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; +use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + +class StructuralElementDenormalizer implements ContextAwareDenormalizerInterface, CacheableSupportsMethodInterface +{ + + private NormalizerInterface $normalizer; + private EntityManagerInterface $entityManager; + + private array $object_cache = []; + + public function __construct(ObjectNormalizer $normalizer, EntityManagerInterface $entityManager) + { + $this->normalizer = $normalizer; + $this->entityManager = $entityManager; + } + + public function supportsDenormalization($data, string $type, string $format = null, array $context = []) + { + return is_array($data) + && is_subclass_of($type, AbstractStructuralDBElement::class) + //Only denormalize if we are doing an file import operation + && in_array('import', $context['groups'] ?? []); + } + + public function denormalize($data, string $type, string $format = null, array $context = []) + { + /** @var AbstractStructuralDBElement $deserialized_entity */ + $deserialized_entity = $this->normalizer->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 hasCacheableSupportsMethod(): bool + { + return false; + } +} \ No newline at end of file