diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php index ef06e4d3e..a42cfef7b 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php @@ -16,8 +16,8 @@ use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Form\CreationPersonType; use Chill\PersonBundle\Form\PersonType; use Chill\PersonBundle\Privacy\PrivacyEvent; +use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface; use Chill\PersonBundle\Repository\PersonRepository; -use Chill\PersonBundle\Search\SimilarPersonMatcher; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; @@ -56,11 +56,6 @@ final class PersonController extends AbstractController */ protected $personRepository; - /** - * @var SimilarPersonMatcher - */ - protected $similarPersonMatcher; - /** * @var TranslatorInterface */ @@ -81,8 +76,9 @@ final class PersonController extends AbstractController */ private $validator; + private PersonACLAwareRepositoryInterface $personACLAwareRepository; + public function __construct( - SimilarPersonMatcher $similarPersonMatcher, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher, PersonRepository $personRepository, @@ -90,9 +86,9 @@ final class PersonController extends AbstractController LoggerInterface $logger, ValidatorInterface $validator, EntityManagerInterface $em, - Security $security + Security $security, + PersonACLAwareRepositoryInterface $personACLAwareRepository ) { - $this->similarPersonMatcher = $similarPersonMatcher; $this->translator = $translator; $this->eventDispatcher = $eventDispatcher; $this->configPersonAltNameHelper = $configPersonAltNameHelper; @@ -101,6 +97,7 @@ final class PersonController extends AbstractController $this->validator = $validator; $this->em = $em; $this->security = $security; + $this->personACLAwareRepository = $personACLAwareRepository; } public function editAction($person_id, Request $request) @@ -236,8 +233,8 @@ final class PersonController extends AbstractController $request->getMethod() === Request::METHOD_POST && $form->isValid() ) { - $alternatePersons = $this->similarPersonMatcher - ->matchPerson($person); + $alternatePersons = $this->personACLAwareRepository + ->findMatchingPersons($person); if ( false === $this->isLastPostDataChanges($form, $request, true) diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php index 78745af1d..382ce59b7 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php @@ -14,10 +14,13 @@ namespace Chill\PersonBundle\Repository; use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Search\ParsingException; +use Chill\MainBundle\Search\SearchApi; use Chill\MainBundle\Search\SearchApiQuery; use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Security\Authorization\PersonVoter; +use Chill\PersonBundle\Templating\Entity\PersonRender; +use Chill\PersonBundle\Repository\PersonNotDuplicateRepository; use DateTimeInterface; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\NonUniqueResultException; @@ -42,16 +45,24 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac private Security $security; + private PersonRender $personRender; + + private PersonNotDuplicateRepository $personNotDuplicateRepository; + public function __construct( Security $security, EntityManagerInterface $em, CountryRepository $countryRepository, - AuthorizationHelper $authorizationHelper + AuthorizationHelper $authorizationHelper, + PersonRender $personRender, + PersonNotDuplicateRepository $personNotDuplicateRepository ) { $this->security = $security; $this->em = $em; $this->countryRepository = $countryRepository; $this->authorizationHelper = $authorizationHelper; + $this->personRender = $personRender; + $this->personNotDuplicateRepository = $personNotDuplicateRepository; } public function buildAuthorizedQuery( @@ -322,4 +333,70 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac }, $authorizedCenters) ); } + + + /** + * @throws NonUniqueResultException + * @throws ParsingException + * + * @return array|Person[] + */ + public function findMatchingPersons( + Person $person, + float $precision = 0.15, + string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY + ): array { + $query = $this->matchPerson($person, $precision, $orderBy); + $authorizedQuery = $this->addAuthorizations($query); + + return $this->fetchQueryPerson($authorizedQuery); + } + + public function matchPerson( + Person $person, + float $precision = 0.15, + string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY + ): SearchApiQuery { + + $fullName = $this->personRender->renderString($person, []); + + $query = new SearchApiQuery(); + $query->setFromClause('chill_person_person AS person'); + $query->andWhereClause( + "SIMILARITY(person.fullnameCanonical, UNACCENT(LOWER(?))) >= ?", + [$fullName, $precision] + ); + + if (null !== $person->getId()) { + $query->andWhereClause( + "person.id != ?", + [$person->getId()] + ); + + $notDuplicatePersons = $this->personNotDuplicateRepository->findNotDuplicatePerson($person); + + if (count($notDuplicatePersons)) { + $query->andWhereClause( + "person.id NOT IN (?)", + [$notDuplicatePersons] + ); + } + } + + switch ($orderBy) { + case self::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL: + $query->setSelectPertinence('person.fullnameCanonical'); + break; + + case self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY: + default: + $query->setSelectPertinence( + 'SIMILARITY(person.fullnameCanonical, UNACCENT(LOWER(?)))', + [$fullName] + ); + } + + return $query; + } + } diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepositoryInterface.php index 72d4682c8..757959699 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepositoryInterface.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepositoryInterface.php @@ -17,6 +17,10 @@ use DateTimeInterface; interface PersonACLAwareRepositoryInterface { + public const SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL = 'alphabetical'; + + public const SIMILAR_SEARCH_ORDER_BY_SIMILARITY = 'similarity'; + public function buildAuthorizedQuery( ?string $default = null, ?string $firstname = null, @@ -61,4 +65,13 @@ interface PersonACLAwareRepositoryInterface ?string $phonenumber = null, ?string $city = null ): array; + + /** + * @return array|Person[] + */ + public function findMatchingPersons( + Person $person, + float $precision = 0.15, + string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY + ): array; }