chill-bundles/src/Bundle/ChillPersonBundle/Service/AccompanyingPeriodWork/AccompanyingPeriodWorkMergeService.php

214 lines
8.2 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\PersonBundle\Service\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
class AccompanyingPeriodWorkMergeService
{
public function __construct(private readonly EntityManagerInterface $em) {}
/**
* @throws Exception
*/
public function merge(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
{
$conn = $this->em->getConnection();
$conn->beginTransaction();
try {
$queries = array_merge(
$this->updateReferencesSQL($toKeep, $toDelete),
$this->transferWorkflowsSQL($toKeep, $toDelete),
$this->generateStartDateSQL($toDelete, $toKeep),
$this->generateEndDateSQL($toDelete, $toKeep),
$this->generateCommentSQL($toDelete, $toKeep),
$this->removeAccompanyingPeriodWork($toDelete)
);
foreach ($queries as $query) {
dump($query);
$conn->executeStatement($query['sql'], $query['params']);
}
$conn->commit();
} catch (\Exception $e) {
dump($e->getMessage());
$conn->rollBack();
throw $e;
}
}
private function transferWorkflowsSQL(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): array
{
$queries = [];
$queries[] = [
'sql' => "UPDATE chill_main_workflow_entity w
SET relatedentityid = :toKeepId
WHERE w.relatedentityid = :toDeleteId
AND w.relatedentityclass = 'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'",
'params' => ['toKeepId' => $toKeep->getId(), 'toDeleteId' => $toDelete->getId()],
];
return $queries;
}
private function generateStartDateSQL(AccompanyingPeriodWork $toDelete, AccompanyingPeriodWork $toKeep): array
{
$queries = [];
$queries[] = [
'sql' => 'UPDATE chill_person_accompanying_period_work
SET startdate = LEAST(
COALESCE((SELECT startdate FROM chill_person_accompanying_period_work WHERE id = :toDelete), startdate),
startdate
)
WHERE id = :toKeep',
'params' => ['toDelete' => $toDelete->getId(), 'toKeep' => $toKeep->getId()],
];
return $queries;
}
private function generateEndDateSQL(AccompanyingPeriodWork $toDelete, AccompanyingPeriodWork $toKeep): array
{
$queries = [];
$queries[] = [
'sql' => '
UPDATE chill_person_accompanying_period_work
SET enddate =
CASE
WHEN (SELECT enddate FROM chill_person_accompanying_period_work WHERE id = :toDelete) IS NULL
OR enddate IS NULL
THEN NULL
ELSE GREATEST(
COALESCE((SELECT enddate FROM chill_person_accompanying_period_work WHERE id = :toDelete), enddate),
enddate
)
END
WHERE id = :toKeep',
'params' => ['toDelete' => $toDelete->getId(), 'toKeep' => $toKeep->getId()],
];
return $queries;
}
private function generateCommentSQL(AccompanyingPeriodWork $toDelete, AccompanyingPeriodWork $toKeep): array
{
$queries = [];
$queries[] = [
'sql' => "WITH updated_values AS (
SELECT
acpw1.id AS to_update,
acpw1.note || ' ' || acpw2.note AS new_note,
jsonb_set(
acpw1.privatecomment_comments::jsonb,
'{1}',
to_jsonb((acpw1.privatecomment_comments::jsonb->>'1') || ' ' || (acpw2.privatecomment_comments::jsonb->>'1'))
) AS new_privatecomment_comments
FROM
chill_person_accompanying_period_work acpw1,
chill_person_accompanying_period_work acpw2
WHERE
acpw1.id = :toKeep AND
acpw2.id = :toDelete
)
UPDATE chill_person_accompanying_period_work
SET
note = updated_values.new_note,
privatecomment_comments = updated_values.new_privatecomment_comments
FROM
updated_values
WHERE
chill_person_accompanying_period_work.id = updated_values.to_update",
'params' => ['toDelete' => $toDelete->getId(), 'toKeep' => $toKeep->getId()],
];
return $queries;
}
private function updateReferencesSQL(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $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 (AccompanyingPeriodWork::class !== $assoc['targetEntity'] && AccompanyingPeriodWork::class !== $assoc['sourceEntity']) {
continue;
}
if (($assoc['type'] & ClassMetadata::TO_ONE) !== 0) {
$joinColumn = $meta->getSingleAssociationJoinColumnName($assoc['fieldName']);
if (AccompanyingPeriodWorkReferrerHistory::class === $assoc['sourceEntity']) {
$queries[] = [
'sql' => "DELETE FROM {$tableName} WHERE {$joinColumn} = :toDelete",
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
];
}
$queries[] = [
'sql' => "UPDATE {$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete",
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
];
} elseif (8 === $assoc['type'] && isset($assoc['joinTable'])) {
if ($assoc['isOwningSide']) {
dump($assoc);
$joinTable = $assoc['joinTable']['name'];
$joinColumn = $assoc['joinTable']['joinColumns'][0]['name'];
$relationColumn = $assoc['joinTable']['inverseJoinColumns'][0]['name'];
$queries[] = [
'sql' => "
UPDATE {$joinTable} SET {$joinColumn} = :toKeep
WHERE {$joinColumn} = :toDelete
AND NOT EXISTS (
SELECT 1
FROM {$joinTable} AS t2
WHERE t2.{$joinColumn} = :toKeep
AND t2.{$relationColumn} = {$joinTable}.{$relationColumn}
)
",
'params' => ['toDelete' => $toDelete->getId(), 'toKeep' => $toKeep->getId()],
];
$queries[] = [
'sql' => "DELETE FROM {$joinTable} WHERE {$joinColumn} = :toDelete",
'params' => ['toDelete' => $toDelete->getId()],
];
}
}
}
}
return $queries;
}
public function removeAccompanyingPeriodWork(AccompanyingPeriodWork $toDelete): array
{
return [[
'sql' => 'DELETE FROM chill_person_accompanying_period_work WHERE id = :toDelete',
'params' => ['toDelete' => $toDelete->getId()],
]];
}
}