412 lines
13 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\Import;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Chill\PersonBundle\Repository\SocialWork\GoalRepository;
use Chill\PersonBundle\Repository\SocialWork\ResultRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Comparison;
use Doctrine\Persistence\ObjectRepository;
use Exception;
use function count;
final class SocialWorkMetadata implements SocialWorkMetadataInterface
{
private EntityManagerInterface $entityManager;
private EvaluationRepository $evaluationRepository;
private GoalRepository $goalRepository;
private ResultRepository $resultRepository;
private SocialActionRepository $socialActionRepository;
private SocialIssueRepository $socialIssueRepository;
public function __construct(
SocialIssueRepository $socialIssueRepository,
SocialActionRepository $socialActionRepository,
GoalRepository $goalRepository,
ResultRepository $resultRepository,
EvaluationRepository $evaluationRepository,
EntityManagerInterface $entityManager
) {
$this->socialIssueRepository = $socialIssueRepository;
$this->socialActionRepository = $socialActionRepository;
$this->goalRepository = $goalRepository;
$this->resultRepository = $resultRepository;
$this->evaluationRepository = $evaluationRepository;
$this->entityManager = $entityManager;
}
/**
* @throws Exception
*/
public function import(iterable $dataset): bool
{
// Initialisation of the previous result row with the proper data structure.
$result = [
'socialIssues' => [
'socialIssue' => null,
'socialIssueChild' => null,
],
'socialActions' => [
'socialAction' => null,
'socialActionChild' => null,
],
'goal' => null,
'result' => null,
'eval' => null,
];
foreach ($dataset as $key => $row) {
$result = $this
->import1(
$key,
// Columns cleanup before importing data.
array_map(
static fn (string $column): ?string => '' === $column ? null : $column,
array_map('trim', $row)
),
$result
);
}
return true;
}
/**
* @return array<int, object>
*/
private function findByJson(ObjectRepository $repository, string $field, array $jsonCriterias): array
{
$qb = $this
->entityManager
->createQueryBuilder()
->select('s')
->from($repository->getClassName(), 's');
$expr = $qb->expr();
$temporaryJsonCriterias = [];
foreach ($jsonCriterias as $key => $value) {
$temporaryJsonCriterias[] = [$field, $key, $value, sprintf(':placeholder_%s_%s', $field, $key)];
}
$jsonParameters = array_reduce(
$temporaryJsonCriterias,
static function (array $carry, array $row): array {
[,, $value, $placeholder] = $row;
return array_merge(
$carry,
[
$placeholder => sprintf('"%s"', $value),
]
);
},
[]
);
$jsonPredicates = array_map(
static function (array $row) use ($expr): Comparison {
[$field, $key,, $placeholder] = $row;
$left = sprintf(
"GET_JSON_FIELD_BY_KEY(s.%s, '%s')",
$field,
$key
);
return $expr
->eq(
$left,
$placeholder
);
},
$temporaryJsonCriterias
);
return $qb
->select('s')
->where(...$jsonPredicates)
->setParameters($jsonParameters)
->getQuery()
->getResult();
}
/**
* @throws Exception
*
* @return object
*/
private function getOrCreateEntity(ObjectRepository $repository, string $field, array $jsonCriterias = [])
{
$results = $this
->findByJson(
$repository,
$field,
$jsonCriterias
);
if ([] === $results) {
$entity = $repository->getClassName();
return new $entity();
}
if (count($results) === 1) {
return reset($results);
}
throw new Exception(
'Unable to create entity.'
);
}
private function handleEvaluation(?string $evaluationTitle, SocialAction $socialAction): ?Evaluation
{
if (null === $evaluationTitle) {
return null;
}
/** @var Evaluation $eval */
$eval = $this->getOrCreateEntity($this->evaluationRepository, 'title', ['fr' => $evaluationTitle]);
$eval->setTitle(['fr' => $evaluationTitle]);
$socialAction->addEvaluation($eval);
$this->entityManager->persist($eval);
return $eval;
}
private function handleGoal(?string $goalTitle, SocialAction $socialAction): ?Goal
{
if (null === $goalTitle) {
return null;
}
/** @var Goal $goal */
$goal = $this->getOrCreateEntity($this->goalRepository, 'title', ['fr' => $goalTitle]);
$goal->setTitle(['fr' => $goalTitle]);
$socialAction->addGoal($goal);
$goal->addSocialAction($socialAction);
$this->entityManager->persist($socialAction);
$this->entityManager->persist($goal);
return $goal;
}
private function handleResult(?string $resultTitle, SocialAction $socialAction, ?Goal $goal): ?Result
{
if (null === $resultTitle) {
return null;
}
/** @var Result $result */
$result = $this->getOrCreateEntity($this->resultRepository, 'title', ['fr' => $resultTitle]);
$result->setTitle(['fr' => $resultTitle]);
if (null !== $goal) {
$result->addGoal($goal);
$goal->addResult($result);
$this->entityManager->persist($goal);
} else {
$result->addSocialAction($socialAction);
$socialAction->addResult($result);
}
$this->entityManager->persist($result);
$this->entityManager->persist($socialAction);
return $result;
}
/**
* @throws Exception
*
* @return array<string, SocialAction|null>
*/
private function handleSocialAction(
?string $socialActionTitle,
?string $socialActionChildTitle,
SocialIssue $socialIssue,
float $orderingParent,
float $orderingChild,
?SocialAction $previousSocialAction,
?SocialAction $previousSocialActionChild
): array {
if (null === $socialActionTitle) {
return [
'socialAction' => null,
'socialActionChild' => null,
];
}
$return = [];
if (null !== $previousSocialAction && ($previousSocialAction->getTitle()['fr'] === $socialActionTitle)) {
$return = [
'socialAction' => $parent = $previousSocialAction,
];
$parentIsSame = true;
} else {
$return['socialAction'] = $parent = (new SocialAction())->setTitle(['fr' => $socialActionTitle])
->setOrdering($orderingParent);
$parent->setIssue($socialIssue);
$this->entityManager->persist($parent);
$parentIsSame = false;
}
if (null === $socialActionChildTitle) {
$return['socialActionChild'] = null;
} elseif ($parentIsSame && null !== $previousSocialActionChild && $previousSocialActionChild->getTitle()['fr'] === $socialActionChildTitle) {
$return['socialActionChild'] = $previousSocialActionChild;
} else {
$return['socialActionChild'] = $child = (new SocialAction())->setTitle(['fr' => $socialActionChildTitle]);
$child->setParent($parent);
$child->setIssue($socialIssue)->setOrdering($orderingChild);
$this->entityManager->persist($child);
}
return $return;
}
/**
* @throws Exception
*
* @return array<string, SocialIssue|null>
*/
private function handleSocialIssue(
?string $socialIssueTitle,
?string $socialIssueChildTitle,
float $orderingParent,
float $orderingChild,
?SocialIssue $previousSocialIssue,
?SocialIssue $previousSocialIssueChild
): array {
$return = [];
if (null !== $previousSocialIssue && ($previousSocialIssue->getTitle()['fr'] === $socialIssueTitle)) {
$return = [
'socialIssue' => $parent = $previousSocialIssue,
];
$parentIsSame = true;
} elseif (null !== $socialIssueTitle) {
$return['socialIssue'] = $parent = (new SocialIssue())->setTitle(['fr' => $socialIssueTitle])
->setOrdering($orderingParent);
$this->entityManager->persist($parent);
$parentIsSame = false;
} else {
return [
'socialIssue' => null,
'socialIssueChild' => null,
];
}
if ($parentIsSame && null !== $previousSocialIssueChild && ($previousSocialIssueChild->getTitle()['fr'] === $socialIssueChildTitle)) {
$return['socialIssueChild'] = $previousSocialIssueChild;
} elseif (null !== $socialIssueChildTitle) {
$return['socialIssueChild'] = $child = (new SocialIssue())->setTitle(['fr' => $socialIssueChildTitle])
->setOrdering($orderingChild);
$child->setParent($parent);
$this->entityManager->persist($child);
} else {
$return['socialIssueChild'] = null;
}
return $return;
}
/**
* Row Structure:.
*
* Index 0: Parent SocialIssue
* Index 1: Child SocialIssue
* Index 2: Parent SocialAction
* Index 3: Child SocialAction
* Index 4: Goal
* Index 5: Result
* Index 6: Evaluation
*
* @param array<int, ?string> $row
* @param array<string, array<string, <|array|null>|SocialIssue|string, SocialAction|null>|Evaluation|Goal|Result> $previousRow
*
* @throws Exception
*
* @return array<string, array<string, <|array|null>|SocialIssue|string, SocialAction|null>|Evaluation|Goal|Result>
*/
private function import1(int $key, array $row, array $previousRow): array
{
$baseOrdering = $key * 10.0;
$socialIssues = $this
->handleSocialIssue(
$row[0],
$row[1],
$key + 1.0,
$key + 3.0,
$previousRow['socialIssues']['socialIssue'] ?? null,
$previousRow['socialIssues']['socialIssueChild'] ?? null
);
$socialIssue = $socialIssues['socialIssueChild'] ?? $socialIssues['socialIssue'];
if (null === $socialIssue) {
throw new Exception(sprintf("no social issue on row {$key}, values: %s", implode(', ', $row)));
}
$socialActions = $this
->handleSocialAction(
$row[2],
$row[3],
$socialIssue,
$key + 5.0,
$key + 7.0,
$previousRow['socialActions']['socialAction'] ?? null,
$previousRow['socialActions']['socialActionChild'] ?? null
);
$socialAction = $socialActions['socialActionChild'] ?? $socialActions['socialAction'];
if (null !== $socialAction) {
$goal = $this->handleGoal($row[4], $socialAction);
$result = $this->handleResult($row[5], $socialAction, $goal);
$eval = $this->handleEvaluation($row[6], $socialAction);
}
$this->entityManager->flush();
return [
'socialIssues' => $socialIssues,
'socialActions' => $socialActions,
'goal' => $goal ?? null,
'result' => $result ?? null,
'eval' => $eval ?? null,
];
}
}