Implement context-specific voters for all current entities that can be linked to a document

For reusability an AbstractStoredObjectVoter was created and a StoredObjectVoterInterface.
A WorkflowDocumentService checks whether the StoredObject is involved in a workflow.
This commit is contained in:
Julie Lenaerts 2024-06-26 13:45:15 +02:00
parent 73797b98f6
commit 1310d53589
18 changed files with 456 additions and 88 deletions

View File

@ -12,6 +12,8 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Repository; namespace Chill\ActivityBundle\Repository;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
@ -23,7 +25,7 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Activity[] findAll() * @method Activity[] findAll()
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class ActivityRepository extends ServiceEntityRepository class ActivityRepository extends ServiceEntityRepository implements AssociatedEntityToStoredObjectInterface
{ {
public function __construct(ManagerRegistry $registry) public function __construct(ManagerRegistry $registry)
{ {
@ -97,4 +99,17 @@ class ActivityRepository extends ServiceEntityRepository
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
{
$qb = $this->createQueryBuilder('a');
$query = $qb
->join('a.documents', 'ad')
->join('ad.storedObject', 'so')
->where('so.id = :storedObjectId')
->setParameter('storedObjectId', $storedObject->getId())
->getQuery();
return $query->getResult();
}
} }

View File

@ -19,7 +19,7 @@ use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository; use Doctrine\Persistence\ObjectRepository;
class AccompanyingCourseDocumentRepository implements ObjectRepository class AccompanyingCourseDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
{ {
private readonly EntityRepository $repository; private readonly EntityRepository $repository;
@ -46,8 +46,8 @@ class AccompanyingCourseDocumentRepository implements ObjectRepository
return $qb->getQuery()->getSingleScalarResult(); return $qb->getQuery()->getSingleScalarResult();
} }
public function findLinkedCourseDocument(StoredObject $storedObject): ?AccompanyingCourseDocument { public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
{
$qb = $this->repository->createQueryBuilder('d'); $qb = $this->repository->createQueryBuilder('d');
$query = $qb->where('d.storedObject = :storedObject') $query = $qb->where('d.storedObject = :storedObject')
->setParameter('storedObject', $storedObject) ->setParameter('storedObject', $storedObject)
@ -66,7 +66,7 @@ class AccompanyingCourseDocumentRepository implements ObjectRepository
return $this->repository->findAll(); return $this->repository->findAll();
} }
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array
{ {
return $this->repository->findBy($criteria, $orderBy, $limit, $offset); return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
} }
@ -76,7 +76,7 @@ class AccompanyingCourseDocumentRepository implements ObjectRepository
return $this->findOneBy($criteria); return $this->findOneBy($criteria);
} }
public function getClassName() public function getClassName(): string
{ {
return AccompanyingCourseDocument::class; return AccompanyingCourseDocument::class;
} }

View File

@ -0,0 +1,10 @@
<?php
namespace Chill\DocStoreBundle\Repository;
use Chill\DocStoreBundle\Entity\StoredObject;
interface AssociatedEntityToStoredObjectInterface
{
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object;
}

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Repository; namespace Chill\DocStoreBundle\Repository;
use Chill\DocStoreBundle\Entity\PersonDocument; use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\DocStoreBundle\Entity\StoredObject;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository; use Doctrine\Persistence\ObjectRepository;
@ -19,7 +20,7 @@ use Doctrine\Persistence\ObjectRepository;
/** /**
* @template ObjectRepository<PersonDocument::class> * @template ObjectRepository<PersonDocument::class>
*/ */
readonly class PersonDocumentRepository implements ObjectRepository readonly class PersonDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
{ {
private EntityRepository $repository; private EntityRepository $repository;
@ -53,4 +54,14 @@ readonly class PersonDocumentRepository implements ObjectRepository
{ {
return PersonDocument::class; return PersonDocument::class;
} }
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
{
$qb = $this->repository->createQueryBuilder('d');
$query = $qb->where('d.storedObject = :storedObject')
->setParameter('storedObject', $storedObject)
->getQuery();
return $query->getResult();
}
} }

View File

