mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-03-09 23:47:46 +00:00
Merge remote-tracking branch 'origin/ticket-app-master' into migrate_to_sf72
# Conflicts: # .gitlab-ci.yml # composer.json # config/services.yaml # phpunit.xml.dist # src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivity.php # src/Bundle/ChillCalendarBundle/Entity/CancelReason.php # src/Bundle/ChillCalendarBundle/Messenger/Handler/CalendarRemoveHandler.php # src/Bundle/ChillCalendarBundle/RemoteCalendar/DependencyInjection/RemoteCalendarCompilerPass.php # src/Bundle/ChillDocGeneratorBundle/Service/Messenger/OnGenerationFails.php # src/Bundle/ChillJobBundle/src/Entity/Immersion.php # src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php # src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadLocationType.php # src/Bundle/ChillMainBundle/Entity/Location.php # src/Bundle/ChillMainBundle/Routing/MenuComposer.php # src/Bundle/ChillMainBundle/Routing/MenuTwig.php # src/Bundle/ChillMainBundle/Security/PasswordRecover/RecoverPasswordHelper.php # src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php # src/Bundle/ChillMainBundle/Tests/Form/Type/ScopePickerTypeTest.php # src/Bundle/ChillMainBundle/Tests/Services/MenuComposerTest.php # src/Bundle/ChillPersonBundle/Controller/PersonController.php # src/Bundle/ChillPersonBundle/Entity/Person.php # src/Bundle/ChillPersonBundle/Form/DataMapper/PersonAltNameDataMapper.php # src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php # src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php # src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/PersonJsonNormalizerTest.php # src/Bundle/ChillTaskBundle/Form/SingleTaskType.php
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<?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\Identifier;
|
||||
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class PersonIdentifierRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry, private readonly PersonIdentifierManagerInterface $personIdentifierManager)
|
||||
{
|
||||
parent::__construct($registry, PersonIdentifier::class);
|
||||
}
|
||||
|
||||
public function findByDefinitionAndCanonical(PersonIdentifierDefinition $definition, array|string $valueOrCanonical): array
|
||||
{
|
||||
return $this->createQueryBuilder('p')
|
||||
->where('p.definition = :definition')
|
||||
->andWhere('p.canonical = :canonical')
|
||||
->setParameter('definition', $definition)
|
||||
->setParameter(
|
||||
'canonical',
|
||||
is_string($valueOrCanonical) ?
|
||||
$valueOrCanonical :
|
||||
$this->personIdentifierManager->buildWorkerByPersonIdentifierDefinition($definition)->canonicalizeValue($valueOrCanonical),
|
||||
)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,25 @@ use Chill\MainBundle\Search\ParsingException;
|
||||
use Chill\MainBundle\Search\SearchApiQuery;
|
||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierWorker;
|
||||
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\Bundle\SecurityBundle\Security;
|
||||
|
||||
final readonly class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface
|
||||
{
|
||||
public function __construct(private Security $security, private EntityManagerInterface $em, private CountryRepository $countryRepository, private AuthorizationHelperInterface $authorizationHelper) {}
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private EntityManagerInterface $em,
|
||||
private CountryRepository $countryRepository,
|
||||
private AuthorizationHelperInterface $authorizationHelper,
|
||||
private PersonIdentifierManagerInterface $personIdentifierManager,
|
||||
) {}
|
||||
|
||||
public function buildAuthorizedQuery(
|
||||
?string $default = null,
|
||||
@@ -105,6 +115,15 @@ final readonly class PersonACLAwareRepository implements PersonACLAwareRepositor
|
||||
$query
|
||||
->setFromClause('chill_person_person AS person');
|
||||
|
||||
$idDefinitionWorkers = array_map(
|
||||
fn (PersonIdentifierWorker $worker) => $worker->getDefinition()->getId(),
|
||||
array_filter(
|
||||
$this->personIdentifierManager->getWorkers(),
|
||||
fn (PersonIdentifierWorker $worker) => $worker->getDefinition()->isSearchable()
|
||||
)
|
||||
);
|
||||
$idDefinitionWorkerQuestionMarks = implode(', ', array_fill(0, count($idDefinitionWorkers), '?'));
|
||||
|
||||
$pertinence = [];
|
||||
$pertinenceArgs = [];
|
||||
$andWhereSearchClause = [];
|
||||
@@ -122,20 +141,53 @@ final readonly class PersonACLAwareRepository implements PersonACLAwareRepositor
|
||||
'(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);
|
||||
$q = [
|
||||
'LOWER(UNACCENT(?)) <<% person.fullnamecanonical',
|
||||
"person.fullnamecanonical LIKE '%' || LOWER(UNACCENT(?)) || '%' ",
|
||||
];
|
||||
$qArguments = [$str, $str];
|
||||
|
||||
if (count($idDefinitionWorkers) > 0) {
|
||||
$q[] = $mq = "EXISTS (
|
||||
SELECT 1 FROM chill_person_identifier AS identifier
|
||||
WHERE identifier.canonical LIKE LOWER(UNACCENT(?)) || '%' AND identifier.definition_id IN ({$idDefinitionWorkerQuestionMarks})
|
||||
AND person.id = identifier.person_id
|
||||
)";
|
||||
$pertinence[] = "({$mq})::int * 1000000";
|
||||
$qArguments = [...$qArguments, $str, ...$idDefinitionWorkers];
|
||||
$pertinenceArgs = [...$pertinenceArgs, $str, ...$idDefinitionWorkers];
|
||||
}
|
||||
|
||||
$andWhereSearchClause[] = '('.implode(' OR ', $q).')';
|
||||
$andWhereSearchClauseArgs = [...$andWhereSearchClauseArgs, ...$qArguments];
|
||||
}
|
||||
|
||||
$query->andWhereClause(
|
||||
\implode(' AND ', $andWhereSearchClause),
|
||||
$andWhereSearchClauseArgs
|
||||
);
|
||||
} else {
|
||||
$pertinence = ['1'];
|
||||
$pertinenceArgs = [];
|
||||
}
|
||||
|
||||
if (null !== $phonenumber) {
|
||||
$personPhoneClause = "person.phonenumber LIKE '%' || ? || '%' OR person.mobilenumber LIKE '%' || ? || '%' OR EXISTS (SELECT 1 FROM chill_person_phone where person_id = person.id AND phonenumber LIKE '%' || ? || '%')";
|
||||
if (count($andWhereSearchClauseArgs) > 0) {
|
||||
$initialSearchClause = '(('.\implode(' AND ', $andWhereSearchClause).') OR '.$personPhoneClause.')';
|
||||
}
|
||||
$andWhereSearchClauseArgs = [...$andWhereSearchClauseArgs, $phonenumber, $phonenumber, $phonenumber];
|
||||
|
||||
// drastically increase pertinence
|
||||
$pertinence[] = "(person.phonenumber LIKE '%' || ? || '%' OR person.mobilenumber LIKE '%' || ? || '%' OR EXISTS (SELECT 1 FROM chill_person_phone where person_id = person.id AND phonenumber LIKE '%' || ? || '%'))::int * 1000000";
|
||||
$pertinenceArgs = [...$pertinenceArgs, $phonenumber, $phonenumber, $phonenumber];
|
||||
} else {
|
||||
$initialSearchClause = \implode(' AND ', $andWhereSearchClause);
|
||||
}
|
||||
|
||||
if (isset($initialSearchClause)) {
|
||||
$query->andWhereClause(
|
||||
$initialSearchClause,
|
||||
$andWhereSearchClauseArgs
|
||||
);
|
||||
}
|
||||
|
||||
$query
|
||||
->setSelectPertinence(\implode(' + ', $pertinence), $pertinenceArgs);
|
||||
|
||||
@@ -174,14 +226,6 @@ final readonly class PersonACLAwareRepository implements PersonACLAwareRepositor
|
||||
);
|
||||
}
|
||||
|
||||
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 '.
|
||||
@@ -298,4 +342,27 @@ final readonly class PersonACLAwareRepository implements PersonACLAwareRepositor
|
||||
\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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Search\SearchApiQuery;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use libphonenumber\PhoneNumber;
|
||||
|
||||
interface PersonACLAwareRepositoryInterface
|
||||
{
|
||||
@@ -60,4 +61,13 @@ interface PersonACLAwareRepositoryInterface
|
||||
?string $phonenumber = null,
|
||||
?string $city = null,
|
||||
): array;
|
||||
|
||||
/**
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function findByPhone(
|
||||
PhoneNumber $phoneNumber,
|
||||
int $start = 0,
|
||||
int $limit = 20,
|
||||
): array;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ declare(strict_types=1);
|
||||
namespace Chill\PersonBundle\Repository;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\PersonPhone;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use libphonenumber\PhoneNumber;
|
||||
|
||||
class PersonRepository implements ObjectRepository
|
||||
{
|
||||
@@ -29,6 +31,8 @@ class PersonRepository implements ObjectRepository
|
||||
/**
|
||||
* @throws \Doctrine\ORM\NoResultException
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function countByPhone(
|
||||
string $phonenumber,
|
||||
@@ -71,6 +75,8 @@ class PersonRepository implements ObjectRepository
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*
|
||||
* @deprecated Use @see{self::findByPhoneNumber} or use a dedicated method in PersonACLAwareRepository
|
||||
*/
|
||||
public function findByPhone(
|
||||
string $phonenumber,
|
||||
@@ -91,6 +97,25 @@ class PersonRepository implements ObjectRepository
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a person which is associated to the given phonenumber, without restrictions
|
||||
* on any.
|
||||
*
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function findByPhoneNumber(PhoneNumber $phoneNumber, int $firstResult = 0, int $maxResults = 50): array
|
||||
{
|
||||
$qb = $this->repository->createQueryBuilder('p');
|
||||
$qb->select('p');
|
||||
|
||||
$this->searchByPhoneNumbers($qb, $phoneNumber);
|
||||
|
||||
$qb->setFirstResult($firstResult)
|
||||
->setMaxResults($maxResults);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function findOneBy(array $criteria)
|
||||
{
|
||||
return $this->repository->findOneBy($criteria);
|
||||
@@ -109,6 +134,20 @@ class PersonRepository implements ObjectRepository
|
||||
}
|
||||
}
|
||||
|
||||
private function searchByPhoneNumbers(QueryBuilder $qb, PhoneNumber $phoneNumber): void
|
||||
{
|
||||
$qb->setParameter('number', $phoneNumber, 'phone_number');
|
||||
|
||||
$orX = $qb->expr()->orX();
|
||||
$orX->add($qb->expr()->eq('p.mobilenumber', ':number'));
|
||||
$orX->add($qb->expr()->eq('p.phonenumber', ':number'));
|
||||
$orX->add(
|
||||
$qb->expr()->exists('SELECT 1 FROM '.PersonPhone::class.' k WHERE k.phonenumber = :number AND k.person = p')
|
||||
);
|
||||
|
||||
$qb->andWhere($orX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user