em->getRepository(PersonDocument::class)->createQueryBuilder('d'); $qb ->where($qb->expr()->eq('d.person', ':person')) ->setParameter('person', $person); return $qb; } public function buildFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface { $query = $this->buildBaseFetchQueryForPerson($person, $startDate, $endDate, $content); return $this->addFetchQueryByPersonACL($query, $person); } public function buildFetchQueryForAccompanyingPeriod(AccompanyingPeriod $period, ?\DateTimeImmutable $startDate = null, ?\DateTimeImmutable $endDate = null, ?string $content = null): FetchQueryInterface { $personDocMetadata = $this->em->getClassMetadata(PersonDocument::class); $participationMetadata = $this->em->getClassMetadata(AccompanyingPeriodParticipation::class); $query = new FetchQuery( PersonDocumentGenericDocProvider::KEY, sprintf('jsonb_build_object(\'id\', person_document.%s)', $personDocMetadata->getSingleIdentifierColumnName()), sprintf('person_document.%s', $personDocMetadata->getColumnName('date')), sprintf('%s AS person_document', $personDocMetadata->getSchemaName().'.'.$personDocMetadata->getTableName()) ); $query->addJoinClause( sprintf( 'JOIN %s AS participation ON participation.%s = person_document.%s '. 'AND person_document.%s BETWEEN participation.%s AND COALESCE(participation.%s, \'infinity\'::date)', $participationMetadata->getTableName(), $participationMetadata->getSingleAssociationJoinColumnName('person'), $personDocMetadata->getSingleAssociationJoinColumnName('person'), $personDocMetadata->getColumnName('date'), $participationMetadata->getColumnName('startDate'), $participationMetadata->getColumnName('endDate') ) ); $query->addWhereClause( sprintf('participation.%s = ?', $participationMetadata->getSingleAssociationJoinColumnName('accompanyingPeriod')), [$period->getId()], [Types::INTEGER] ); // can we see the document for this person ? $orPersonId = []; foreach ($period->getParticipations() as $participation) { if (!$this->security->isGranted(PersonDocumentVoter::SEE, $participation->getPerson())) { continue; } $orPersonId[] = $participation->getPerson()->getId(); } if ([] === $orPersonId) { $query->addWhereClause('FALSE = TRUE'); return $query; } $query->addWhereClause( sprintf( 'participation.%s IN (%s)', $participationMetadata->getSingleAssociationJoinColumnName('person'), implode(', ', array_fill(0, count($orPersonId), '?')) ), $orPersonId, array_fill(0, count($orPersonId), Types::INTEGER) ); return $this->addFilterClauses($query, $startDate, $endDate, $content); } public function buildBaseFetchQueryForPerson(Person $person, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery { $personDocMetadata = $this->em->getClassMetadata(PersonDocument::class); $query = new FetchQuery( PersonDocumentGenericDocProvider::KEY, sprintf('jsonb_build_object(\'id\', person_document.%s)', $personDocMetadata->getSingleIdentifierColumnName()), sprintf('person_document.%s', $personDocMetadata->getColumnName('date')), sprintf('%s AS person_document', $personDocMetadata->getSchemaName().'.'.$personDocMetadata->getTableName()) ); $query->addWhereClause( sprintf('person_document.%s = ?', $personDocMetadata->getSingleAssociationJoinColumnName('person')), [$person->getId()], [Types::INTEGER] ); return $this->addFilterClauses($query, $startDate, $endDate, $content); } private function addFilterClauses(FetchQuery $query, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ?string $content = null): FetchQuery { $personDocMetadata = $this->em->getClassMetadata(PersonDocument::class); if (null !== $startDate) { $query->addWhereClause( sprintf('? <= %s', $personDocMetadata->getColumnName('date')), [$startDate], [Types::DATE_IMMUTABLE] ); } if (null !== $endDate) { $query->addWhereClause( sprintf('? >= %s', $personDocMetadata->getColumnName('date')), [$endDate], [Types::DATE_IMMUTABLE] ); } if (null !== $content and '' !== $content) { $query->addWhereClause( sprintf( '(%s ilike ? OR %s ilike ?)', $personDocMetadata->getColumnName('title'), $personDocMetadata->getColumnName('description') ), ['%' . $content . '%', '%' . $content . '%'], [Types::STRING, Types::STRING] ); } return $query; } public function countByPerson(Person $person): int { $qb = $this->buildQueryByPerson($person)->select('COUNT(d)'); $this->addACL($qb, $person); return $qb->getQuery()->getSingleScalarResult(); } public function findByPerson(Person $person, array $orderBy = [], int $limit = 20, int $offset = 0): array { $qb = $this->buildQueryByPerson($person)->select('d'); $this->addACL($qb, $person); foreach ($orderBy as $field => $order) { $qb->addOrderBy('d.' . $field, $order); } $qb->setFirstResult($offset)->setMaxResults($limit); return $qb->getQuery()->getResult(); } private function addACL(QueryBuilder $qb, Person $person): void { $reachableScopes = []; foreach ($this->centerResolverManager->resolveCenters($person) as $center) { $reachableScopes = [ ...$reachableScopes, ...$this->authorizationHelperForCurrentUser ->getReachableScopes( PersonDocumentVoter::SEE, $center ) ]; } if ([] === $reachableScopes) { $qb->andWhere("'FALSE' = 'TRUE'"); return; } $qb->andWhere($qb->expr()->in('d.scope', ':scopes')) ->setParameter('scopes', $reachableScopes); } private function addFetchQueryByPersonACL(FetchQuery $fetchQuery, Person $person): FetchQuery { $personDocMetadata = $this->em->getClassMetadata(PersonDocument::class); $reachableScopes = []; foreach ($this->centerResolverManager->resolveCenters($person) as $center) { $reachableScopes = [ ...$reachableScopes, ...$this->authorizationHelperForCurrentUser->getReachableScopes(PersonDocumentVoter::SEE, $center) ]; } if ([] === $reachableScopes) { $fetchQuery->addWhereClause('FALSE = TRUE'); return $fetchQuery; } $fetchQuery->addWhereClause( sprintf( 'person_document.%s IN (%s)', $personDocMetadata->getSingleAssociationJoinColumnName('scope'), implode(', ', array_fill(0, count($reachableScopes), '?')) ), array_map(static fn (Scope $s) => $s->getId(), $reachableScopes), array_fill(0, count($reachableScopes), Types::INTEGER) ); return $fetchQuery; } }