mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-03-14 11:19:07 +01:00
Correctly denormalize parent-child relationships in import, when only children not parent fields are given
This fixes issue #1272
This commit is contained in:
@@ -42,6 +42,8 @@ class StructuralElementDenormalizer implements DenormalizerInterface, Denormaliz
|
||||
|
||||
private const ALREADY_CALLED = 'STRUCTURAL_DENORMALIZER_ALREADY_CALLED';
|
||||
|
||||
private const PARENT_ELEMENT = 'STRUCTURAL_DENORMALIZER_PARENT_ELEMENT';
|
||||
|
||||
private array $object_cache = [];
|
||||
|
||||
public function __construct(
|
||||
@@ -89,32 +91,55 @@ class StructuralElementDenormalizer implements DenormalizerInterface, Denormaliz
|
||||
|
||||
$context[self::ALREADY_CALLED][] = $data;
|
||||
|
||||
//In the first step, denormalize without children
|
||||
$context_without_children = $context;
|
||||
$context_without_children['groups'] = array_filter(
|
||||
$context_without_children['groups'] ?? [],
|
||||
static fn($group) => $group !== 'include_children',
|
||||
);
|
||||
//Also unset any parent element, to avoid infinite loops. We will set the parent element in the next step, when we denormalize the children
|
||||
unset($context_without_children[self::PARENT_ELEMENT]);
|
||||
/** @var AbstractStructuralDBElement $entity */
|
||||
$entity = $this->denormalizer->denormalize($data, $type, $format, $context_without_children);
|
||||
|
||||
/** @var AbstractStructuralDBElement $deserialized_entity */
|
||||
$deserialized_entity = $this->denormalizer->denormalize($data, $type, $format, $context);
|
||||
//Assign the parent element to the denormalized entity, so it can be used in the denormalization of the children (e.g. for path generation)
|
||||
if (isset($context[self::PARENT_ELEMENT]) && $context[self::PARENT_ELEMENT] instanceof $entity && $entity->getID() === null) {
|
||||
$entity->setParent($context[self::PARENT_ELEMENT]);
|
||||
}
|
||||
|
||||
//Check if we already have the entity in the database (via path)
|
||||
/** @var StructuralDBElementRepository<T> $repo */
|
||||
$repo = $this->entityManager->getRepository($type);
|
||||
$deserialized_entity = $entity;
|
||||
|
||||
$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);
|
||||
$entity = 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
|
||||
//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];
|
||||
$entity = $this->object_cache[$type][$path];
|
||||
} else {
|
||||
//Save the entity in the cache
|
||||
$this->object_cache[$type][$path] = $deserialized_entity;
|
||||
}
|
||||
|
||||
//Save the entity in the cache
|
||||
$this->object_cache[$type][$path] = $deserialized_entity;
|
||||
//In the next step we can denormalize the children, and add our children to the entity.
|
||||
if (in_array('include_children', $context['groups'], true) && isset($data['children']) && is_array($data['children'])) {
|
||||
foreach ($data['children'] as $child_data) {
|
||||
$child_entity = $this->denormalize($child_data, $type, $format, array_merge($context, [self::PARENT_ELEMENT => $entity]));
|
||||
if ($child_entity !== null && !$entity->getChildren()->contains($child_entity)) {
|
||||
$entity->addChild($child_entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//We don't have the entity in the database, so we have to persist it
|
||||
$this->entityManager->persist($deserialized_entity);
|
||||
|
||||
Reference in New Issue
Block a user