doctrine/dbal: the schema-aware layer under Doctrine ORM and Laravel
Doctrine Database Abstraction Layer
At a glance
- What is it?
- DBAL is not an ORM. It is the piece that knows what your database actually looks like across seven SQL platforms, which is why migrations and schema diffing work at all.
- Who is it for?
- DBAL earns its place when you need to know what the database looks like right now, not what you think it looks like. That is schema diffing, migrations against a live database, introspection across vendors, and portable schema definition objects, and none of those belong in an ORM.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Star history
- doctrine/dbal9,700 ★ · -2
| Repository | 2026-10-07 | 2026-10-22 |
|---|---|---|
| doctrine/dbal | 9.7k | 9.7k |
-2 Stars in the last 15 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What separates DBAL from an ORM
The README expands the name and then stops, which undersells the thing that makes the project useful. A database abstraction layer sits between your application and a specific database driver, and its job is to make the same code work against the platforms listed in the repository topics: MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, Oracle and IBM Db2.
What separates it from a plain PDO wrapper is schema introspection. The README names database schema introspection and schema management as the features, and that is the part with real leverage. Given only a connection, DBAL can report the tables, columns, indexes, foreign keys and sequences that actually exist on the server. That capability is what makes automatic migrations possible: compare an intended schema against the live one, emit the difference, apply it.
The distinction matters when you decide what to install. Doctrine ORM is built on top of DBAL and uses its schema layer for its own migrations. If you install DBAL directly you are choosing the lower half: connections, platforms, query building and schema work, without entities, a unit of work, proxies or identity mapping. Those are real features you give up, and they are not things DBAL does badly, they are simply out of scope.
Four maintained branches and what the default branch tells you
The README's badge table is the most informative part of the file and it is easy to skim past. Four version lines are shown: 5.0 under development, 4.5 under development, 4.4, and 3.10. Each column carries its own CI badge and its own code coverage badge, which means each branch is built and measured independently rather than one branch being tested and the others assumed.
The repository's default branch is `4.4.x`, which tells you where a new installation is expected to land. That is the branch to read if you are starting fresh. The 3.10 line exists for applications that have not migrated and 5.0 and 4.5 are where the next changes land.
Recent releases show the shape of the work. Version 4.4.3, published 2026-03-20, contained two fixes: JSON object mapping and creating a union main part for a common table expression in the query builder. Version 4.4.4, published 2026-07-21, resolved one issue and merged eighteen pull requests, including detecting a lost PostgreSQL connection, distinguishing table names that differ only in case, and applying an index column length to the correct column. Version 3.10.6 shipped the same day as 4.4.4 and was mostly documentation and dependency bumps.
That last detail is the one to notice: the 3.x line is still receiving maintenance releases on the same day as 4.4.x, including a documentation change explaining the real reason implicit indexes exist. It is not abandoned.
What the repository tree says about the engineering setup
The root is short and tells you how the project is checked. There is `composer.json`, and three separate tool configurations: `phpcs.xml.dist` for coding standards, `phpstan.neon.dist` for static analysis with a matching `phpstan-baseline.neon`, and `phpunit.xml.dist` for the test suite.
composer install
phpunit.xml.dist
phpstan.neon.dist
phpcs.xml.distThe presence of a baseline file is the interesting detail. A baseline records the static analysis errors that are known and accepted, which is how a project raises its type coverage without blocking on a backlog. It also means the analysis is not clean in absolute terms, and a baseline can hide new debt if it is edited carelessly.
`ci/` holds the continuous integration configuration and `static-analysis/` is a separate top-level directory rather than being folded into `tests/`, which suggests the static analysis setup is substantial enough to deserve its own place. `docs/` holds the documentation source, `.doctrine-project.json` carries project metadata for the Doctrine tooling, and `SECURITY.md` and `UPGRADE.md` sit at the root as you would expect from a library with a large installed base.
The project is MIT licensed and its primary language is PHP, with a homepage on doctrine-project.org and issue tracking on GitHub.
Platform abstraction as the central design decision
Almost every design question in DBAL reduces to this one. Do you generate the vendor's own SQL, or do you emit something ANSI and hope? The project answers it by having a platform layer that knows each vendor's dialect, and the vendor list in the repository topics is effectively the list of dialects maintained.
This shows up in ordinary code. Boolean handling differs between PostgreSQL and MySQL. Identifier quoting differs. Sequence support differs, and Oracle and Db2 both have their own conventions. Without a platform layer, application code accumulates conditionals for each of these, and every new vendor adds another branch.
The cost is that the abstraction is only as good as its least capable target. Something SQLite supports but Oracle does not cannot be exposed as a first-class feature without either emulating it or restricting it. So when you are choosing between using a DBAL method and writing vendor SQL directly, the question to ask is whether the operation you need is in the portable surface at all.
The 4.4.4 release notes hint at where the sharp edges are. The fix for distinguishing table names differing only in case is a schema introspection correctness issue, and the fix for applying an index column length to the correct column is a schema definition bug. Both are in the area where an abstraction layer earns its keep and also where its bugs live.
When to use DBAL directly and when to stay out of it
The honest case for using it directly is a task where the schema is the subject: writing a migration tool, building an installer that inspects an existing database, generating documentation from a live schema, or writing a health check that verifies the deployed schema matches expectations. ORM abstractions get in the way of all four.
The honest case against is performance work on a hot path. Going through the abstraction costs you something, and if you have already chosen a database and a driver, an ORM or a query builder built for that driver will be faster. There is also the compatibility question. If you use a DBAL feature that is only well supported on one vendor, you have quietly tied yourself to that vendor while keeping the abstraction's overhead.
A third case is simply not needing it. Plenty of PHP applications talk to one database through PDO and would be worse off with another layer between them and the driver. Adding DBAL to a project that does not introspect schemas is overhead with no return.
The comparison the README implicitly invites is with the driver, not with an ORM. DBAL wraps a platform-specific driver and adds schema knowledge on top. If you are choosing between DBAL and using the driver's API directly, the deciding question is whether you need portable schema handling.
Where the README ends and the documentation begins
The README is a landing page. Its one paragraph of prose describes the project, and everything else is badge definitions and links. The three resources it lists are the project website, the documentation at docs.doctrine-project.org, and the issue tracker.
That is appropriate for a library with an installed base this size, but it means the README settles nothing about how the platform layer actually behaves. Questions like what happens when a vendor lacks a feature, or how schema comparison handles a type that maps differently on two databases, are answered in the documentation tree or not at all.
The release notes are the second documentation source and they are more revealing than the README, because a patch release that fixes detection of a lost PostgreSQL connection tells you the failure was real and reported by someone running it in production. The 3.10.6 documentation change explaining why implicit indexes exist is the same kind of signal: users were misunderstanding behaviour that the API had always had.
For upgrade planning, `UPGRADE.md` in the repository root is the file to read first when moving between major lines. The 3.10 and 4.x lines both being maintained does not mean the platform API is the same in both.
Editorial conclusion
DBAL earns its place when you need to know what the database looks like right now, not what you think it looks like. That is schema diffing, migrations against a live database, introspection across vendors, and portable schema definition objects, and none of those belong in an ORM. Reach for the platform-specific driver instead when you want maximum performance and handle vendor differences yourself. Start on the 4.4 branch, which is what the default branch points at, and read `UPGRADE.md` before moving between 3.10 and 4.x, because the platform API changed substantially. Version 4.4.4 shipped on 2026-07-21 and the last push was on 2026-09-12.
Frequently asked questions
Is Doctrine an ORM?
Doctrine is the project, and it contains both. Doctrine DBAL is the database abstraction layer with schema introspection and schema management, while Doctrine ORM is the object mapper built on top of it. DBAL on its own gives you connections, platform dialects, a query builder and schema comparison, with no entities or unit of work.
What does Doctrine DBAL do that PDO does not?
PDO connects and runs queries. DBAL connects, runs queries through a query builder, and can also report the tables, columns, indexes, foreign keys and sequences that exist on a live database. That introspection layer is what makes schema diffing and migrations against a real database possible.
Which databases does Doctrine DBAL support?
The repository topics name the platform set it maintains: MySQL, MariaDB, PostgreSQL, SQLite, SQL Server, Oracle and IBM Db2, with database as the general topic. Support is implemented as a platform layer that knows each vendor's SQL dialect, which is also why the abstract surface is limited to what all of them can do.
Which version of Doctrine DBAL should I use?
The default branch is 4.4.x, so that is where a new installation should start, and 4.4.4 was published on 2026-07-21. The 3.10 line is still receiving maintenance releases, including one on the same day, so it is a supported choice for an existing application rather than an abandoned one. Read UPGRADE.md before moving between the two.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/doctrine-dbal)Star history chart for your README
[](https://hysenlabs.com/projects/doctrine-dbal)