chill-bundles/src/Bundle/ChillPersonBundle/Search/SimilarPersonMatcher.php

124 lines
4.1 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\Search;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonNotDuplicateRepository;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class SimilarPersonMatcher
{
final public const SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL = 'alphabetical';
final public const SIMILAR_SEARCH_ORDER_BY_SIMILARITY = 'similarity';
/**
* @var AuthorizationHelper
*/
protected $authorizationHelper;
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var TokenStorageInterface
*/
protected $tokenStorage;
public function __construct(
EntityManagerInterface $em,
AuthorizationHelper $authorizationHelper,
TokenStorageInterface $tokenStorage,
protected PersonNotDuplicateRepository $personNotDuplicateRepository,
protected PersonRenderInterface $personRender,
) {
$this->em = $em;
$this->authorizationHelper = $authorizationHelper;
$this->tokenStorage = $tokenStorage;
}
public function matchPerson(
Person $person,
float $precision = 0.30,
string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY,
bool $addYearComparison = false,
) {
$centers = $this->authorizationHelper->getReachableCenters(
$this->tokenStorage->getToken()->getUser(),
PersonVoter::SEE
);
$query = $this->em->createQuery();
$qb = $this->em->createQueryBuilder();
$qb->select('p')
->from(Person::class, 'p')
->join('p.centerHistory', 'center_history')
->where('SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName))) >= :precision')
->andWhere($qb->expr()->in('center_history.center', ':centers'))
->andWhere($qb->expr()->andX(
$qb->expr()->lte('center_history.startDate', 'CURRENT_DATE()'),
$qb->expr()->orX(
$qb->expr()->isNull('center_history.endDate'),
$qb->expr()->gt('center_history.endDate', 'CURRENT_DATE()')
)
))
;
$qb
->setParameter('fullName', $this->personRender->renderString($person, []))
->setParameter('centers', $centers)
->setParameter('precision', $precision);
if (null !== $person->getBirthdate()) {
$qb->andWhere($qb->expr()->orX(
$qb->expr()->eq('p.birthdate', ':personBirthdate'),
$qb->expr()->isNull('p.birthdate')
));
$qb->setParameter('personBirthdate', $person->getBirthdate());
}
if (null !== $person->getId()) {
$qb->andWhere($qb->expr()->neq('p.id', ':personId'));
$qb->setParameter('personId', $person->getId());
$notDuplicatePersons = $this->personNotDuplicateRepository->findNotDuplicatePerson($person);
if (\count($notDuplicatePersons)) {
$qb->andWhere($qb->expr()->notIn('p.id', ':notDuplicatePersons'));
$qb->setParameter('notDuplicatePersons', $notDuplicatePersons);
}
}
switch ($orderBy) {
case self::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL:
$qb->orderBy('p.fullnameCanonical', 'ASC');
break;
case self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY:
default:
$qb->orderBy('SIMILARITY(p.fullnameCanonical, UNACCENT(LOWER(:fullName)))', 'DESC');
$qb->setParameter('fullName', $this->personRender->renderString($person, []));
}
return $qb->getQuery()->getResult();
}
}