CLI Commands¶
The bundle provides three console commands: tenancy:init for scaffolding configuration,
tenancy:migrate for running Doctrine Migrations across all tenants, and tenancy:run for
executing any Symfony console command within a specific tenant's context.
tenancy:init¶
Scaffold a fully commented config/packages/tenancy.yaml with all configuration keys, Doctrine
detection, and next-steps guidance.
Usage¶
# First-time setup — creates config/packages/tenancy.yaml
bin/console tenancy:init
# Regenerate (overwrite existing file)
bin/console tenancy:init --force
Behavior¶
- File creation: Creates
config/packages/tenancy.yamlin the project root. If theconfig/packages/directory does not exist, it is created automatically. - Overwrite protection: If
config/packages/tenancy.yamlalready exists and--forceis NOT passed, the command prints a warning and exits with failure. Pass--forceto overwrite. - Doctrine detection: The command checks
interface_exists(EntityManagerInterface::class). If Doctrine ORM is installed, it recommendsdatabase_per_tenantas the driver and uncomments thedriver:line in the generated YAML. If Doctrine is absent, it recommendsshared_dband leaves the driver line commented. - Next-steps guidance: After creating the file, the command prints actionable next steps (review config, create Tenant entity, configure app_domain, run schema update).
- No dependencies: The command is always registered — no optional packages required.
Output¶
When Doctrine ORM is detected:
[OK] Created config/packages/tenancy.yaml
Doctrine ORM detected — recommended driver: database_per_tenant
Uncomment driver and set database.enabled: true in your config.
Next Steps
----------
* Review and uncomment the configuration values in config/packages/tenancy.yaml
* Create your Tenant entity implementing Tenancy\Bundle\TenantInterface
* Configure your host.app_domain if using subdomain-based resolution
* Run bin/console doctrine:schema:update or create migrations for the Tenant entity
* Visit https://github.com/danplaton4/tenancy-bundle for full documentation
When Doctrine ORM is NOT detected:
[OK] Created config/packages/tenancy.yaml
Doctrine ORM not detected — recommended driver: shared_db
Install doctrine/orm to use database_per_tenant mode.
Next Steps
----------
* Review and uncomment the configuration values in config/packages/tenancy.yaml
* Create your Tenant entity implementing Tenancy\Bundle\TenantInterface
* Configure your host.app_domain if using subdomain-based resolution
* Run bin/console doctrine:schema:update or create migrations for the Tenant entity
* Visit https://github.com/danplaton4/tenancy-bundle for full documentation
tenancy:migrate¶
Run Doctrine Migrations for all tenants or a single tenant.
Requirements¶
Prerequisites
tenancy:migrate requires both:
tenancy.database.enabled: true(database-per-tenant driver)doctrine/migrationspackage installed:composer require doctrine/migrations
The command is silently unavailable if either requirement is missing.
Usage¶
# Run pending migrations for all tenants
bin/console tenancy:migrate
# Run pending migrations for a single tenant only
bin/console tenancy:migrate --tenant=acme
Output¶
The command iterates all tenants from TenantProviderInterface::findAll(), boots each tenant's
context, runs pending migrations, and reports per-tenant results:
✓ acme
✓ demo
✗ broken-tenant (Connection refused: mysql:host=broken-host;dbname=broken_db)
Completed: 2 succeeded, 1 failed
Failed tenants:
- broken-tenant
Behavior¶
- Continue on failure: The command does not stop on the first error. If one tenant's migration fails, it continues with the remaining tenants and reports all failures at the end.
- Exit code: Returns
1if any tenant migration failed,0if all succeeded. - No-op tenants: Tenants with no pending migrations are silently skipped.
- Shared-DB guard: Running
tenancy:migratewithdriver: shared_dbreturns an error immediately — the command only applies to database-per-tenant mode.
How It Works¶
For each tenant, the command:
- Sets the tenant in
TenantContextand callsBootstrapperChain::boot()— this switches the DBAL connection to the tenant's database. - Creates a
DependencyFactoryusing the switched DBAL connection. - Runs all pending migrations up to
latest. - Calls
BootstrapperChain::clear()in afinallyblock to reset the connection.
This means each tenant migration runs against the correct isolated database, not a shared one.
tenancy:run¶
Wrap any Symfony console command with a specific tenant's context.
Usage¶
Examples¶
# Clear cache for tenant 'acme'
bin/console tenancy:run acme "cache:clear"
# Import data for tenant 'demo'
bin/console tenancy:run demo "app:import-products --format=csv"
# Run a custom application command for tenant 'beta'
bin/console tenancy:run beta "app:send-reports --period=monthly"
How It Works¶
tenancy:run spawns a subprocess (via symfony/process) that runs bin/console with the inner
command, appending --tenant=<slug>. The ConsoleResolver in the child process picks up the
--tenant argument and resolves the tenant before the command executes:
The tenant slug is validated before the subprocess is spawned — if the tenant does not exist or is inactive, the command fails immediately with a clear error.
Output Forwarding¶
All stdout and stderr from the subprocess are forwarded in real time to the parent process output.
Exit codes are propagated — if the inner command returns a non-zero exit code, tenancy:run
propagates it.
Requirements¶
symfony/process is a production dependency of the bundle (it is promoted from require-dev).
No additional installation is needed.
See Also¶
- Installation — initial setup with tenancy:init
- Database-per-Tenant — connection switching mechanics
- Testing — running tests with tenant context
- Architecture: DI Compilation Pipeline