The Postgres Schema Bundle provides seamless multi-tenant schema support for PostgreSQL within Symfony applications. It automatically switches PostgreSQL search_path
based on the current request context and ensures proper schema resolution across Doctrine and Messenger.## Installation
- Automatically sets PostgreSQL
search_path
from request headers. - Validates that the schema exists in the database.
- Works only if the configured database driver is PostgreSQL.
- Integrates with Schema Context Bundle.
- Compatible with Symfony Messenger and Doctrine ORM.
Use Composer to install the bundle:
composer require macpaw/postgres-schema-bundle
Enable the bundle by adding it to the list of registered bundles in config/bundles.php
// config/bundles.php
<?php
return [
// ...
Macpaw\SchemaContextBundle\SchemaContextBundle::class => ['all' => true],
Macpaw\PostgresSchemaBundle\PostgresSchemaBundle::class => ['all' => true],
];
You must tell Doctrine to use the SchemaConnection class as its DBAL connection class:
# config/packages/doctrine.yaml
doctrine:
dbal:
connections:
default:
wrapper_class: Macpaw\PostgresSchemaBundle\Doctrine\SchemaConnection
Set BaggageSchemaResolver
to SchemaConnection
at kernel boot
# src/Kernel.php
use Macpaw\PostgresSchemaBundle\Doctrine\SchemaConnection;
use Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
public function boot(): void
{
parent::boot();
SchemaConnection::setSchemaResolver(
$this->getContainer()->get(BaggageSchemaResolver::class),
);
}
// ...
}
Make sure you configure the context bundle properly:
See https://github.com/MacPaw/schema-context-bundle/blob/develop/README.md
schema_context:
app_name: '%env(APP_NAME)%' # Application name
header_name: 'X-Tenant' # Request header to extract schema name
default_schema: 'public' # Default schema to fallback to
allowed_app_names: ['develop', 'staging', 'test'] # App names where schema context is allowed to change
- A request comes in with a header like X-Tenant-Id: tenant123.
- The SchemaRequestListener sets this schema in the context.
- When Doctrine connects to PostgreSQL, it sets the search_path to the specified schema.
- If the schema does not exist or DB is not PostgreSQL, an exception is thrown.
The bundle provides three optional commands for schema management that can be registered in your services configuration:
Drops a PostgreSQL schema and all its objects:
# config/services.yaml
services:
SharedServices\Command\Doctrine\DoctrineSchemaDropCommand: ~
Usage:
php bin/console doctrine:schema:delete <schema_name>
Runs Doctrine migrations within a specific schema. Creates the schema if it doesn't exist:
# config/services.yaml
services:
SharedServices\Command\Doctrine\DoctrineSchemaMigrationsMigrateCommand:
arguments:
- '@doctrine_migrations.migrate_command'
Usage:
php bin/console doctrine:schema:migrations:migrate <schema_name> [options]
Loads Doctrine fixtures within a specific schema:
# config/services.yaml
services:
SharedServices\Command\Doctrine\DoctrineSchemaFixturesLoadCommand:
arguments:
- '@doctrine.fixtures_load_command'
Usage:
php bin/console doctrine:schema:fixtures:load <schema_name> [options]
Note: These commands are optional and should only be registered if you're using the corresponding Doctrine features (migrations and/or fixtures) in your project.
To run tests:
vendor/bin/phpunit
Feel free to open issues and submit pull requests.
This bundle is released under the MIT license.