mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-07-09 18:36:13 +00:00
A new function, findByPhone, has been added to the PersonACLAwareRepository. This function allows searching for people based on their phone numbers. Changes also reflect in the PersonACLAwareRepositoryInterface, and new test cases have been added to the PersonACLAwareRepositoryTest.
327 lines
11 KiB
PHP
327 lines
11 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\Repository;
|
|
|
|
use Chill\MainBundle\Entity\Center;
|
|
use Chill\MainBundle\Repository\CountryRepository;
|
|
use Chill\MainBundle\Search\ParsingException;
|
|
use Chill\MainBundle\Search\SearchApiQuery;
|
|
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
|
use Chill\PersonBundle\Entity\Person;
|
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Doctrine\ORM\NonUniqueResultException;
|
|
use Doctrine\ORM\Query;
|
|
use libphonenumber\PhoneNumber;
|
|
use libphonenumber\PhoneNumberFormat;
|
|
use Symfony\Component\Security\Core\Security;
|
|
|
|
final readonly class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface
|
|
{
|
|
public function __construct(private Security $security, private EntityManagerInterface $em, private CountryRepository $countryRepository, private AuthorizationHelperInterface $authorizationHelper) {}
|
|
|
|
public function buildAuthorizedQuery(
|
|
?string $default = null,
|
|
?string $firstname = null,
|
|
?string $lastname = null,
|
|
?\DateTimeInterface $birthdate = null,
|
|
?\DateTimeInterface $birthdateBefore = null,
|
|
?\DateTimeInterface $birthdateAfter = null,
|
|
?string $gender = null,
|
|
?string $countryCode = null,
|
|
?string $phonenumber = null,
|
|
?string $city = null
|
|
): SearchApiQuery {
|
|
$query = $this->createSearchQuery(
|
|
$default,
|
|
$firstname,
|
|
$lastname,
|
|
$birthdate,
|
|
$birthdateBefore,
|
|
$birthdateAfter,
|
|
$gender,
|
|
$countryCode,
|
|
$phonenumber,
|
|
$city
|
|
);
|
|
|
|
return $this->addAuthorizations($query);
|
|
}
|
|
|
|
public function countBySearchCriteria(
|
|
?string $default = null,
|
|
?string $firstname = null,
|
|
?string $lastname = null,
|
|
?\DateTimeInterface $birthdate = null,
|
|
?\DateTimeInterface $birthdateBefore = null,
|
|
?\DateTimeInterface $birthdateAfter = null,
|
|
?string $gender = null,
|
|
?string $countryCode = null,
|
|
?string $phonenumber = null,
|
|
?string $city = null
|
|
): int {
|
|
$query = $this->buildAuthorizedQuery(
|
|
$default,
|
|
$firstname,
|
|
$lastname,
|
|
$birthdate,
|
|
$birthdateBefore,
|
|
$birthdateAfter,
|
|
$gender,
|
|
$countryCode,
|
|
$phonenumber,
|
|
$city
|
|
);
|
|
|
|
return $this->fetchQueryCount($query);
|
|
}
|
|
|
|
/**
|
|
* Create a search query without ACL.
|
|
*
|
|
* @throws NonUniqueResultException
|
|
* @throws ParsingException
|
|
*/
|
|
public function createSearchQuery(
|
|
?string $default = null,
|
|
?string $firstname = null,
|
|
?string $lastname = null,
|
|
?\DateTimeInterface $birthdate = null,
|
|
?\DateTimeInterface $birthdateBefore = null,
|
|
?\DateTimeInterface $birthdateAfter = null,
|
|
?string $gender = null,
|
|
?string $countryCode = null,
|
|
?string $phonenumber = null,
|
|
?string $city = null
|
|
): SearchApiQuery {
|
|
$query = new SearchApiQuery();
|
|
$query
|
|
->setFromClause('chill_person_person AS person');
|
|
|
|
$pertinence = [];
|
|
$pertinenceArgs = [];
|
|
$andWhereSearchClause = [];
|
|
$andWhereSearchClauseArgs = [];
|
|
|
|
if ('' !== trim((string) $default)) {
|
|
foreach (\explode(' ', (string) $default) as $str) {
|
|
if ('' === trim($str)) {
|
|
continue;
|
|
}
|
|
$pertinence[] =
|
|
'STRICT_WORD_SIMILARITY(LOWER(UNACCENT(?)), person.fullnamecanonical) + '.
|
|
"(person.fullnamecanonical LIKE '%' || LOWER(UNACCENT(?)) || '%')::int + ".
|
|
"(EXISTS (SELECT 1 FROM unnest(string_to_array(fullnamecanonical, ' ')) AS t WHERE starts_with(t, UNACCENT(LOWER(?)))))::int + ".
|
|
'(starts_with(LOWER(UNACCENT(lastname)), UNACCENT(LOWER(?))))::int';
|
|
\array_push($pertinenceArgs, $str, $str, $str, $str);
|
|
|
|
$andWhereSearchClause[] =
|
|
'(LOWER(UNACCENT(?)) <<% person.fullnamecanonical OR '.
|
|
"person.fullnamecanonical LIKE '%' || LOWER(UNACCENT(?)) || '%' )";
|
|
\array_push($andWhereSearchClauseArgs, $str, $str);
|
|
}
|
|
|
|
$query->andWhereClause(
|
|
\implode(' AND ', $andWhereSearchClause),
|
|
$andWhereSearchClauseArgs
|
|
);
|
|
} else {
|
|
$pertinence = ['1'];
|
|
$pertinenceArgs = [];
|
|
}
|
|
$query
|
|
->setSelectPertinence(\implode(' + ', $pertinence), $pertinenceArgs);
|
|
|
|
if (null !== $birthdate) {
|
|
$query->andWhereClause(
|
|
'person.birthdate = ?::date',
|
|
[$birthdate->format('Y-m-d')]
|
|
);
|
|
}
|
|
|
|
if (null !== $firstname) {
|
|
$query->andWhereClause(
|
|
"UNACCENT(LOWER(person.firstname)) LIKE '%' || UNACCENT(LOWER(?)) || '%'",
|
|
[$firstname]
|
|
);
|
|
}
|
|
|
|
if (null !== $lastname) {
|
|
$query->andWhereClause(
|
|
"UNACCENT(LOWER(person.lastname)) LIKE '%' || UNACCENT(LOWER(?)) || '%'",
|
|
[$lastname]
|
|
);
|
|
}
|
|
|
|
if (null !== $birthdateBefore) {
|
|
$query->andWhereClause(
|
|
'person.birthdate <= ?::date',
|
|
[$birthdateBefore->format('Y-m-d')]
|
|
);
|
|
}
|
|
|
|
if (null !== $birthdateAfter) {
|
|
$query->andWhereClause(
|
|
'person.birthdate >= ?::date',
|
|
[$birthdateAfter->format('Y-m-d')]
|
|
);
|
|
}
|
|
|
|
if (null !== $phonenumber) {
|
|
$query->andWhereClause(
|
|
"person.phonenumber LIKE '%' || ? || '%' OR person.mobilenumber LIKE '%' || ? || '%' OR pp.phonenumber LIKE '%' || ? || '%'",
|
|
[$phonenumber, $phonenumber, $phonenumber]
|
|
);
|
|
$query->setFromClause($query->getFromClause().' LEFT JOIN chill_person_phone pp ON pp.person_id = person.id');
|
|
}
|
|
|
|
if (null !== $city) {
|
|
$query->setFromClause($query->getFromClause().' '.
|
|
'JOIN view_chill_person_current_address vcpca ON vcpca.person_id = person.id '.
|
|
'JOIN chill_main_address cma ON vcpca.address_id = cma.id '.
|
|
'JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id');
|
|
|
|
foreach (\explode(' ', $city) as $cityStr) {
|
|
$query->andWhereClause(
|
|
"(UNACCENT(LOWER(cmpc.label)) LIKE '%' || UNACCENT(LOWER(?)) || '%' OR cmpc.code LIKE '%' || UNACCENT(LOWER(?)) || '%')",
|
|
[$cityStr, $city]
|
|
);
|
|
}
|
|
}
|
|
|
|
if (null !== $countryCode) {
|
|
$query->setFromClause($query->getFromClause().' JOIN country ON person.nationality_id = country.id');
|
|
$query->andWhereClause('country.countrycode = UPPER(?)', [$countryCode]);
|
|
}
|
|
|
|
if (null !== $gender) {
|
|
$query->andWhereClause('person.gender = ?', [$gender]);
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
public function fetchQueryCount(SearchApiQuery $query): int
|
|
{
|
|
$rsm = new Query\ResultSetMapping();
|
|
$rsm->addScalarResult('c', 'c');
|
|
|
|
$nql = $this->em->createNativeQuery($query->buildQuery(true), $rsm);
|
|
$nql->setParameters($query->buildParameters(true));
|
|
|
|
return $nql->getSingleScalarResult();
|
|
}
|
|
|
|
/**
|
|
* @return array|Person[]
|
|
*/
|
|
public function fetchQueryPerson(SearchApiQuery $query, ?int $start = 0, ?int $limit = 50): array
|
|
{
|
|
$rsm = new Query\ResultSetMappingBuilder($this->em);
|
|
$rsm->addRootEntityFromClassMetadata(Person::class, 'person');
|
|
|
|
$query->addSelectClause($rsm->generateSelectClause());
|
|
|
|
$nql = $this->em->createNativeQuery(
|
|
$query->buildQuery().' ORDER BY pertinence DESC OFFSET ? LIMIT ?',
|
|
$rsm
|
|
)->setParameters(\array_merge($query->buildParameters(), [$start, $limit]));
|
|
|
|
return $nql->getResult();
|
|
}
|
|
|
|
/**
|
|
* @return array|Person[]
|
|
*
|
|
* @throws NonUniqueResultException
|
|
* @throws ParsingException
|
|
*/
|
|
public function findBySearchCriteria(
|
|
int $start,
|
|
int $limit,
|
|
bool $simplify = false,
|
|
?string $default = null,
|
|
?string $firstname = null,
|
|
?string $lastname = null,
|
|
?\DateTimeInterface $birthdate = null,
|
|
?\DateTimeInterface $birthdateBefore = null,
|
|
?\DateTimeInterface $birthdateAfter = null,
|
|
?string $gender = null,
|
|
?string $countryCode = null,
|
|
?string $phonenumber = null,
|
|
?string $city = null
|
|
): array {
|
|
$query = $this->buildAuthorizedQuery(
|
|
$default,
|
|
$firstname,
|
|
$lastname,
|
|
$birthdate,
|
|
$birthdateBefore,
|
|
$birthdateAfter,
|
|
$gender,
|
|
$countryCode,
|
|
$phonenumber,
|
|
$city
|
|
);
|
|
|
|
return $this->fetchQueryPerson($query);
|
|
}
|
|
|
|
private function addAuthorizations(SearchApiQuery $query): SearchApiQuery
|
|
{
|
|
$authorizedCenters = $this->authorizationHelper
|
|
->getReachableCenters($this->security->getUser(), PersonVoter::SEE);
|
|
|
|
if ([] === $authorizedCenters) {
|
|
return $query->andWhereClause('FALSE = TRUE', []);
|
|
}
|
|
|
|
return $query
|
|
->setFromClause($query->getFromClause().' JOIN view_chill_person_person_center_history_current vcppchc ON vcppchc.person_id = person.id', $query->getFromParams())
|
|
->andWhereClause(
|
|
strtr(
|
|
'vcppchc.center_id IN ({{ center_ids }})',
|
|
[
|
|
'{{ center_ids }}' => \implode(
|
|
', ',
|
|
\array_fill(0, \count($authorizedCenters), '?')
|
|
),
|
|
]
|
|
),
|
|
\array_map(static fn (Center $c) => $c->getId(), $authorizedCenters)
|
|
);
|
|
}
|
|
|
|
public function findByPhone(PhoneNumber $phoneNumber, int $start = 0, int $limit = 20): array
|
|
{
|
|
$authorizedCenters = $this->authorizationHelper
|
|
->getReachableCenters($this->security->getUser(), PersonVoter::SEE);
|
|
|
|
if ([] === $authorizedCenters) {
|
|
return [];
|
|
}
|
|
|
|
$util = \libphonenumber\PhoneNumberUtil::getInstance();
|
|
|
|
return $this->em->createQuery(
|
|
'SELECT p FROM '.Person::class.' p LEFT JOIN p.otherPhoneNumbers opn JOIN p.centerCurrent pcc '.
|
|
'WHERE (p.phonenumber LIKE :phone OR p.mobilenumber LIKE :phone OR opn.phonenumber LIKE :phone) '.
|
|
'AND pcc.center IN (:centers)'
|
|
)
|
|
->setMaxResults($limit)
|
|
->setFirstResult($start)
|
|
->setParameter('phone', $util->format($phoneNumber, PhoneNumberFormat::E164))
|
|
->setParameter('centers', $authorizedCenters)
|
|
->getResult();
|
|
}
|
|
}
|