Use sql statements for transferData method and fix updateReferences method

This commit is contained in:
Julie Lenaerts 2025-02-10 17:32:19 +01:00
parent bf14c92567
commit 2185791665

View File

@ -11,8 +11,12 @@ declare(strict_types=1);
namespace Chill\ThirdPartyBundle\Service; namespace Chill\ThirdPartyBundle\Service;
use Chill\MainBundle\Entity\Address;
use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
class ThirdpartyMergeService class ThirdpartyMergeService
{ {
@ -26,96 +30,107 @@ class ThirdpartyMergeService
// Update linked entities // Update linked entities
$this->updateReferences($toKeep, $toDelete); $this->updateReferences($toKeep, $toDelete);
// Safely remove the old ThirdParty
$this->em->remove($toDelete); $this->em->remove($toDelete);
$this->em->getConnection()->executeQuery('REFRESH MATERIALIZED VIEW view_chill_main_address_geographical_unit');
$this->em->flush(); $this->em->flush();
} }
/**
* @throws Exception
*/
private function transferData(ThirdParty $toKeep, ThirdParty $toDelete): void private function transferData(ThirdParty $toKeep, ThirdParty $toDelete): void
{ {
$excludedProperties = ['id', 'createdAt']; $conn = $this->em->getConnection();
$reflection = new \ReflectionClass(ThirdParty::class);
foreach ($reflection->getProperties() as $property) { $columns = ['profession', 'firstname', 'name', 'email', 'telephone', 'comment', 'kind', 'contact_data_anonymous', 'types', 'active', 'name_company'];
if (in_array($property->getName(), $excludedProperties, true)) {
continue; $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";
} }
$toKeepValue = $property->getValue($toKeep); // Execute the query
$toDeleteValue = $property->getValue($toDelete); $conn->executeQuery($sqlUpdate, [
'toDelete' => $toDelete->getId(),
if (null === $toKeepValue && null !== $toDeleteValue) { 'toKeep' => $toKeep->getId(),
$property->setValue($toKeep, $toDeleteValue); ]);
} }
if ($toKeepValue instanceof \Doctrine\Common\Collections\Collection $conn->commit();
&& $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 private function updateReferences(ThirdParty $toKeep, ThirdParty $toDelete): void
{ {
$allMeta = $this->em->getMetadataFactory()->getAllMetadata(); $allMeta = $this->em->getMetadataFactory()->getAllMetadata();
$conn = $this->em->getConnection();
foreach ($allMeta as $meta) { foreach ($allMeta as $meta) {
if ($meta->isMappedSuperclass) {
continue;
}
$tableName = $meta->getTableName();
foreach ($meta->getAssociationMappings() as $assoc) { foreach ($meta->getAssociationMappings() as $assoc) {
if (ThirdParty::class !== $assoc['targetEntity']) { if (ThirdParty::class !== $assoc['targetEntity']) {
continue; // Skip unrelated associations continue;
} }
$entityClass = $meta->getName(); if ($assoc['type'] & ClassMetadata::TO_ONE) {
$associationField = $assoc['fieldName'];
if ($assoc['type'] & \Doctrine\ORM\Mapping\ClassMetadata::TO_ONE) { $joinColumn = $meta->getSingleAssociationJoinColumnName($assoc['fieldName']);
// Handle ManyToOne or OneToOne
$qb = $this->em->createQueryBuilder(); if (ThirdParty::class === $assoc['sourceEntity']) {
$qb->update($entityClass, 'e') $sql = "UPDATE chill_3party.{$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete";
->set("e.{$associationField}", ':toKeep') } else {
->where("e.{$associationField} = :toDelete") $sql = "UPDATE {$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete";
->setParameter('toKeep', $toKeep)
->setParameter('toDelete', $toDelete)
->getQuery()
->execute();
} }
if ($assoc['type'] & \Doctrine\ORM\Mapping\ClassMetadata::TO_MANY) { $conn->executeStatement($sql, [
// Handle ManyToMany or OneToMany (inverse side) 'toKeep' => $toKeep->getId(),
$repo = $this->em->getRepository($entityClass); 'toDelete' => $toDelete->getId(),
$linkedEntities = $repo->createQueryBuilder('e') ]);
->join("e.{$associationField}", 't')
->where('t = :toDelete')
->setParameter('toDelete', $toDelete)
->getQuery()
->getResult();
foreach ($linkedEntities as $entity) { if ('parent' === $assoc['fieldName'] && ThirdParty::class === $assoc['targetEntity']) {
$getter = 'get'.ucfirst($associationField); // Refresh $toKeep to sync its children collection
$setter = 'set'.ucfirst($associationField); $this->em->refresh($toKeep);
$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); if ($assoc['type'] & ClassMetadata::TO_MANY) {
if (!isset($assoc['joinTable'])) {
continue;
} }
$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(),
]);
} }
} }
} }