@ -1,61 +0,0 @@
<?php
namespace ChillDocStoreBundle\Security\Authorization;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class AccompanyingCourseDocumentStoredObjectVoter implements StoredObjectVoterInterface
{
final public const SEE_AND_EDIT = 'CHILL_ACCOMPANYING_COURSE_DOCUMENT_STORED_OBJECT_SEE_EDIT';
public function __construct(
private readonly AccompanyingCourseDocumentRepository $repository,
private readonly AccompanyingCourseDocumentVoter $accompanyingCourseDocumentVoter,
private readonly WorkflowDocumentService $workflowDocumentService
){
}
public function supports(string $attribute, StoredObject $subject): bool
{
// check if the stored object is linked to an AccompanyingCourseDocument
return $this->repository->findLinkedCourseDocument($subject) instanceof AccompanyingCourseDocument;
}
public function voteOnAttribute(string $attribute, StoredObject $subject, TokenInterface $token): bool
{
if (!$token->getUser() instanceof User) {
return false;
}
// Retrieve the related accompanying course document
$accompanyingCourseDocument = $this->repository->findLinkedCourseDocument($subject);
// Determine the attribute to pass to AccompanyingCourseDocumentVoter
$voterAttribute = match($attribute) {
self::SEE_AND_EDIT => AccompanyingCourseDocumentVoter::UPDATE,
default => AccompanyingCourseDocumentVoter::SEE_DETAILS,
};
// Check access using AccompanyingCourseDocumentVoter
if (false === $this->accompanyingCourseDocumentVoter->voteOnAttribute($voterAttribute, $accompanyingCourseDocument, $token)) {
return false;
}
// Check if entity is related to a workflow, if so, check if user can apply transition
$relatedWorkflow = $this->workflowDocumentService->getRelatedWorkflow($accompanyingCourseDocument);
if ($relatedWorkflow instanceof EntityWorkflow){
return $this->workflowDocumentService->canApplyTransition($relatedWorkflow);
}
return true;
}
}

View File

@ -48,15 +48,19 @@ class StoredObjectVoter extends Voter
return false; return false;
} }
$attributeAsEnum = StoredObjectRoleEnum::from($attribute);
// Loop through context-specific voters // Loop through context-specific voters
foreach ($this->storedObjectVoters as $storedObjectVoter) { foreach ($this->storedObjectVoters as $storedObjectVoter) {
if ($storedObjectVoter->supports($attribute, $subject)) { if ($storedObjectVoter->supports($attributeAsEnum, $subject)) {
return $storedObjectVoter->voteOnAttribute($attribute, $subject, $token); return $storedObjectVoter->voteOnAttribute($attributeAsEnum, $subject, $token);
} }
} }
// User role-based fallback // User role-based fallback
if ($this->security->isGranted('ROLE_USER') || $this->security->isGranted('ROLE_ADMIN')) { if ($this->security->isGranted('ROLE_USER') || $this->security->isGranted('ROLE_ADMIN')) {
// TODO: this maybe considered as a security issue, as all authenticated users can reach a stored object which
// is potentially detached from an existing entity.
return true; return true;
} }

View File

@ -12,12 +12,13 @@ declare(strict_types=1);
namespace Chill\DocStoreBundle\Security\Authorization; namespace Chill\DocStoreBundle\Security\Authorization;
use Chill\DocStoreBundle\Entity\StoredObject; use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
interface StoredObjectVoterInterface { interface StoredObjectVoterInterface {
public function supports(string $attribute, StoredObject $subject): bool; public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool;
public function voteOnAttribute(string $attribute, StoredObject $subject, TokenInterface $token): bool; public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool;
} }

View File

