Filter And Scoped Data
This is one of the most practical parts of account-bundle: turning the current account route context into automatic read and write scoping for account-owned entities.
The Usual Account-Scoped Pattern
The normal flow is:
- the route contains
/{_account} - the bundle resolves that value to the current account
- Doctrine enables the
accountfilter for the request - entities implementing
AccountScopedInterfaceare limited to the current account automatically
The filter affects SQL generated by Doctrine. It does not replace authorization checks, raw SQL conditions, or validation of an explicitly supplied account. Test with two accounts and verify that records from the other account are excluded.
Requirements For Automatic Filtering
For automatic filtering to work:
- the route must carry the current account, usually as
/{_account} - the entity must implement
AccountScopedInterface - the entity must expose an account relation compatible with the bundled filter
sfs_account.filter.enabledmust be true
SQL Filter Assumption
The bundled SQL filter adds a condition like:
<table_alias>.account_id = :_account
So the default implementation assumes:
- the entity has an
account_idcolumn - the current account should be matched against that column
If your mapping uses another column name or another ownership strategy, treat the bundled filter as a starting point and replace it.
Automatic Account On PrePersist
AccountFilteredEventListener also helps on writes.
When an entity:
- implements
AccountScopedInterface - does not already have an account
- exposes
getAccount()andsetAccount()throughAccountScopedInterface
the listener copies the current request account into the entity during Doctrine prePersist.
This is useful for account-aware create flows:
$project = new Project();
$project->setName('Intranet redesign');
$entityManager->persist($project);
$entityManager->flush();
If the current request resolves an account, the entity receives it automatically. An explicitly assigned account is preserved. In commands, jobs, and tests without a request, call $project->setAccount($account) yourself.
Good Use Cases
This pattern works especially well for:
- projects
- invoices
- subscriptions
- customer records
- provider resources
- account-specific settings
These are entities that belong clearly to one account and should never leak across account contexts.
Limitations To Keep In Mind
- the bundled filter expects
account_id - CLI commands and jobs need an explicit account context and assignment
- complex ownership models may need a custom filter or custom persistence logic
The bundle removes a lot of repeated andWhere('entity.account = :account'), but it is still a base implementation, not a universal multitenancy engine.