shoofly/doctrine-multi-schema-bundle
A Symfony bundle that extends Doctrine to support multiple database schemas, letting entities map across schemas and managing connections, migrations, and schema tooling for multi-tenant or modular database setups.
Given the package has zero stars, zero dependents, and a 0.1 score, treat it as experimental and audit the source before adopting. The first move is to read DependencyInjection/ShooflyDoctrineMultiSchemaExtension.php and ShooflyDoctrineMultiSchemaBundle.php — that's where almost all the actual wiring lives, since these bundles are usually thin compiler passes over doctrine.dbal.connections.
Minimal setup:
composer require shoofly/doctrine-multi-schema-bundle
Register the bundle in config/bundles.php:
Shoofly\DoctrineMultiSchemaBundle\ShooflyDoctrineMultiSchemaBundle::class => ['all' => true],
Then configure your connection with the multiple schemas you want Doctrine to know about:
# config/packages/shoofly_doctrine_multi_schema.yaml
shoofly_doctrine_multi_schema:
connections:
default:
schemas:
- public
- tenant_a
- tenant_b
Your first use case is almost always PostgreSQL with multiple named schemas in one database: you want a single DBAL connection, entities mapped to different schemas, and search_path handled for you. Verify it works by pulling the connection from the container and inspecting $connection->getSchemaManager()->listSchemaNames().
Schema-per-bounded-context with a shared connection. Configure one connection listing all schemas, then map entities with explicit @Table(schema="tenant_a"). This is the pattern the package is designed for — you avoid the overhead of N entity managers just to reach N schemas.
Search-path switching at runtime. For multi-tenant PostgreSQL setups where tables live in identical-shaped schemas, wrap request handling in a listener that sets the connection's search_path before any query runs. The bundle exposes the configured schema list, so the listener can validate the requested tenant against %shoofly_doctrine_multi_schema.connections.default.schemas% rather than trusting user input.
Pair with a migration strategy. DoctrineMigrations does not natively understand "one migration run, many schemas." A common pattern here is per-schema migration configuration directories, each with its own migrations_paths and its own dbname referencing the same physical database. Run them sequentially in deploy scripts.
Entity manager vs. connection split. Prefer one entity manager plus the multi-schema connection over multiple EMs. You lose the clean per-schema config isolation but gain cross-schema joins and UnitOfWork consistency. Reach for multiple EMs only if two schemas truly need disjoint mapping configs (different naming strategies, different metadata dirs).
Fixtures and tests. In KernelTestCase, override the schemas list to a test-only schema and create/drop schemas in setUp/tearDown. Because everything shares one connection, teardown is a single DROP SCHEMA ... CASCADE rather than tearing down multiple databases.
schema_filter / wrapper_class config. If your DoctrineBundle is recent, check whether native config already solves your problem: doctrine.dbal.connections.default.schema_filter plus a custom AbstractSchemaManager wrapper often removes the need for this package entirely.search_path is a PostgreSQL concept. SQLite, MySQL, and SQL Server do not have multiple schemas in the same way. The bundle will not stop you from configuring them, but queries will silently behave differently. Guard config by driver if you're not exclusively on pdo_pgsql.public by default. If search_path is stale when doctrine:migrations:diff runs, you'll diff against the wrong schema and produce phantom migrations. Always set the search path explicitly in your migration console command.schema metadata is authoritative. The bundle configures the connection, not the mapping metadata. If an entity's @Table(schema=...) disagrees with the connection schema list, Doctrine will try to qualify tables that don't exist. Keep both in sync, ideally via a constant or parameter.bin/console doctrine:query:sql "SHOW search_path" against the actual connection the bundle configured — this is the fastest way to confirm whether the bundle's compiler pass is even doing anything.$conn->executeStatement('SET search_path TO ...') yourself. That's fine — it's just not something the bundle is going to help with.How can I help you explore Laravel packages today?