1. Go to this page and download the library: Download nexara/api-platform-voter library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
nexara / api-platform-voter example snippets
use ApiPlatform\Metadata\ApiResource;
use Nexara\ApiPlatformVoter\Attribute\Secured;
#[ApiResource]
#[Secured(prefix: 'article', voter: ArticleVoter::class)]
class Article
{
// Your entity properties...
}
namespace App\Security\Voter;
use App\Entity\Article;
use Nexara\ApiPlatformVoter\Voter\CrudVoter;
use Symfony\Bundle\SecurityBundle\Security;
final class ArticleVoter extends CrudVoter
{
public function __construct(private readonly Security $security)
{
$this->autoConfigure(); // ✨ Zero config!
}
protected function canCreate(): bool
{
return $this->security->isGranted('ROLE_USER');
}
protected function canUpdate(mixed $object, mixed $previousObject): bool
{
return $object->getAuthor() === $this->security->getUser();
}
protected function canDelete(mixed $object): bool
{
return $this->security->isGranted('ROLE_ADMIN');
}
}
final class ArticleVoter extends CrudVoter
{
public function __construct(private readonly Security $security)
{
$this->configure()
->prefix('article')
->resource(Article::class)
->autoDiscoverOperations(); // Auto-finds can* methods
}
protected function canUpdate(mixed $object, mixed $previousObject): bool
{
return $object->getAuthor() === $this->security->getUser();
}
}
final class ArticleVoter extends CrudVoter
{
public function __construct(private readonly Security $security)
{
$this->setPrefix('article');
$this->setResourceClasses(Article::class);
}
protected function canUpdate(mixed $object, mixed $previousObject): bool
{
return $object->getAuthor() === $this->security->getUser();
}
}
#[ApiResource(
operations: [
new Post(
uriTemplate: '/articles/{id}/publish',
name: 'publish',
// ... other config
),
]
)]
#[Secured(voter: ArticleVoter::class)]
class Article
{
// ...
}
final class ArticleVoter extends CrudVoter
{
// ... other methods
protected function canPublish(mixed $object, mixed $previousObject): bool
{
// Custom logic for publish operation
return $this->security->isGranted('ROLE_MODERATOR')
&& $object->getStatus() === 'draft';
}
}
final class ArticleVoter extends CrudVoter
{
public function __construct(private readonly Security $security)
{
$this->autoConfigure(); // Reads from #[Secured] + VoterRegistry
}
}
final class ArticleVoter extends CrudVoter
{
public function __construct(private readonly Security $security)
{
$this->configure()
->prefix('article')
->resource(Article::class)
->operations('publish', 'archive')
->autoDiscoverOperations(); // Auto-finds can* methods
}
}
final class ArticleVoter extends CrudVoter
{
public function __construct(private readonly Security $security)
{
$this->setPrefix('article');
$this->setResourceClasses(Article::class);
}
}
#[Secured(
prefix: 'blog_post',
voter: BlogPostVoter::class
)]
class Article { }
public function __construct(
private readonly Security $security,
) {
$this->setPrefix('article');
$this->setResourceClasses(Article::class);
}
protected function canUpdate(mixed $object, mixed $previousObject): bool
{
$user = $this->security->getUser();
return $user && $object->getAuthor() === $user;
}
protected function canUpdate(mixed $object, mixed $previousObject): bool
{
// Prevent changing the author
if ($object->getAuthor() !== $previousObject->getAuthor()) {
return $this->security->isGranted('ROLE_ADMIN');
}
return $object->getAuthor() === $this->security->getUser();
}
public function __construct()
{
$this->setPrefix('content');
$this->setResourceClasses(Article::class, BlogPost::class, Page::class);
}
use Nexara\ApiPlatformVoter\GraphQL\GraphQLCrudVoter;
final class ArticleVoter extends GraphQLCrudVoter
{
protected function canAccessField(string $fieldName, mixed $object): bool
{
return match ($fieldName) {
'email' => $this->security->isGranted('ROLE_ADMIN'),
'internalNotes' => $object->getAuthor() === $this->security->getUser(),
default => true,
};
}
protected function canModifyField(string $fieldName, mixed $object, mixed $newValue): bool
{
return match ($fieldName) {
'author' => $this->security->isGranted('ROLE_ADMIN'),
'publishedAt' => $this->security->isGranted('ROLE_MODERATOR'),
default => true,
};
}
}
use Nexara\ApiPlatformVoter\Voter\CrudVoter;
use Nexara\ApiPlatformVoter\MultiTenancy\TenantAwareVoterTrait;
final class ArticleVoter extends CrudVoter
{
use TenantAwareVoterTrait;
protected function canUpdate(mixed $object, mixed $previousObject): bool
{
// TenantContext is automatically injected
if (!$this->belongsToCurrentTenant($object)) {
return false;
}
return $object->getAuthor() === $this->security->getUser();
}
}
use Nexara\ApiPlatformVoter\Debug\VoterChainVisualizer;
$visualizer = new VoterChainVisualizer($debugger);
// Text visualization
echo $visualizer->visualize('article:update');
// Tree visualization
echo $visualizer->visualizeAsTree('article:update');
// Summary
echo $visualizer->summarize('article:update');
use Nexara\ApiPlatformVoter\Testing\VoterTestTrait;
use PHPUnit\Framework\TestCase;
class ArticleVoterTest extends TestCase
{
use VoterTestTrait;
public function testModeratorCanPublish(): void
{
$user = $this->createUser(['ROLE_MODERATOR']);
// Creates Security with proper role hierarchy
$security = $this->createSecurityWithRoleHierarchy([
'ROLE_ADMIN' => ['ROLE_MODERATOR', 'ROLE_USER'],
'ROLE_MODERATOR' => ['ROLE_USER'],
], $user);
$voter = new ArticleVoter($security);
// Now $security->isGranted('ROLE_USER') returns true for MODERATOR
$article = new Article();
$this->assertTrue($voter->canPublish($article, null));
}
}
use Nexara\ApiPlatformVoter\Testing\SecurityBuilder;
$security = SecurityBuilder::create()
->withRoleHierarchy([
'ROLE_ADMIN' => ['ROLE_MODERATOR', 'ROLE_USER'],
'ROLE_MODERATOR' => ['ROLE_USER'],
])
->withUser($user)
->build();
$voter = new ArticleVoter($security);
use Nexara\ApiPlatformVoter\Testing\VoterTestCase;
class ArticleVoterTest extends VoterTestCase
{
protected function createVoter(): VoterInterface
{
return new ArticleVoter($this->createMock(Security::class));
}
public function testGrantsAccess(): void
{
$this->mockUser(['ROLE_USER']);
$this->assertVoterGrants('article:create', new Article());
}
public function testDeniesAccess(): void
{
$this->mockAnonymousUser();
$this->assertVoterDenies('article:delete', new Article());
}
}
bash
php bin/console make:api-resource-voter
bash
php bin/console voter:analyze-migration
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.