diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 77da7b435..a25bee94a 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -19,6 +19,7 @@ namespace Chill\MainBundle\DependencyInjection; +use Chill\MainBundle\Doctrine\DQL\STContains; use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Form\UserJobType; @@ -186,7 +187,8 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class, 'SIMILARITY' => Similarity::class, 'OVERLAPSI' => OverlapsI::class, - 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class + 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class, + 'ST_CONTAINS' => STContains::class, ], ], 'hydrators' => [ diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STContains.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STContains.php new file mode 100644 index 000000000..5235d51bb --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STContains.php @@ -0,0 +1,52 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\MainBundle\Doctrine\DQL; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\Lexer; + +/** + * Geometry function 'ST_CONTAINS', added by postgis + */ +class STContains extends FunctionNode +{ + private $firstPart; + + private $secondPart; + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'ST_CONTAINS('.$this->firstPart->dispatch($sqlWalker). + ', ' . $this->secondPart->dispatch($sqlWalker) .")"; + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstPart = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondPart = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index bc5c46e08..2b0b5edbd 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -419,6 +419,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * This is computed through database and is optimized on database side. * * @var PersonCurrentAddress|null + * @ORM\OneToOne(targetEntity=PersonCurrentAddress::class, mappedBy="person") */ private ?PersonCurrentAddress $currentPersonAddress = null; diff --git a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCurrentAddress.php b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCurrentAddress.php index 21962b700..e982f8b97 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person/PersonCurrentAddress.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person/PersonCurrentAddress.php @@ -22,7 +22,8 @@ class PersonCurrentAddress { /** * @ORM\Id - * @ORM\OneToOne(targetEntity=Person::class) + * @ORM\OneToOne(targetEntity=Person::class, inversedBy="currentPersonAddress") + * @ORM\JoinColumn(name="person_id", referencedColumnName="id") */ protected Person $person; diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php index 40d06615c..5214b38b7 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php @@ -60,17 +60,32 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac $countryCode); $this->addACLClauses($qb, 'p'); + return $this->getQueryResult($qb, $simplify, $limit, $start); + } + + /** + * Helper method to prepare and return the search query for PersonACL. + * + * This method replace the select clause with required parameters, depending on the + * "simplify" parameter. It also add query limits. + * + * The given alias must represent the person alias. + * + * @return array|Person[] + */ + public function getQueryResult(QueryBuilder $qb, string $alias, bool $simplify, int $limit, int $start): array + { if ($simplify) { $qb->select( - 'p.id', + $alias.'.id', $qb->expr()->concat( - 'p.firstName', + $alias.'.firstName', $qb->expr()->literal(' '), - 'p.lastName' + $alias.'.lastName' ).'AS text' ); } else { - $qb->select('p'); + $qb->select($alias); } $qb @@ -79,8 +94,8 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac //order by firstname, lastname $qb - ->orderBy('p.firstName') - ->addOrderBy('p.lastName'); + ->orderBy($alias.'.firstName') + ->addOrderBy($alias.'.lastName'); if ($simplify) { return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY); @@ -104,7 +119,20 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac $countryCode); $this->addACLClauses($qb, 'p'); - $qb->select('COUNT(p.id)'); + return $this->getCountQueryResult($qb); + } + + /** + * Helper method to prepare and return the count for search query + * + * This method replace the select clause with required parameters, depending on the + * "simplify" parameter. + * + * The given alias must represent the person alias in the query builder. + */ + public function getCountQueryResult(QueryBuilder $qb, $alias): int + { + $qb->select('COUNT('.$alias.'.id)'); return $qb->getQuery()->getSingleScalarResult(); } @@ -115,33 +143,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac $qb = $this->createSimilarityQuery($pattern); $this->addACLClauses($qb, 'sp'); - if ($simplify) { - $qb->select( - 'sp.id', - $qb->expr()->concat( - 'sp.firstName', - $qb->expr()->literal(' '), - 'sp.lastName' - ).'AS text' - ); - } else { - $qb->select('sp'); - } - - $qb - ->setMaxResults($maxResult) - ->setFirstResult($firstResult); - - //order by firstname, lastname - $qb - ->orderBy('sp.firstName') - ->addOrderBy('sp.lastName'); - - if ($simplify) { - return $qb->getQuery()->getResult(Query::HYDRATE_ARRAY); - } else { - return $qb->getQuery()->getResult(); - } + return $this->getQueryResult($qb, 'sp', $simplify, $maxResult, $firstResult); } public function countBySimilaritySearch(string $pattern) @@ -149,12 +151,27 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac $qb = $this->createSimilarityQuery($pattern); $this->addACLClauses($qb, 'sp'); - $qb->select('COUNT(sp.id)'); - - return $qb->getQuery()->getSingleScalarResult(); + return $this->getCountQueryResult($qb, 'sp'); } - private function createSearchQuery( + /** + * Create a search query without ACL + * + * The person alias is a "p" + * + * @param string|null $default + * @param string|null $firstname + * @param string|null $lastname + * @param \DateTime|null $birthdate + * @param \DateTime|null $birthdateBefore + * @param \DateTime|null $birthdateAfter + * @param string|null $gender + * @param string|null $countryCode + * @return QueryBuilder + * @throws NonUniqueResultException + * @throws ParsingException + */ + public function createSearchQuery( string $default = null, string $firstname = null, string $lastname = null, @@ -244,7 +261,15 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac $qb->setParameter('centers', $reachableCenters); } - private function createSimilarityQuery($pattern): QueryBuilder + /** + * Create a query for searching by similarity. + * + * The person alias is "sp". + * + * @param $pattern + * @return QueryBuilder + */ + public function createSimilarityQuery($pattern): QueryBuilder { $qb = $this->em->createQueryBuilder();