voter = $voterFactory ->generate(AbstractTask::class) ->addCheckFor(AbstractTask::class, self::ROLES) ->addCheckFor(Person::class, [self::SHOW, self::CREATE_PERSON]) ->addCheckFor(AccompanyingPeriod::class, [self::SHOW, self::CREATE_COURSE]) ->addCheckFor(null, [self::SHOW]) ->build(); } public function getRoles(): array { return self::ROLES; } public function getRolesWithHierarchy(): array { return [ 'Task' => self::ROLES, ]; } public function getRolesWithoutScope(): array { return []; } public function supports($attribute, $subject) { return $this->voter->supports($attribute, $subject); } protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { if (!$token->getUser() instanceof User) { return false; } $event = new AuthorizationEvent($subject, $attribute, $token); $this->eventDispatcher->dispatch(AuthorizationEvent::VOTE, $event); if ($event->hasVote()) { $this->logger->debug('The TaskVoter is overriding by ' . AuthorizationEvent::VOTE, [ 'vote' => $event->getVote(), 'task_id' => $subject->getId(), ]); return $event->getVote(); } // do pre-flight check, relying on other decision manager // those pre-flight check concern associated entities if ($subject instanceof AbstractTask) { // a user can always see his own tasks if ($subject->getAssignee() === $token->getUser()) { return true; } if (null !== $person = $subject->getPerson()) { if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) { return false; } } elseif (null !== $period = $subject->getCourse()) { if (!$this->accessDecisionManager->decide($token, [AccompanyingPeriodVoter::SEE], $period)) { return false; } } } if ($subject instanceof AccompanyingPeriod) { if (AccompanyingPeriod::STEP_CLOSED === $subject->getStep()) { if (in_array($attribute, [self::UPDATE, self::CREATE_COURSE, self::DELETE], true)) { return false; } } } // do regular check. return $this->voter->voteOnAttribute($attribute, $subject, $token); } }