From 7f69f21b6469bcb716a829bdb340f5b95ec0177e Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 5 Feb 2025 17:08:11 +0100 Subject: [PATCH] Renamen and reorganize thirdparty merge files --- .../ThirdpartyMergeManager.php | 125 --------------- .../ChillThirdPartyExtension.php | 1 - .../Service/ThirdpartyMergeService.php | 145 ++++++++---------- .../Action/ThirdpartyMergeManagerTest.php | 100 ------------ .../config/services/actions.yaml | 6 - 5 files changed, 65 insertions(+), 312 deletions(-) delete mode 100644 src/Bundle/ChillThirdPartyBundle/Actions/MergeThirdparty/ThirdpartyMergeManager.php delete mode 100644 src/Bundle/ChillThirdPartyBundle/Tests/Action/ThirdpartyMergeManagerTest.php delete mode 100644 src/Bundle/ChillThirdPartyBundle/config/services/actions.yaml diff --git a/src/Bundle/ChillThirdPartyBundle/Actions/MergeThirdparty/ThirdpartyMergeManager.php b/src/Bundle/ChillThirdPartyBundle/Actions/MergeThirdparty/ThirdpartyMergeManager.php deleted file mode 100644 index 941288367..000000000 --- a/src/Bundle/ChillThirdPartyBundle/Actions/MergeThirdparty/ThirdpartyMergeManager.php +++ /dev/null @@ -1,125 +0,0 @@ -transferData($toKeep, $toDelete); - - // Update linked entities - $this->updateReferences($toKeep, $toDelete); - - $this->em->remove($toDelete); - $this->em->flush(); - } - - private function transferData(ThirdParty $toKeep, ThirdParty $toDelete): void - { - $excludedProperties = ['id', 'createdAt']; - $reflection = new \ReflectionClass(ThirdParty::class); - - foreach ($reflection->getProperties() as $property) { - if (in_array($property->getName(), $excludedProperties, true)) { - continue; - } - - $toKeepValue = $property->getValue($toKeep); - $toDeleteValue = $property->getValue($toDelete); - - if (null === $toKeepValue && null !== $toDeleteValue) { - $property->setValue($toKeep, $toDeleteValue); - } - - if ($toKeepValue instanceof \Doctrine\Common\Collections\Collection - && $toDeleteValue instanceof \Doctrine\Common\Collections\Collection) { - foreach ($toDeleteValue as $item) { - if (!$toKeepValue->contains($item)) { - $toKeepValue->add($item); - } - } - } - } - } - - private function updateReferences(ThirdParty $toKeep, ThirdParty $toDelete): void - { - $allMeta = $this->em->getMetadataFactory()->getAllMetadata(); - - foreach ($allMeta as $meta) { - foreach ($meta->getAssociationMappings() as $assoc) { - if (ThirdParty::class !== $assoc['targetEntity']) { - continue; // Skip unrelated associations - } - - $entityClass = $meta->getName(); - $associationField = $assoc['fieldName']; - - if ($assoc['type'] & \Doctrine\ORM\Mapping\ClassMetadata::TO_ONE) { - // Handle ManyToOne or OneToOne - $qb = $this->em->createQueryBuilder(); - $qb->update($entityClass, 'e') - ->set("e.{$associationField}", ':toKeep') - ->where("e.{$associationField} = :toDelete") - ->setParameter('toKeep', $toKeep) - ->setParameter('toDelete', $toDelete) - ->getQuery() - ->execute(); - } - - if ($assoc['type'] & \Doctrine\ORM\Mapping\ClassMetadata::TO_MANY) { - // Handle ManyToMany or OneToMany (inverse side) - $repo = $this->em->getRepository($entityClass); - $linkedEntities = $repo->createQueryBuilder('e') - ->join("e.{$associationField}", 't') - ->where('t = :toDelete') - ->setParameter('toDelete', $toDelete) - ->getQuery() - ->getResult(); - - foreach ($linkedEntities as $entity) { - $getter = 'get'.ucfirst($associationField); - $setter = 'set'.ucfirst($associationField); - $adder = 'add'.ucfirst(rtrim($associationField, 's')); - $remover = 'remove'.ucfirst(rtrim($associationField, 's')); - - if (method_exists($entity, $getter) && method_exists($entity, $setter)) { - // For OneToMany owning side - $collection = $entity->{$getter}(); - if ($collection->contains($toDelete)) { - $collection->removeElement($toDelete); - if (!$collection->contains($toKeep)) { - $collection->add($toKeep); - } - } - } elseif (method_exists($entity, $adder) && method_exists($entity, $remover)) { - // For ManyToMany - $entity->{$remover}($toDelete); - $entity->{$adder}($toKeep); - } - - $this->em->persist($entity); - } - } - } - } - - $this->em->flush(); - } -} diff --git a/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php b/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php index e12807819..475d22049 100644 --- a/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php +++ b/src/Bundle/ChillThirdPartyBundle/DependencyInjection/ChillThirdPartyExtension.php @@ -51,7 +51,6 @@ class ChillThirdPartyExtension extends Extension implements PrependExtensionInte $loader->load('services/serializer.yaml'); $loader->load('services/repository.yaml'); $loader->load('services/doctrineEventListener.yaml'); - $loader->load('services/actions.yaml'); } public function prepend(ContainerBuilder $container) diff --git a/src/Bundle/ChillThirdPartyBundle/Service/ThirdpartyMergeService.php b/src/Bundle/ChillThirdPartyBundle/Service/ThirdpartyMergeService.php index 820ec742e..8c4d0e9ef 100644 --- a/src/Bundle/ChillThirdPartyBundle/Service/ThirdpartyMergeService.php +++ b/src/Bundle/ChillThirdPartyBundle/Service/ThirdpartyMergeService.php @@ -11,12 +11,8 @@ declare(strict_types=1); namespace Chill\ThirdPartyBundle\Service; -use Chill\MainBundle\Entity\Address; use Chill\ThirdPartyBundle\Entity\ThirdParty; -use Doctrine\Common\Collections\Collection; -use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadata; class ThirdpartyMergeService { @@ -30,107 +26,96 @@ class ThirdpartyMergeService // Update linked entities $this->updateReferences($toKeep, $toDelete); - // Safely remove the old ThirdParty $this->em->remove($toDelete); - $this->em->getConnection()->executeQuery('REFRESH MATERIALIZED VIEW view_chill_main_address_geographical_unit'); - $this->em->flush(); } - /** - * @throws Exception - */ private function transferData(ThirdParty $toKeep, ThirdParty $toDelete): void { - $conn = $this->em->getConnection(); + $excludedProperties = ['id', 'createdAt']; + $reflection = new \ReflectionClass(ThirdParty::class); - $columns = ['profession', 'firstname', 'name', 'email', 'telephone', 'comment', 'kind', 'contact_data_anonymous', 'types', 'active', 'name_company']; - - $conn->beginTransaction(); - $metadata = $this->em->getClassMetadata(ThirdParty::class); - - foreach ($columns as $column) { - $columnType = $metadata->getTypeOfField($column); - - if ('string' === $columnType || 'text' === $columnType) { - $sqlUpdate = " - UPDATE chill_3party.third_party - SET {$column} = COALESCE((SELECT {$column} FROM chill_3party.third_party WHERE id = :toDelete), {$column}) - WHERE id = :toKeep AND ({$column} IS NULL OR {$column} = '')"; - } else { - $sqlUpdate = " - UPDATE chill_3party.third_party - SET {$column} = COALESCE((SELECT {$column} FROM chill_3party.third_party WHERE id = :toDelete), {$column}) - WHERE id = :toKeep AND {$column} IS NULL"; + foreach ($reflection->getProperties() as $property) { + if (in_array($property->getName(), $excludedProperties, true)) { + continue; } - // Execute the query - $conn->executeQuery($sqlUpdate, [ - 'toDelete' => $toDelete->getId(), - 'toKeep' => $toKeep->getId(), - ]); - } + $toKeepValue = $property->getValue($toKeep); + $toDeleteValue = $property->getValue($toDelete); - $conn->commit(); + if (null === $toKeepValue && null !== $toDeleteValue) { + $property->setValue($toKeep, $toDeleteValue); + } + + if ($toKeepValue instanceof \Doctrine\Common\Collections\Collection + && $toDeleteValue instanceof \Doctrine\Common\Collections\Collection) { + foreach ($toDeleteValue as $item) { + if (!$toKeepValue->contains($item)) { + $toKeepValue->add($item); + } + } + } + } } private function updateReferences(ThirdParty $toKeep, ThirdParty $toDelete): void { $allMeta = $this->em->getMetadataFactory()->getAllMetadata(); - $conn = $this->em->getConnection(); foreach ($allMeta as $meta) { - if ($meta->isMappedSuperclass) { - continue; - } - - $tableName = $meta->getTableName(); - foreach ($meta->getAssociationMappings() as $assoc) { if (ThirdParty::class !== $assoc['targetEntity']) { - continue; + continue; // Skip unrelated associations } - if ($assoc['type'] & ClassMetadata::TO_ONE) { + $entityClass = $meta->getName(); + $associationField = $assoc['fieldName']; - $joinColumn = $meta->getSingleAssociationJoinColumnName($assoc['fieldName']); - - if (ThirdParty::class === $assoc['sourceEntity']) { - $sql = "UPDATE chill_3party.{$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete"; - } else { - $sql = "UPDATE {$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete"; - } - - $conn->executeStatement($sql, [ - 'toKeep' => $toKeep->getId(), - 'toDelete' => $toDelete->getId(), - ]); - - if ('parent' === $assoc['fieldName'] && ThirdParty::class === $assoc['targetEntity']) { - // Refresh $toKeep to sync its children collection - $this->em->refresh($toKeep); - } + if ($assoc['type'] & \Doctrine\ORM\Mapping\ClassMetadata::TO_ONE) { + // Handle ManyToOne or OneToOne + $qb = $this->em->createQueryBuilder(); + $qb->update($entityClass, 'e') + ->set("e.{$associationField}", ':toKeep') + ->where("e.{$associationField} = :toDelete") + ->setParameter('toKeep', $toKeep) + ->setParameter('toDelete', $toDelete) + ->getQuery() + ->execute(); } - if ($assoc['type'] & ClassMetadata::TO_MANY) { - if (!isset($assoc['joinTable'])) { - continue; + if ($assoc['type'] & \Doctrine\ORM\Mapping\ClassMetadata::TO_MANY) { + // Handle ManyToMany or OneToMany (inverse side) + $repo = $this->em->getRepository($entityClass); + $linkedEntities = $repo->createQueryBuilder('e') + ->join("e.{$associationField}", 't') + ->where('t = :toDelete') + ->setParameter('toDelete', $toDelete) + ->getQuery() + ->getResult(); + + foreach ($linkedEntities as $entity) { + $getter = 'get'.ucfirst($associationField); + $setter = 'set'.ucfirst($associationField); + $adder = 'add'.ucfirst(rtrim($associationField, 's')); + $remover = 'remove'.ucfirst(rtrim($associationField, 's')); + + if (method_exists($entity, $getter) && method_exists($entity, $setter)) { + // For OneToMany owning side + $collection = $entity->{$getter}(); + if ($collection->contains($toDelete)) { + $collection->removeElement($toDelete); + if (!$collection->contains($toKeep)) { + $collection->add($toKeep); + } + } + } elseif (method_exists($entity, $adder) && method_exists($entity, $remover)) { + // For ManyToMany + $entity->{$remover}($toDelete); + $entity->{$adder}($toKeep); + } + + $this->em->persist($entity); } - - $joinTable = $assoc['joinTable']['name']; - - if ($assoc['isOwningSide']) { - $joinColumn = $assoc['joinTable']['inverseJoinColumns'][0]['name']; - } else { - $joinColumn = $assoc['joinTable']['joinColumns'][0]['name']; - } - - $sql = "UPDATE {$joinTable} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete"; - - $conn->executeStatement($sql, [ - 'toKeep' => $toKeep->getId(), - 'toDelete' => $toDelete->getId(), - ]); } } } diff --git a/src/Bundle/ChillThirdPartyBundle/Tests/Action/ThirdpartyMergeManagerTest.php b/src/Bundle/ChillThirdPartyBundle/Tests/Action/ThirdpartyMergeManagerTest.php deleted file mode 100644 index a36a393bc..000000000 --- a/src/Bundle/ChillThirdPartyBundle/Tests/Action/ThirdpartyMergeManagerTest.php +++ /dev/null @@ -1,100 +0,0 @@ -mergeManager = $this->getContainer()->get(ThirdpartyMergeManager::class); - $this->em = $this->getContainer()->get(EntityManagerInterface::class); - $this->connection = $this->em->getConnection(); - - // Start a transaction before each test - $this->connection->beginTransaction(); - } - - protected function tearDown(): void - { - try { - // Rollback the transaction after each test to ensure no data is persisted - $this->connection->rollBack(); - } catch (\Exception $e) { - $this->connection->close(); - } - - parent::tearDown(); - } - - public function testThirdpartyMerge(): void - { - // Arrange: Create Thirdparty entities - $toKeep = new ThirdParty(); - $toKeep->setName('Thirdparty ToKeep'); - $toKeep->setEmail('keep@example.com'); - - $toDelete = new Thirdparty(); - $toDelete->setName('Thirdparty ToDelete'); // This should be ignored - $toDelete->setTelephone(new PhoneNumber('123456789')); - - // Related entities - $activity = new Activity(); - $activity->addThirdParty($toDelete); // This is a Many-to-Many relation - - $personResource = new PersonResource(); - $personResource->setThirdParty($toDelete); // This is a Many-to-One relation - - $this->em->persist($toKeep); - $this->em->persist($toDelete); - $this->em->persist($activity); - $this->em->persist($personResource); - $this->em->flush(); - - // Merge - $this->mergeManager->merge($toKeep, $toDelete); - $this->em->clear(); - - // Verify data was merged correctly - $mergedThirdparty = $this->em->getRepository(Thirdparty::class)->find($toKeep->getId()); - $this->assertNotNull($mergedThirdparty); - $this->assertEquals('Primary Name', $mergedThirdparty->getName(), 'Name should remain unchanged'); - $this->assertEquals('keep@example.com', $mergedThirdparty->getEmail(), 'Email should remain unchanged'); - $this->assertEquals('123456789', $mergedThirdparty->getPhone(), 'Phone should be transferred from Thirdparty ToDelete'); - - // Check that relationships are updated - $updatedActivity = $this->em->getRepository(Activity::class)->find($activity->getId()); - $this->assertTrue( - $updatedActivity->getThirdParties()->contains($mergedThirdparty), - 'Activity should be linked to the merged Thirdparty' - ); - $this->assertFalse( - $updatedActivity->getThirdParties()->exists(fn($key, $tp) => $tp->getId() === $toDelete->getId()), - 'Activity should no longer reference the deleted Thirdparty' - ); - - $updatedPersonResource = $this->em->getRepository(PersonResource::class)->find($personResource->getId()); - $this->assertEquals( - $mergedThirdparty->getId(), - $updatedPersonResource->getThirdParty()->getId(), - 'PersonResource should reference the merged Thirdparty' - ); - - // Ensure the 'toDelete' entity is removed - $deletedThirdparty = $this->em->getRepository(Thirdparty::class)->find($toDelete->getId()); - $this->assertNull($deletedThirdparty, 'The deleted Thirdparty should no longer exist in the database'); - } -} diff --git a/src/Bundle/ChillThirdPartyBundle/config/services/actions.yaml b/src/Bundle/ChillThirdPartyBundle/config/services/actions.yaml deleted file mode 100644 index e65ed6600..000000000 --- a/src/Bundle/ChillThirdPartyBundle/config/services/actions.yaml +++ /dev/null @@ -1,6 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - - Chill\ThirdPartyBundle\Actions\MergeThirdparty\ThirdpartyMergeManager: ~