Installation:
composer require curiosity26/acl-helper-bundle
Ensure the bundle is enabled in config/bundles.php:
return [
// ...
Curiosity26\AclHelperBundle\Curiosity26AclHelperBundle::class => ['all' => true],
];
First Use Case:
Inject AclHelper into your controller/service and query ACL-filtered entities directly:
use Curiosity26\AclHelperBundle\Helper\AclHelper;
use Symfony\Component\Security\Acl\Permission\BasicPermissionMap;
public function __construct(private AclHelper $aclHelper) {}
public function listAction(): array
{
$agent = $aclHelper->createAgent(MyEntity::class);
$permMap = new BasicPermissionMap();
$query = $aclHelper->getQueryBuilder(MyEntity::class, $agent, $permMap, 'VIEW');
return $query->getQuery()->getResult();
}
Key Files:
src/Helper/AclHelper.php: Core logic for ACL-aware queries.Tests/ directory: Example implementations (e.g., TestObject).Resources/config/services.yaml: Service configuration.Agent Creation:
$agent = $aclHelper->createAgent(MyEntity::class);
Permission Mapping:
Use BasicPermissionMap or extend it for custom permissions:
$permMap = new BasicPermissionMap();
$permMap->addPermission('EDIT'); // Add custom permissions if needed
Query Construction:
$queryBuilder = $aclHelper->getQueryBuilder(
MyEntity::class,
$agent,
$permMap,
'VIEW' // Permission to check
);
QueryBuilder pre-filtered for ACLs.->setMaxResults(10)).Integration with Doctrine:
$entities = $queryBuilder
->where('e.active = :active')
->setParameter('active', true)
->getQuery()
->getResult();
Dynamic Permissions: Pass a closure for dynamic permission checks:
$queryBuilder = $aclHelper->getQueryBuilder(
MyEntity::class,
$agent,
$permMap,
fn(MyEntity $entity) => $entity->isEditableBy($user)
);
Bulk Operations:
Use getQueryBuilder() for DELETE/UPDATE queries to filter records by ACLs:
$queryBuilder->delete();
Caching:
Cache QueryBuilder instances if permissions rarely change:
$cacheKey = 'acl_query_' . $userId . '_' . md5(serialize($permMap));
$queryBuilder = $cache->get($cacheKey, fn() => $aclHelper->getQueryBuilder(...));
Associations Not Filtered:
User::with('posts')).Field-Level Security:
Performance with Large Datasets:
OR conditions) can bloat queries.owner_id and ACL-related fields.Permission Map Limitations:
BasicPermissionMap is rigid. Extend it for custom logic:
class CustomPermissionMap extends BasicPermissionMap {
public function __construct() {
$this->addPermission('APPROVE'); // Add custom permissions
}
}
Deprecated Symfony ACL:
Security.Acl component.Query Dumping: Enable Doctrine’s query logging to inspect generated SQL:
# config/packages/doctrine.yaml
doctrine:
dbal:
logging: true
logging_format: '%%timestamp%% %%sql%%'
Permission Debugging: Log the effective permissions for an entity:
$aclHelper->debugPermissions(MyEntity::class, $agent, $permMap);
Custom ACL Providers: Override the default ACL provider by binding your own service:
# config/services.yaml
services:
App\Security\CustomAclProvider:
tags: [curiosity26.acl_helper.provider]
QueryBuilder Modifiers: Subscribe to events to modify queries:
$aclHelper->addQueryModifier(function (QueryBuilder $qb, string $entityClass) {
$qb->andWhere('e.published = :published')
->setParameter('published', true);
});
Event Listeners:
Listen for acl_helper.query_built to inspect/modify queries:
$dispatcher->addListener('acl_helper.query_built', function (QueryBuiltEvent $event) {
$event->getQueryBuilder()->setMaxResults(50);
});
How can I help you explore Laravel packages today?