137 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ThirdPartyBundle\Service;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
class ThirdpartyMergeService
{
public function __construct(private readonly EntityManagerInterface $em) {}
public function merge(ThirdParty $toKeep, ThirdParty $toDelete): void
{
$conn = $this->em->getConnection();
$conn->beginTransaction();
try {
$queries = array_merge(
$this->transferData($toKeep, $toDelete),
$this->updateReferences($toKeep, $toDelete),
$this->removeThirdparty($toDelete)
);
foreach ($queries as $query) {
$conn->executeStatement($query['sql'], $query['params']);
}
$conn->commit();
} catch (\Exception $e) {
$conn->rollBack();
throw $e;
}
}
/**
* @throws Exception
*/
private function transferData(ThirdParty $toKeep, ThirdParty $toDelete): array
{
$queries = [];
$columns = ['profession', 'firstname', 'name', 'email', 'telephone', 'comment', 'kind', 'contact_data_anonymous', 'types', 'active', 'name_company'];
$metadata = $this->em->getClassMetadata(ThirdParty::class);
foreach ($columns as $column) {
$columnType = $metadata->getTypeOfField($column);
$condition = ('string' === $columnType || 'text' === $columnType)
? "({$column} IS NULL OR {$column} = '')"
: "{$column} IS NULL";
$queries[] = [
'sql' => "
UPDATE chill_3party.third_party
SET {$column} = COALESCE((SELECT {$column} FROM chill_3party.third_party WHERE id = :toDelete), {$column})
WHERE id = :toKeep AND {$condition}",
'params' => ['toDelete' => $toDelete->getId(), 'toKeep' => $toKeep->getId()],
];
}
return $queries;
}
private function updateReferences(ThirdParty $toKeep, ThirdParty $toDelete): array
{
$queries = [];
$allMeta = $this->em->getMetadataFactory()->getAllMetadata();
foreach ($allMeta as $meta) {
if ($meta->isMappedSuperclass) {
continue;
}
$tableName = $meta->getTableName();
foreach ($meta->getAssociationMappings() as $assoc) {
if (ThirdParty::class !== $assoc['targetEntity'] && ThirdParty::class !== $assoc['sourceEntity']) {
continue;
}
if (ClassMetadata::TO_ONE === $assoc['type']) {
$joinColumn = $meta->getSingleAssociationJoinColumnName($assoc['fieldName']);
if (ThirdParty::class === $assoc['sourceEntity'] && 'parent_id' !== $joinColumn) {
// TODO what with 'address_id' and 'civility_id'? This condition also contains columns like updatedBy_id which we want to ignore...
continue;
}
$schemaPrefix = (ThirdParty::class === $assoc['sourceEntity'] && 'parent_id' === $joinColumn)
? 'chill_3party.'
: '';
$queries[] = [
'sql' => "UPDATE {$schemaPrefix}{$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete",
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
];
}
if (ClassMetadata::TO_MANY === $assoc['type'] && isset($assoc['joinTable'])) {
$joinTable = $assoc['joinTable']['name'];
$joinColumn = $assoc['joinTable']['joinColumns'][0]['name'];
if ('thirdparty_id' === $joinColumn) {
$queries[] = [
'sql' => "DELETE FROM {$joinTable} WHERE {$joinColumn} = :toDelete",
'params' => ['toDelete' => $toDelete->getId()],
];
} else {
$queries[] = [
'sql' => "UPDATE {$joinTable} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete",
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
];
}
}
}
}
return $queries;
}
public function removeThirdparty(ThirdParty $toDelete): array
{
return [[
'sql' => 'DELETE FROM chill_3party.third_party WHERE id = :toDelete',
'params' => ['toDelete' => $toDelete->getId()],
]];
}
}