mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-04-04 20:13:42 +00:00
Add support for parent > child notation in motive import.
- Trimmed labels during motive import to ensure consistent formatting. - Introduced logic to parse and handle "parent > child" syntax in motive names. - Automatically creates and links parent motives if they don't exist. - Added test case to verify parent-child relationship creation and linking.
This commit is contained in:
@@ -54,19 +54,60 @@ final readonly class ImportMotivesFromDirectory
|
||||
throw new \RuntimeException(sprintf('Item %d: missing or invalid "label" (expected array).', $index));
|
||||
}
|
||||
$labelArray = $item['label'];
|
||||
// Trim all labels when they are strings
|
||||
foreach ($labelArray as $k => $v) {
|
||||
if (\is_string($v)) {
|
||||
$labelArray[$k] = trim($v);
|
||||
}
|
||||
}
|
||||
|
||||
$labelForSearch = $labelArray[$lang] ?? null;
|
||||
if (!\is_string($labelForSearch) || '' === trim($labelForSearch)) {
|
||||
throw new \RuntimeException(sprintf('Item %d: missing label for language "%s".', $index, $lang));
|
||||
}
|
||||
|
||||
$existing = $this->motiveRepository->findByLabel($labelForSearch, $lang);
|
||||
// Support parent > child notation in the current language label
|
||||
$parentName = null;
|
||||
$childName = trim($labelForSearch);
|
||||
if (false !== strpos($childName, '>')) {
|
||||
[$parentName, $childName] = array_map('trim', explode('>', $childName, 2));
|
||||
}
|
||||
|
||||
// Find or create the current motive (child or standalone)
|
||||
$existing = $this->motiveRepository->findByLabel($childName, $lang);
|
||||
if (\count($existing) > 1) {
|
||||
throw new \RuntimeException(sprintf('Item %d: multiple motives found with label "%s" for lang "%s".', $index, $labelForSearch, $lang));
|
||||
throw new \RuntimeException(sprintf('Item %d: multiple motives found with label "%s" for lang "%s".', $index, $childName, $lang));
|
||||
}
|
||||
$motive = $existing[0] ?? new Motive();
|
||||
|
||||
// Ensure the stored label for the current language is the child/standalone name (trimmed)
|
||||
$labelArray[$lang] = $childName;
|
||||
$motive->setLabel($labelArray);
|
||||
|
||||
// If a parent is defined, ensure it exists and link it
|
||||
if (null !== $parentName && '' !== $parentName) {
|
||||
$parentCandidates = $this->motiveRepository->findByLabel($parentName, $lang);
|
||||
if (\count($parentCandidates) > 1) {
|
||||
throw new \RuntimeException(sprintf('Item %d: multiple parent motives found with label "%s" for lang "%s".', $index, $parentName, $lang));
|
||||
}
|
||||
$parent = $parentCandidates[0] ?? null;
|
||||
if (null === $parent) {
|
||||
$parent = new Motive();
|
||||
$parentLabel = $labelArray;
|
||||
$parentLabel[$lang] = $parentName;
|
||||
// Make sure all labels are trimmed
|
||||
foreach ($parentLabel as $k => $v) {
|
||||
if (\is_string($v)) {
|
||||
$parentLabel[$k] = trim($v);
|
||||
}
|
||||
}
|
||||
$parent->setLabel($parentLabel);
|
||||
$this->entityManager->persist($parent);
|
||||
}
|
||||
|
||||
$motive->setParent($parent);
|
||||
}
|
||||
|
||||
// urgent: if true => YES, if explicitly false => NO, if absent => leave null
|
||||
if (array_key_exists('urgent', $item)) {
|
||||
$urgent = (bool) $item['urgent'];
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Chill\TicketBundle\Tests\Service\Import;
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Entity\StoredObjectVersion;
|
||||
use Chill\DocStoreBundle\Service\StoredObjectManagerInterface;
|
||||
use Chill\TicketBundle\Entity\Motive;
|
||||
use Chill\TicketBundle\Repository\MotiveRepository;
|
||||
use Chill\TicketBundle\Service\Import\ImportMotivesFromDirectory;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -92,4 +93,71 @@ YAML;
|
||||
// If we reached here, it's a successful smoke test
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testImportWithParentCreatesAndLinks(): void
|
||||
{
|
||||
// 1) Prepare temporary directory with a motives.yaml that declares a parent > child label
|
||||
$tmpBase = sys_get_temp_dir().DIRECTORY_SEPARATOR.'chill_ticket_import_'.bin2hex(random_bytes(6));
|
||||
$this->assertTrue(mkdir($tmpBase, 0777, true));
|
||||
|
||||
$yaml = <<<'YAML'
|
||||
- label:
|
||||
fr: " Parent > Child "
|
||||
YAML;
|
||||
file_put_contents($tmpBase.DIRECTORY_SEPARATOR.'motives.yaml', $yaml);
|
||||
|
||||
// 2) Create repository prophecy: both child and parent are initially missing
|
||||
$repoProphecy = $this->prophesize(MotiveRepository::class);
|
||||
$repoProphecy->findByLabel('Child', 'fr')->willReturn([])->shouldBeCalled();
|
||||
$repoProphecy->findByLabel('Parent', 'fr')->willReturn([])->shouldBeCalled();
|
||||
|
||||
// 3) Capture persisted motives to verify parent/child relation
|
||||
$persistedMotives = [];
|
||||
$emProphecy = $this->prophesize(EntityManagerInterface::class);
|
||||
$emProphecy->persist(Argument::type(Motive::class))
|
||||
->will(function ($args) use (&$persistedMotives) {
|
||||
$persistedMotives[] = $args[0];
|
||||
})
|
||||
->shouldBeCalled();
|
||||
$emProphecy->flush()->shouldBeCalled();
|
||||
|
||||
// StoredObjectManager is unused in this scenario; provide a dummy mock
|
||||
$somMock = $this->createMock(StoredObjectManagerInterface::class);
|
||||
|
||||
// 4) Run the importer
|
||||
$importer = new ImportMotivesFromDirectory(
|
||||
$emProphecy->reveal(),
|
||||
$somMock,
|
||||
$repoProphecy->reveal(),
|
||||
);
|
||||
$importer->import($tmpBase, 'fr');
|
||||
|
||||
// 5) Assertions: we should have at least two motives persisted (parent and child)
|
||||
$this->assertGreaterThanOrEqual(2, \count($persistedMotives));
|
||||
|
||||
// Identify child and parent
|
||||
$child = null;
|
||||
$parent = null;
|
||||
foreach ($persistedMotives as $m) {
|
||||
if (!$m instanceof Motive) {
|
||||
continue;
|
||||
}
|
||||
$label = $m->getLabel();
|
||||
$fr = $label['fr'] ?? null;
|
||||
if ('Child' === $fr) {
|
||||
$child = $m;
|
||||
} elseif ('Parent' === $fr) {
|
||||
$parent = $m;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertInstanceOf(Motive::class, $child, 'Child motive must be created');
|
||||
$this->assertInstanceOf(Motive::class, $parent, 'Parent motive must be created');
|
||||
$this->assertSame($parent, $child->getParent(), 'Child must reference the created parent');
|
||||
$this->assertTrue($parent->isParent(), 'Parent must have at least one child');
|
||||
|
||||
// 6) Cleanup
|
||||
@unlink($tmpBase.DIRECTORY_SEPARATOR.'motives.yaml');
|
||||
@rmdir($tmpBase);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user