a5sys/acl-doctrine-filter-bundle
Installation
composer require a5sys/acl-doctrine-filter-bundle
Add the bundle to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):
A5sys\AclDoctrineFilterBundle\AclDoctrineFilterBundle::class => ['all' => true],
Configure Doctrine Filter
In config/packages/doctrine.yaml (Symfony 4+):
doctrine:
orm:
filters:
acl:
class: 'A5sys\AclDoctrineFilterBundle\Filter\AclFilter'
enabled: true
Annotate an Entity
Add @AclAnnotation to your entity (e.g., Project):
use A5sys\AclDoctrineFilterBundle\Annotation\AclAnnotation;
/**
* @AclAnnotation(aclSql="##TABLEALIAS##.id IN (SELECT project.id FROM user_project WHERE user_project.user_id = ##USERID## AND user_project.project_id = ##TABLEALIAS##.id)")
*/
class Project {}
Enable the Filter In your repository or service, enable the filter:
$em->getFilters()->enable('acl');
Fetch projects accessible by the current user:
$projects = $projectRepo->findBy([]); // Automatically filtered by ACL
Role-Based Exclusion
Disable ACL for specific roles (e.g., admins) in config/packages/acl_doctrine_filter.yaml:
acl_doctrine_filter:
no_acl_roles:
- "ROLE_ADMIN"
Dynamic Filtering Enable/disable the filter dynamically:
// Enable for a specific query
$em->getFilters()->enable('acl');
$projects = $projectRepo->findAll();
$em->getFilters()->disable('acl'); // Reset
// Or per-repository
$projectRepo->getEntityManager()->getFilters()->enable('acl');
Complex ACL Logic
Use SQL placeholders (##TABLEALIAS##, ##USERID##) for joins/subqueries:
@AclAnnotation(aclSql="##TABLEALIAS##.id IN (
SELECT p.id FROM Project p
JOIN p.userProjects up
WHERE up.user = ##USERID##
)")
Symfony Security Integration
Inject the Security component to fetch the current user’s ID:
$userId = $this->security->getUser()->getId();
// Use in custom filter logic if extending the bundle.
prePersist/preRemove to update ACL metadata.@ApiResource entities by ensuring the filter is enabled in the controller.$em->getFilters()->disable('acl'); // Bypass ACL for testing
Deprecation Warning The bundle is deprecated in favor of Doctrine’s built-in filters. Migrate to:
# config/packages/doctrine.yaml
doctrine:
orm:
filters:
acl:
class: Doctrine\ORM\Mapping\ClassMetadataFilter
params:
userId: 1 # Dynamically set via event listener
SQL Injection Risk
The aclSql annotation directly interpolates ##USERID##. Sanitize inputs if using dynamic values outside the bundle’s scope.
Performance Overhead
Complex subqueries in aclSql can slow queries. Optimize with indexes on join columns (e.g., user_project.user_id, user_project.project_id).
Filter Scope The filter applies globally to all queries on annotated entities. Disable it explicitly when needed (e.g., admin dashboards).
var_dump($em->getFilters()->isEnabled('acl')); // Should return true
config/packages/dev/doctrine.yaml:
doctrine:
dbal:
logging: true
profiling: true
Look for WHERE clauses added by the filter.Custom Filter Logic
Extend the AclFilter class to add logic (e.g., role-based overrides):
class CustomAclFilter extends AclFilter {
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) {
if ($this->security->isGranted('ROLE_ADMIN')) {
return ''; // Bypass filter for admins
}
return parent::addFilterConstraint($targetEntity, $targetTableAlias);
}
}
Register it in config/packages/doctrine.yaml:
filters:
acl:
class: AppBundle\Filter\CustomAclFilter
Dynamic User ID
Override getParameter() in the filter to fetch the user ID from a custom source (e.g., API token):
protected function getParameter($name) {
if ($name === 'userId') {
return $this->tokenStorage->getToken()->getUser()->getId();
}
return parent::getParameter($name);
}
Composite Entities
For entities with multiple ACL tables (e.g., UserProject and ProjectRole), combine annotations:
@AclAnnotation(aclSql="##TABLEALIAS##.id IN (
SELECT project.id FROM user_project
WHERE user_project.user_id = ##USERID##
UNION
SELECT project.id FROM project_role
WHERE project_role.user_id = ##USERID##
)")
How can I help you explore Laravel packages today?