@@ -50,14 +50,6 @@ which makes creating a voter even easier::
|
50 | 50 |
|
51 | 51 | .. _how-to-use-the-voter-in-a-controller:
|
52 | 52 |
|
53 |
| -.. tip:: |
54 |
| - |
55 |
| -Checking each voter several times can be time consuming for applications |
56 |
| -that perform a lot of permission checks. To improve performance in those cases, |
57 |
| -you can make your voters implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\CacheableVoterInterface`. |
58 |
| -This allows the access decision manager to remember the attribute and type |
59 |
| -of subject supported by the voter, to only call the needed voters each time. |
60 |
| - |
61 | 53 | Setup: Checking for Access in a Controller
|
62 | 54 | ------------------------------------------
|
63 | 55 |
|
@@ -296,6 +288,89 @@ If you're using the :ref:`default services.yaml configuration <service-container
|
296 | 288 | you're done! Symfony will automatically pass the ``security.helper``
|
297 | 289 | service when instantiating your voter (thanks to autowiring).
|
298 | 290 |
|
| 291 | +Improving Voter Performance |
| 292 | +--------------------------- |
| 293 | + |
| 294 | +If your application defines many voters and checks permissions on many objects |
| 295 | +during a single request, this can impact performance. Most of the time, voters |
| 296 | +only care about specific permissions (attributes), such as ``EDIT_BLOG_POST``, |
| 297 | +or specific object types, such as ``User`` or ``Invoice``. That's why Symfony |
| 298 | +can cache the voter resolution (i.e. the decision to apply or skip a voter for |
| 299 | +a given attribute or object). |
| 300 | + |
| 301 | +To enable this optimization, make your voter implement |
| 302 | +:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\CacheableVoterInterface`. |
| 303 | +This is already the case when extending the abstract ``Voter`` class shown above. |
| 304 | +Then, override one or both of the following methods:: |
| 305 | + |
| 306 | +use App\Entity\Post; |
| 307 | +use Symfony\Component\Security\Core\Authorization\Voter\Voter; |
| 308 | +// ... |
| 309 | + |
| 310 | +class PostVoter extends Voter |
| 311 | +{ |
| 312 | +const VIEW = 'view'; |
| 313 | +const EDIT = 'edit'; |
| 314 | + |
| 315 | +protected function supports(string $attribute, mixed $subject): bool |
| 316 | +{ |
| 317 | +// ... |
| 318 | +} |
| 319 | + |
| 320 | +protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool |
| 321 | +{ |
| 322 | +// ... |
| 323 | +} |
| 324 | + |
| 325 | +// this method returns true if the voter applies to the given attribute; |
| 326 | +// if it returns false, Symfony won't call it again for this attribute |
| 327 | +public function supportsAttribute(string $attribute): bool |
| 328 | +{ |
| 329 | +return in_array($attribute, [self::VIEW, self::EDIT], true); |
| 330 | +} |
| 331 | + |
| 332 | +// this method returns true if the voter applies to the given object class/type; |
| 333 | +// if it returns false, Symfony won't call it again for that type of object |
| 334 | +public function supportsAttribute(string $attribute): bool |
| 335 | +{ |
| 336 | +// you can't use a simple Post::class === $subjectType comparison |
| 337 | +// because the subject type might be a Doctrine proxy class |
| 338 | +return is_a($subjectType, Post::class, true); |
| 339 | +} |
| 340 | +} |
| 341 | + |
| 342 | +.. _security-voters-change-message-and-status-code: |
| 343 | + |
| 344 | +Changing the message and status code returned |
| 345 | +--------------------------------------------- |
| 346 | + |
| 347 | +By default, the ``#[IsGranted]`` attribute will throw a |
| 348 | +:class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` |
| 349 | +and return an http **403** status code with **Access Denied** as message. |
| 350 | + |
| 351 | +However, you can change this behavior by specifying the message and status code returned:: |
| 352 | + |
| 353 | +// src/Controller/PostController.php |
| 354 | + |
| 355 | +// ... |
| 356 | +use Symfony\Component\Security\Http\Attribute\IsGranted; |
| 357 | + |
| 358 | +class PostController extends AbstractController |
| 359 | +{ |
| 360 | +#[Route('/posts/{id}', name: 'post_show')] |
| 361 | +#[IsGranted('show', 'post', 'Post not found', 404)] |
| 362 | +public function show(Post $post): Response |
| 363 | +{ |
| 364 | +// ... |
| 365 | +} |
| 366 | +} |
| 367 | + |
| 368 | +.. tip:: |
| 369 | + |
| 370 | +If the status code is different than 403, an |
| 371 | +:class:`Symfony\\Component\\HttpKernel\\Exception\\HttpException` |
| 372 | +will be thrown instead. |
| 373 | + |
299 | 374 | .. _security-voters-change-strategy:
|
300 | 375 |
|
301 | 376 | Changing the Access Decision Strategy
|
@@ -467,35 +542,3 @@ must implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Ac
|
467 | 542 | // ...
|
468 | 543 | ;
|
469 | 544 | };
|
470 |
| -
|
471 |
| -.. _security-voters-change-message-and-status-code: |
472 |
| - |
473 |
| -Changing the message and status code returned |
474 |
| ---------------------------------------------- |
475 |
| - |
476 |
| -By default, the ``#[IsGranted]`` attribute will throw a |
477 |
| -:class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` |
478 |
| -and return an http **403** status code with **Access Denied** as message. |
479 |
| - |
480 |
| -However, you can change this behavior by specifying the message and status code returned:: |
481 |
| - |
482 |
| -// src/Controller/PostController.php |
483 |
| - |
484 |
| -// ... |
485 |
| -use Symfony\Component\Security\Http\Attribute\IsGranted; |
486 |
| - |
487 |
| -class PostController extends AbstractController |
488 |
| -{ |
489 |
| -#[Route('/posts/{id}', name: 'post_show')] |
490 |
| -#[IsGranted('show', 'post', 'Post not found', 404)] |
491 |
| -public function show(Post $post): Response |
492 |
| -{ |
493 |
| -// ... |
494 |
| -} |
495 |
| -} |
496 |
| - |
497 |
| -.. tip:: |
498 |
| - |
499 |
| -If the status code is different than 403, an |
500 |
| -:class:`Symfony\\Component\\HttpKernel\\Exception\\HttpException` |
501 |
| -will be thrown instead. |
|
0 commit comments