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:

  1. the route contains /{_account}
  2. the bundle resolves that value to the current account
  3. Doctrine enables the account filter for the request
  4. entities implementing AccountScopedInterface are 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.enabled must 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_id column
  • 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() and setAccount() through AccountScopedInterface

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.