@@ -40,14 +40,20 @@ or extend :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Vote
4040which makes creating a voter even easier::
4141
4242 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
43+ use Symfony\Component\Security\Core\Authorization\Voter\Vote;
4344 use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
4445
4546 abstract class Voter implements VoterInterface
4647 {
4748 abstract protected function supports(string $attribute, mixed $subject): bool;
48- abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool;
49+ abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null ): bool;
4950 }
5051
52+ .. versionadded :: 7.3
53+
54+ The `$vote ` parameter in the :method: `Symfony\\ Component\\ Security\\ Core\\ Authorization\\ Voter\\ VoterInterface::voteOnAttribute ` method
55+ was introduced in Symfony 7.3.
56+
5157.. _how-to-use-the-voter-in-a-controller :
5258
5359.. tip ::
@@ -140,6 +146,7 @@ would look like this::
140146 use App\Entity\Post;
141147 use App\Entity\User;
142148 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
149+ use Symfony\Component\Security\Core\Authorization\Voter\Vote;
143150 use Symfony\Component\Security\Core\Authorization\Voter\Voter;
144151
145152 class PostVoter extends Voter
@@ -163,12 +170,14 @@ would look like this::
163170 return true;
164171 }
165172
166- protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
173+ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null ): bool
167174 {
168175 $user = $token->getUser();
176+ $vote ??= new Vote();
169177
170178 if (!$user instanceof User) {
171179 // the user must be logged in; if not, deny access
180+ $vote->reasons[] = 'The user is not logged in.';
172181 return false;
173182 }
174183
@@ -197,7 +206,13 @@ would look like this::
197206 private function canEdit(Post $post, User $user): bool
198207 {
199208 // this assumes that the Post object has a `getOwner()` method
200- return $user === $post->getOwner();
209+ if ($user === $post->getOwner()) {
210+ return true;
211+ }
212+
213+ $vote->reasons[] = 'You are not the owner of the Post.';
214+
215+ return false;
201216 }
202217 }
203218
@@ -215,11 +230,12 @@ To recap, here's what's expected from the two abstract methods:
215230 return ``true `` if the attribute is ``view `` or ``edit `` and if the object is
216231 a ``Post `` instance.
217232
218- ``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token) ``
233+ ``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null ) ``
219234 If you return ``true `` from ``supports() ``, then this method is called. Your
220235 job is to return ``true `` to allow access and ``false `` to deny access.
221- The ``$token `` can be used to find the current user object (if any). In this
222- example, all of the complex business logic is included to determine access.
236+ The ``$token `` can be used to find the current user object (if any). The ``$vote ``
237+ argument can be used to add a reason to the vote. In this example, all of the
238+ complex business logic is included to determine access.
223239
224240.. _declaring-the-voter-as-a-service :
225241
@@ -256,7 +272,7 @@ with ``ROLE_SUPER_ADMIN``::
256272 ) {
257273 }
258274
259- protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token): bool
275+ protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null ): bool
260276 {
261277 // ...
262278
0 commit comments