The Doctrine database abstraction & access layer (DBAL) offers a leightweight and thin runtime layer around a PDO-like API and a lot of additional, horizontal features like database schema introspection and manipulation through an OO API.
The fact that the Doctrine DBAL abstracts the concrete PDO API away through the use of interfaces that closely resemble the existing PDO API
makes it possible to implement custom drivers that may use existing native or self-made APIs. For example, the DBAL ships with a driver for Oracle databases
The `DriverManager` returns an instance of `Doctrine\DBAL\Connection` which is a wrapper around the underlying driver connection (which is often a PDO instance).
The following sections describe the available connection parameters in detail.
+++ Driver
The driver specifies the actual implementations of the DBAL interfaces to use. It can be configured in one of three ways:
* `driver`: The built-in driver implementation to use. The following drivers are currently available:
* `pdo_mysql`: A MySQL driver that uses the pdo_mysql PDO extension.
* `pdo_sqlite`: An SQLite driver that uses the pdo_sqlite PDO extension.
* `pdo_pgsql`: A PostgreSQL driver that uses the pdo_pgsql PDO extension.
* `pdo_oci`: An Oracle driver that uses the pdo_oci PDO extension. **Note that this driver caused problems in our tests. Prefer the oci8 driver if possible.**
* `oci8`:` An Oracle driver that uses the oci8 PHP extension.
* `driverClass`: Specifies a custom driver implementation if no 'driver' is specified. This allows the use of custom drivers that are not part of the Doctrine DBAL itself.
* `pdo`: Specifies an existing PDO instance to use.
`Doctrine\DBAL\Events::postConnect` is triggered right after the connection to the database is established. It allows
to specify any relevant connection specific options and gives access to the `Doctrine\DBAL\Connection` instance
that is responsible for the connection management via an instance of `Doctrine\DBAL\Event\ConnectionEventArgs`
event arguments instance.
Doctrine is already shipped with two implementations for the "PostConnect" event:
* `Doctrine\DBAL\Event\Listeners\OracleSessionInit` allows to specify any number of Oracle Session related enviroment variables that are set right after the connection is established.
* `Doctrine\DBAL\Event\Listeners\MysqlSessionInit` allows to specify the Charset and Collation of the Client Connection if these options are not configured correctly on the MySQL server side.
You can register events by subscribing them to the `EventManager` instance passed to the Connection factory:
The DBAL is seperated into several different packages that perfectly seperate responsibilities of the different RDBMS layers.
* **Drivers** abstract a PHP specific database API by enforcing two interfaces `\Doctrine\DBAL\Driver\Driver` and `\Doctrine\DBAL\Driver\Statement` which require exactly the same methods as PDO.
* **Platforms** abstract the generation of queries and which database features a platform supports. The `\Doctrine\DBAL\Platforms\AbstractPlatform` defines the common denominator of what a database platform has to publish to the userland, to be fully supportable by Doctrine. This includes the SchemaTool, Transaction Isolation and many other features. The Database platform for MySQL for example can be used by all 3 mysql extensions, PDO, Mysqli and ext/mysql.
* **Logging** holds the interface and some implementations for debugging of Doctrine SQL query execution during a request.
* **Schema** offers an API for each database platform to execute DDL statements against your platform or retrieve metadata about it. It also holds the Schema Abstraction Layer which is used by the different Schema Management facilities of Doctrine DBAL and ORM.
* **Types** offers an abstraction layer for the converting and generation of types between Databases and PHP.
* `prepare($sql)` - Prepare a given sql statement and return the `\Doctrine\DBAL\Driver\Statement` instance.
* `executeUpdate($sql, array $params)` - Executes a prepared statement with the given sql and parameters and returns the affected rows count.
* `execute($sql, array $params)` - Creates a prepared statement for the given sql and passes the parameters to the execute method, then returning the statement.
* `fetchAll($sql, array $params)` - Execute the query and fetch all results into an array.
* `fetchArray($sql, array $params)` - Numeric index retrieval of first result row of the given query.
* `fetchBoth($sql, array $params)` - Both numeric and assoc column name retrieval of the first result row.
* `fetchColumn($sql, array $params, $colnum)` - Retrieve only the given column of the first result row.
* `fetchRow($sql, array $params)` - Retrieve assoc row of the first result row.
* `select($sql, $limit, $offset)` - Modify the given query with a limit clause.
There are also convenience methods for data manipulation queries:
* `delete($tableName, array $identifier)` - Delete all rows of a table matching the given identifier, where keys are column names.
* `insert($tableName, array $data)` - Insert a row into the given table name using the key value pairs of data.
* `update($tableName, array $data, array $identifier)` - Update all rows for the matching key value identifiers with the given data.
By default the Doctrine DBAL does no escaping. Escaping is a very tricky business to do automagically, therefore there is none by default. The ORM internally escapes all your values, because it has lots of metadata available about the current context. When you use the Doctrine DBAL as standalone, you have to take care of this yourself. The following methods help you with it:
* `quote($input, $type=null)` - Quote a value
* `quoteIdentifier($identifier)`- Quote an identifier according to the platform details.
However, **a rollback in a nested transaction block will always mark the current transaction so that the only possible outcome of the transaction is to be rolled back**.
That means in the above example, the rollback in the inner transaction block marks the whole transaction for rollback only.
Even if the nested transaction block would not rethrow the exception, the transaction is marked for rollback only and the commit of
the outer transaction would trigger an exception, leading to the final rollback.
This also means that you can not successfully commit some changes in an outer transaction if an inner transaction block fails and issues a rollback,
even if this would be the desired behavior (i.e. because the nested operation is "optional" for the purpose of the outer transaction block).
To achieve that, you need to restructure your application logic so as to avoid nesting transaction blocks. If this is not possible
because the nested transaction blocks are in a third-party API you're out of luck.
All that is guaruanteed to the inner transaction is that it still happens atomically, all or nothing, the transaction just gets a wider scope
and the control is handed to the outer scope.
> *CAUTION*
> The transaction nesting described here is a debated feature that has it's critics. Form your own opinion.
> We recommend avoiding nesting transaction blocks when possible, and most of the time, it is possible.
> Transaction control should mostly be left to a service layer and not be handled in data access objects or similar.
-
> **CAUTION**
> Directly invoking `PDO#beginTransaction()`, `PDO#commit()` or `PDO#rollback()` or the
> corresponding methods on the particular `Doctrine\DBAL\Driver\Connection` instance in
> use bybasses the transparent transaction nesting that is provided by
> `Doctrine\DBAL\Connection` and can therefore corrupt the nesting level, causing errors
> with broken transaction boundaries that may be hard to debug.
Doctrine has a very powerful abstraction of database schemas. It offers an object-oriented representation of a database schema with support for all the details of Tables, Sequences, Indexes and Foreign Keys. These Schema instances generate a representation that is equal for all the supported platforms. Internally this functionality is used by the ORM Schema Tool to offer you create, drop and update database schema methods from your Doctrine ORM Metadata model. Up to very specific functionality of your database system this allows you to generate SQL code that makes your Domain model work.
You will be pleased to hear, that Schema representation is completly decoupled from the Doctrine ORM though, that is you can also use it in any other project to implement database migrations or for SQL schema generation for any metadata model that your application has. You can easily generate a Schema, as a simple example shows:
$queries = $schema->toSql($myPlatform); // get queries to create this schema.
$dropSchema = $schema->toDropSql($myPlatform); // get queries to safely delete this schema.
Now if you want to compare this schema with another schema, you can use the `Comparator` class to get instances of `SchemaDiff`, `TableDiff` and `ColumnDiff`, aswell as information about other foreign key, sequence and index changes.
[php]
$comparator = new \Doctrine\DBAL\Schema\Comparator();
The Save Diff mode is a specific mode that prevents the deletion of tables and sequences that might occour when making a diff of your schema. This is often necessary when your target schema is not complete but only describes a subset of your application.
All methods that generate SQL queries for you make much effort to get the order of generation correct, so that no problems will ever occour with missing links of foreign keys.
++ Platforms
Platforms abstract query generation and specifics of the RDBMS featuresets. In most cases you don't need to interact with this package alot, but there might be certain cases when you are programming database independent where you want to access the platform to generate queries for you.
The platform can be accessed from any `Doctrine\DBAL\Connection` instance by calling the `getDatabasePlatform()` method.
You can use your own platform by specifying the 'platform' key with an instance of your own platform:
[php]
$myPlatform = new MyPlatform();
$options = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite',
'platform' => $myPlatform
);
This way you can optimize your schema or generated SQL code with features that might not be portable for instance,
however are required for your special needs.
++ Schema Manager
A Schema Manager instance helps you with the abstraction of the generation of SQL assets such as Tables, Sequences, Foreign Keys and Indexes. You can use any of the Schema Asset classes `Table`, `Sequence`, `ForeignKeyConstraint` and `Index` for use with the methods of the style `dropAndCreate(AssetName)($asset)`, `drop(AssetName)($asset)` and `create(AssetName)($asset)`.
You also have methods to retrieve instances of those types from the current database you are connected to. These methods are:
* `listDatabases()`
* `listSequences()`
* `listTableColumns($tableName)`
* `listTableDetails($tableName)`
* `listTableForeignKeys($tableName)`
* `listTableIndexes($tableName)`
* `listTables()`
* `listUsers()`
* `listViews()`
For a complete representation of the current database you can use the `createSchema()` method which returns an instance of Schema, which you can use in conjunction with the SchemaTool or Schema Comparator.
++ Supporting other Databases
To support a database which is not currently shipped with Doctrine you have to implement the following interfaces and abstract classes:
* `\Doctrine\DBAL\Driver\Driver`
* `\Doctrine\DBAL\Driver\Statement`
* `\Doctrine\DBAL\Platforms\AbstractPlatform`
* `\Doctrine\DBAL\Schema\AbstractSchemaManager`
For an already supported platform but unsupported driver you only need to implement the first two interfaces, since the SQL Generation and Schema Management is already supported by the respective platform and schema instances. You can also make use of several Abstract Unittests in the `\Doctrine\Tests\DBAL` package to check if your platform behaves like all the others which is necessary for SchemaTool support, namely: