security = $security; $this->em = $em; $this->countryRepository = $countryRepository; $this->authorizationHelper = $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 ); 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 = []; $orWhereSearchClause = []; $orWhereSearchClauseArgs = []; if ('' !== $default) { foreach (explode(' ', $default) as $str) { $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); $orWhereSearchClause[] = '(LOWER(UNACCENT(?)) <<% person.fullnamecanonical OR ' . "person.fullnamecanonical LIKE '%' || LOWER(UNACCENT(?)) || '%' )"; array_push($orWhereSearchClauseArgs, $str, $str); } $query->andWhereClause( implode(' OR ', $orWhereSearchClause), $orWhereSearchClauseArgs ); } 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(); } /** * @throws NonUniqueResultException * @throws ParsingException * * @return array|Person[] */ 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 ->andWhereClause( strtr( 'person.center_id IN ({{ center_ids }})', [ '{{ center_ids }}' => implode( ', ', array_fill(0, count($authorizedCenters), '?') ), ] ), array_map(function (Center $c) {return $c->getId(); }, $authorizedCenters) ); } }