Voters

Contents
A voter class
Check the attribute
Check the subject
Deny by default

A voter class

A voter is a small class that answers one question, namely whether the current user may perform an action on an object. Symfony calls each voter and grants access when at least one voter agrees.

Create a class that extends Voter and implement two methods called supports and voteOnAttribute. Keep one voter per resource so the logic stays easy to test.

namespace App\Security; use App\Entity\Post; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter { protected function supports(string $attribute, mixed $subject): bool { return $attribute === "POST_EDIT"; } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { return true; } }

Check the attribute

The attribute is a plain string such as POST_EDIT or POST_VIEW that names the requested action. The supports method must return true only for attributes the voter understands.

Ignore unknown attributes by returning false, which tells Symfony to ask the next voter. Use constants or clearly spelled strings so template and controller code stay consistent.

namespace App\Security; use App\Entity\Post; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter { protected function supports(string $attribute, mixed $subject): bool { return $attribute === "POST_EDIT" || $attribute === "POST_VIEW"; } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { return true; } }

Check the subject

The subject is the object under review, for example a post entity loaded from the database. First confirm the subject type, then compare the current user with the owner stored on the object.

Read the user with getUser on the token and reject anonymous visitors before any other check. This pattern keeps ownership logic in one place instead of scattered conditions.

namespace App\Security; use App\Entity\Post; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter { protected function supports(string $attribute, mixed $subject): bool { return $attribute === "POST_EDIT" && $subject instanceof Post; } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $user = $token->getUser(); if (!$user instanceof User) { return false; } return $subject->getOwner() === $user; } }

Deny by default

A safe voter denies access unless every required condition passes. Return false for unknown users, unknown attributes, and any branch that does not grant access explicitly.

End the method with a plain false so new attributes added later stay closed until you allow them. This default closed rule prevents accidental grants when the code evolves.

namespace App\Security; use App\Entity\Post; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter { protected function supports(string $attribute, mixed $subject): bool { return $attribute === "POST_EDIT" && $subject instanceof Post; } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $user = $token->getUser(); if (!$user instanceof User) { return false; } if ($attribute === "POST_EDIT") { return $subject->getOwner() === $user; } return false; } }

Next: Console commands

Article author: Arthur Isaev

Related articles
Development with PHP
What is Symfony and where it runs
Install Symfony
Your first page
Routes
Controllers
Twig basics
Twig advanced
Doctrine setup
Entities
Migrations
Repositories
Relations
Forms
Validation
Services and DI
JSON controllers
Serializer
Security intro
Users
Login form
Voters
Console commands
Events
Messenger
Mailer
Cache
Sessions
CSRF
Translations
Dotenv and env
Profiler
Testing
Fixtures
Pagination
File uploads
API Platform in one lesson
DQL
Transactions
Migrations advanced
Security advanced
HTTP client
Scheduler
Webhooks
Asset Mapper
Deployment
Performance
Capstone: blog
Capstone: JSON API
Modern Symfony tour
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: