mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
302 lines
10 KiB
PHP
302 lines
10 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 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)
|
|
);
|
|
}
|
|
}
|