From 50b27c7ed9da9690a5fe2e10a5ebf5ae013de5e4 Mon Sep 17 00:00:00 2001 From: michnovka Date: Sun, 17 Nov 2024 13:05:41 +0100 Subject: [PATCH] Update voters.rst --- security/voters.rst | 73 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/security/voters.rst b/security/voters.rst index a770e386c02..6cb51428748 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -48,19 +48,74 @@ which makes creating a voter even easier:: abstract protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool; } -.. _how-to-use-the-voter-in-a-controller: +The Cacheable Voter Interface +----------------------------- +When Symfony calls the ``isGranted()`` method during runtime, it checks each voter +until it meets the access decision strategy set by the configuration. +While this generally works well, it can slow down performance in some cases. + +Imagine a backend displaying 20 items, each with 6 properties and 3 actions +(like edit, show, delete). Checking permissions for all these requires 180 calls +to each voter. With 5 voters, that's 900 calls. + +Usually, voters only need to focus on a specific permission (e.g., ``EDIT_BLOG_POST``) +or object type (e.g., ``User``). This focus makes voters cacheable by implementing a +:class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\CacheableVoterInterface` + + namespace Symfony\Component\Security\Core\Authorization\Voter; + + interface CacheableVoterInterface extends VoterInterface + { + public function supportsAttribute(string $attribute): bool; + + // $subjectType is the value returned by get_class($subject) or get_debug_type($subject) + public function supportsType(string $subjectType): bool; + } -.. tip:: +If your voter returns false for these methods, it will cache this result, +and your voter won't be called again for that attribute or type. - Checking each voter several times can be time consumming for applications - that perform a lot of permission checks. To improve performance in those cases, - you can make your voters implement the :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\CacheableVoterInterface`. - This allows the access decision manager to remember the attribute and type - of subject supported by the voter, to only call the needed voters each time. +If you extend from the abstract Voter class, you don't need to implement +the interface directly - just override ``supportsAttribute()`` and/or ``supportsType()``. - .. versionadded:: 5.4 +For instance, if your voter handles multiple object types but all permissions +are prefixed with ``APPROVE_``, do this: + + namespace App\Security; + + use Symfony\Component\Security\Core\Authorization\Voter\Voter; + + class MyVoter extends Voter + { + public function supportsAttribute(string $attribute): bool + { + return str_starts_with($attribute, 'APPROVE_'); + } + + // ... + } - The ``CacheableVoterInterface`` interface was introduced in Symfony 5.4. +If your voter handles various permissions for a specific type, do this: + + namespace App\Security; + + use App\Entity\BlogPost; + use Symfony\Component\Security\Core\Authorization\Voter\Voter; + + class MyVoter extends Voter + { + public function supportsType(string $subjectType): bool + { + // you can't use a simple BlogPost::class === $subjectType comparison + // here because the given subject type could be the proxy class used + // by Doctrine when creating the entity object + return is_a($subjectType, BlogPost::class, true); + } + + // ... + } + +.. _how-to-use-the-voter-in-a-controller: Setup: Checking for Access in a Controller ------------------------------------------