chill-bundles/src/Bundle/ChillEventBundle/Repository/EventACLAwareRepository.php

143 lines
4.8 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\EventBundle\Repository;
use Chill\EventBundle\Entity\Event;
use Chill\EventBundle\Entity\Participation;
use Chill\EventBundle\Security\Authorization\EventVoter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperForCurrentUserInterface;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Security;
final readonly class EventACLAwareRepository implements EventACLAwareRepositoryInterface
{
public function __construct(
private AuthorizationHelperForCurrentUserInterface $authorizationHelperForCurrentUser,
private EntityManagerInterface $entityManager,
private Security $security,
) {
}
/**
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function countAllViewable(array $filters): int
{
if (!$this->security->getUser() instanceof User) {
return 0;
}
$qb = $this->buildQueryByAllViewable($filters);
$this->addFilters($filters, $qb);
$qb->select('COUNT(event.id)');
return $qb->getQuery()->getSingleScalarResult();
}
public function findAllViewable(array $filters, int $offset = 0, int $limit = 50): array
{
if (!$this->security->getUser() instanceof User) {
return [];
}
$qb = $this->buildQueryByAllViewable($filters)->select('event');
$this->addFilters($filters, $qb);
$qb->setFirstResult($offset)->setMaxResults($limit);
$qb->addOrderBy('event.date', 'DESC');
return $qb->getQuery()->getResult();
}
private function addFilters(array $filters, QueryBuilder $qb): void
{
if (($filters['q'] ?? '') !== '') {
$qb->andWhere('event.name LIKE :content');
$qb->setParameter('content', '%'.$filters['q'].'%');
}
if (array_key_exists('dates', $filters)) {
$dates = $filters['dates'];
if (null !== ($dates['from'] ?? null)) {
$qb->andWhere('event.date >= :date_from');
$qb->setParameter('date_from', $dates['from']);
}
if (null !== ($dates['to'] ?? null)) {
$qb->andWhere('event.date <= :date_to');
$qb->setParameter('date_to', $dates['to']);
}
}
if (0 < count($filters['event_types'] ?? [])) {
$qb->andWhere('event.type IN (:event_types)');
$qb->setParameter('event_types', $filters['event_types']);
}
}
public function buildQueryByAllViewable(array $filters): QueryBuilder
{
$qb = $this->entityManager->createQueryBuilder();
$qb->from(Event::class, 'event');
$aclConditions = $qb->expr()->orX();
$i = 0;
foreach ($this->authorizationHelperForCurrentUser->getReachableCenters(EventVoter::SEE) as $center) {
foreach ($this->authorizationHelperForCurrentUser->getReachableScopes(EventVoter::SEE, $center) as $scopes) {
$aclConditions->add(
$qb->expr()->andX(
'event.circle IN (:scopes_'.$i.')',
$qb->expr()->orX(
'event.center = :center_'.$i,
$qb->expr()->exists(
'SELECT 1 FROM '.Participation::class.' participation_'.$i.' JOIN participation_'.$i.'.event event_'.$i.
' JOIN '.Person\PersonCenterHistory::class.' person_center_history_'.$i.
' WITH IDENTITY(person_center_history_'.$i.'.person) = IDENTITY(participation_'.$i.'.person) '.
' AND event_'.$i.'.date <= person_center_history_'.$i.'.startDate AND (person_center_history_'.$i.'.endDate IS NULL OR person_center_history_'.$i.'.endDate > event_'.$i.'.date) '.
' WHERE participation_'.$i.'.event = event'
)
)
)
);
$qb->setParameter('scopes_'.$i, $scopes);
$qb->setParameter('center_'.$i, $center);
++$i;
}
}
if (0 === $i) {
$aclConditions->add('FALSE = TRUE');
}
$qb
->andWhere(
$qb->expr()->orX(
'event.createdBy = :user',
$aclConditions
)
);
$qb->setParameter('user', $this->security->getUser());
return $qb;
}
}