@ -0,0 +1,67 @@
<?php
namespace Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoterInterface;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Chill\MainBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
abstract class AbstractStoredObjectVoter implements StoredObjectVoterInterface
{
abstract protected function getRepository(): AssociatedEntityToStoredObjectInterface;
/**
* @return class-string
*/
abstract protected function getClass(): string;
abstract protected function attributeToRole(StoredObjectRoleEnum $attribute): string;
abstract protected function canBeAssociatedWithWorkflow(): bool;
function __construct(
private readonly Security $security,
private readonly ?WorkflowDocumentService $workflowDocumentService = null,
)
{
}
public function supports(StoredObjectRoleEnum $attribute, StoredObject $subject): bool
{
$class = $this->getClass();
return $this->getRepository()->findAssociatedEntityToStoredObject($subject) instanceof $class;
}
public function voteOnAttribute(StoredObjectRoleEnum $attribute, StoredObject $subject, TokenInterface $token): bool
{
if (!$token->getUser() instanceof User) {
return false;
}
// Retrieve the related accompanying course document
$entity = $this->getRepository()->findAssociatedEntityToStoredObject($subject);
// Determine the attribute to pass to AccompanyingCourseDocumentVoter
$voterAttribute = $this->attributeToRole($attribute);
if (false === $this->security->isGranted($voterAttribute, $entity)) {
return false;
}
if ($this->canBeAssociatedWithWorkflow()) {
if (null === $this->workflowDocumentService) {
throw new \LogicException("Provide a workflow document service");
}
return $this->workflowDocumentService->notBlockedByWorkflow($entity);
}
return true;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\AccompanyingCourseDocumentVoter;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Symfony\Component\Security\Core\Security;
final class AccompanyingCourseStoredObjectVoter extends AbstractStoredObjectVoter
{
public function __construct(
private readonly AccompanyingCourseDocumentRepository $repository,
Security $security,
WorkflowDocumentService $workflowDocumentService
){
parent::__construct($security, $workflowDocumentService);
}
protected function getRepository(): AssociatedEntityToStoredObjectInterface
{
return $this->repository;
}
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
{
return match ($attribute) {
StoredObjectRoleEnum::EDIT => AccompanyingCourseDocumentVoter::UPDATE,
StoredObjectRoleEnum::SEE => AccompanyingCourseDocumentVoter::SEE_DETAILS,
};
}
protected function getClass(): string
{
return AccompanyingCourseDocument::class;
}
protected function canBeAssociatedWithWorkflow(): bool
{
return true;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocumentRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkEvaluationDocumentVoter;
use Symfony\Component\Security\Core\Security;
class AccompanyingPeriodWorkEvaluationStoredObjectVoter extends AbstractStoredObjectVoter
{
public function __construct(
private readonly AccompanyingPeriodWorkEvaluationDocumentRepository $repository,
Security $security,
WorkflowDocumentService $workflowDocumentService
){
parent::__construct($security, $workflowDocumentService);
}
protected function getRepository(): AssociatedEntityToStoredObjectInterface
{
return $this->repository;
}
/**
* @inheritDoc
*/
protected function getClass(): string
{
return AccompanyingPeriodWorkEvaluationDocument::class;
}
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
{
//Question: there is no update/edit check in AccompanyingPeriodWorkEvaluationDocumentVoter, so for both SEE and EDIT of the
// stored object I check with SEE right in AccompanyingPeriodWorkEvaluationDocumentVoter, correct?
return match ($attribute) {
StoredObjectRoleEnum::SEE, StoredObjectRoleEnum::EDIT => AccompanyingPeriodWorkEvaluationDocumentVoter::SEE,
};
}
protected function canBeAssociatedWithWorkflow(): bool
{
return true;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Repository\ActivityDocumentACLAwareRepository;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Symfony\Component\Security\Core\Security;
class ActivityStoredObjectVoter extends AbstractStoredObjectVoter
{
public function __construct(
private readonly ActivityRepository $repository,
Security $security,
WorkflowDocumentService $workflowDocumentService
){
parent::__construct($security, $workflowDocumentService);
}
protected function getRepository(): AssociatedEntityToStoredObjectInterface
{
return $this->repository;
}
/**
* @inheritDoc
*/
protected function getClass(): string
{
return Activity::class;
}
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
{
return match ($attribute) {
StoredObjectRoleEnum::EDIT => ActivityVoter::UPDATE,
StoredObjectRoleEnum::SEE => ActivityVoter::SEE_DETAILS,
};
}
protected function canBeAssociatedWithWorkflow(): bool
{
return false;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Chill\EventBundle\Entity\Event;
use Chill\EventBundle\Repository\EventRepository;
use Chill\EventBundle\Security\Authorization\EventVoter;
use Symfony\Component\Security\Core\Security;
class EventStoredObjectVoter extends AbstractStoredObjectVoter
{
public function __construct(
private readonly EventRepository $repository,
Security $security,
WorkflowDocumentService $workflowDocumentService
){
parent::__construct($security, $workflowDocumentService);
}
protected function getRepository(): AssociatedEntityToStoredObjectInterface
{
return $this->repository;
}
/**
* @inheritDoc
*/
protected function getClass(): string
{
return Event::class;
}
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
{
return match ($attribute) {
StoredObjectRoleEnum::EDIT => EventVoter::UPDATE,
StoredObjectRoleEnum::SEE => EventVoter::SEE_DETAILS,
};
}
protected function canBeAssociatedWithWorkflow(): bool
{
return false;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace src\Bundle\ChillDocStoreBundle\Security\Authorization\StoredObjectVoters;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\DocStoreBundle\Repository\PersonDocumentRepository;
use Chill\DocStoreBundle\Security\Authorization\PersonDocumentVoter;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoters\AbstractStoredObjectVoter;
use Chill\DocStoreBundle\Service\WorkflowDocumentService;
use Symfony\Component\Security\Core\Security;
class PersonStoredObjectVoter extends AbstractStoredObjectVoter
{
public function __construct(
private readonly PersonDocumentRepository $repository,
Security $security,
WorkflowDocumentService $workflowDocumentService
){
parent::__construct($security, $workflowDocumentService);
}
protected function getRepository(): AssociatedEntityToStoredObjectInterface
{
return $this->repository;
}
/**
* @inheritDoc
*/
protected function getClass(): string
{
return PersonDocument::class;
}
protected function attributeToRole(StoredObjectRoleEnum $attribute): string
{
return match ($attribute) {
StoredObjectRoleEnum::EDIT => PersonDocumentVoter::UPDATE,
StoredObjectRoleEnum::SEE => PersonDocumentVoter::SEE_DETAILS,
};
}
protected function canBeAssociatedWithWorkflow(): bool
{
return true;
}
}

View File

@ -13,19 +13,19 @@ class WorkflowDocumentService
{ {
} }
public function getRelatedWorkflow($entity): ?EntityWorkflow public function notBlockedByWorkflow($entity): bool
{ {
return $this->repository->findByRelatedEntity(get_class($entity), $entity->getId()); /**
} * @var EntityWorkflow
*/
$workflow = $this->repository->findByRelatedEntity(get_class($entity), $entity->getId());
public function canApplyTransition(EntityWorkflow $entityWorkflow): bool if ($workflow->isFinal()) {
{
if ($entityWorkflow->isFinal()) {
return false; return false;
} }
$currentUser = $this->security->getUser(); $currentUser = $this->security->getUser();
if ($entityWorkflow->getCurrentStep()->getAllDestUser()->contains($currentUser)) { if ($workflow->getCurrentStep()->getAllDestUser()->contains($currentUser)) {
return true; return true;
} }

View File

@ -31,7 +31,7 @@ use Symfony\Component\Validator\Constraints as Assert;
/** /**
* Class Event. * Class Event.
*/ */
#[ORM\Entity(repositoryClass: \Chill\EventBundle\Repository\EventRepository::class)] #[ORM\Entity]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
#[ORM\Table(name: 'chill_event_event')] #[ORM\Table(name: 'chill_event_event')]
class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInterface, TrackUpdateInterface class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInterface, TrackUpdateInterface

View File

@ -11,17 +11,66 @@ declare(strict_types=1);
namespace Chill\EventBundle\Repository; namespace Chill\EventBundle\Repository;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\EventBundle\Entity\Event; use Chill\EventBundle\Entity\Event;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
/** /**
* Class EventRepository. * Class EventRepository.
*/ */
class EventRepository extends ServiceEntityRepository class EventRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
{ {
public function __construct(ManagerRegistry $registry) private readonly EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{ {
parent::__construct($registry, Event::class); $this->repository = $entityManager->getRepository(Event::class);
}
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
{
return $this->repository->createQueryBuilder($alias, $indexBy);
}
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
{
$qb = $this->createQueryBuilder('e');
$query = $qb
->join('e.documents', 'ed')
->join('ed.storedObject', 'so')
->where('so.id = :storedObjectId')
->setParameter('storedObjectId', $storedObject->getId())
->getQuery();
return $query->getResult();
}
public function find($id)
{
return $this->repository->find($id);
}
public function findAll(): array
{
return $this->repository->findAll();
}
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
}
public function findOneBy(array $criteria)
{
return $this->repository->findOneBy($criteria);
}
public function getClassName(): string
{
return Event::class;
} }
} }

View File

@ -99,6 +99,22 @@ class EntityWorkflowRepository implements ObjectRepository
return $this->repository->findAll(); return $this->repository->findAll();
} }
public function findByRelatedEntity($entityClass, $relatedEntityId): ?EntityWorkflow
{
$qb = $this->repository->createQueryBuilder('w');
$query = $qb->where(
$qb->expr()->andX(
$qb->expr()->eq('w.relatedEntityClass', ':entity_class'),
$qb->expr()->eq('w.relatedEntityId', ':entity_id'),
)
)->setParameter('entity_class', $entityClass)
->setParameter('entity_id', $relatedEntityId);
return $query->getQuery()->getResult();
}
/** /**
* @param mixed|null $limit * @param mixed|null $limit
* @param mixed|null $offset * @param mixed|null $offset

View File

@ -11,12 +11,14 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository; use Doctrine\Persistence\ObjectRepository;
class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectRepository, AssociatedEntityToStoredObjectInterface
{ {
private readonly EntityRepository $repository; private readonly EntityRepository $repository;
@ -58,4 +60,14 @@ class AccompanyingPeriodWorkEvaluationDocumentRepository implements ObjectReposi
{ {
return AccompanyingPeriodWorkEvaluationDocument::class; return AccompanyingPeriodWorkEvaluationDocument::class;
} }
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
{
$qb = $this->repository->createQueryBuilder('ed');
$query = $qb->where('ed.storedObject = :storedObject')
->setParameter('storedObject', $storedObject)
->getQuery();
return $query->getResult();
}
} }