Commit d7a52291 authored by Jonathan H. Wage's avatar Jonathan H. Wage

Updating DBAL documentation, adding more text/thoroughness as well as...

Updating DBAL documentation, adding more text/thoroughness as well as splitting into multiple chapters since it will be separate documentation for DBAL.
parent e3ddd688
+ Database Abstraction Layer + Introduction
\ No newline at end of file + Architecture
+ Configuration
+ Events
+ Data Retrieval and Manipulation
+ Transactions
+ Platforms
+ Schema Manager
+ Schema Representation
+ Supporting Other Databases
\ No newline at end of file
The DBAL is separated into several different packages that perfectly separate responsibilities of the different RDBMS layers.
++ Drivers
The drivers abstract a PHP specific database API by enforcing two interfaces:
* `\Doctrine\DBAL\Driver\Driver`
* `\Doctrine\DBAL\Driver\Statement`
The above two interfaces require exactly the same methods as PDO.
++ Platforms
The 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
The logging holds the interface and some implementations for debugging of Doctrine SQL query execution during a request.
++ Schema
The 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
The types offer an abstraction layer for the converting and generation of types between Databases and PHP. Doctrine comes bundled with some common types but offers the ability for developers to define custom types or extend existing ones easily.
\ No newline at end of file
You can create a new Doctrine Connection by using the `Doctrine\DBAL\DriverManager` class and the `getConnection()` static function:
[php]
$config = new \Doctrine\DBAL\Configuration();
//..
$connectionParams = array(
'dbname' => 'mydb',
'user' => 'user',
'password' => 'secret',
'host' => 'locahlost',
'driver' => 'pdo_mysql',
);
$conn = DriverManager::getConnection($connectionParams);
The `DriverManager` returns an instance of `Doctrine\DBAL\Connection` which is a wrapper around any configured database driver, for example the PDO Mysql driver in the previous example.
++ Connection Options
Common Configuration Options across all database drivers:
* **driver** - Allows to specify the default drivers shipped with Doctrine 2, 'pdo_mysql', 'pdo_sqlite', 'pdo_pgsql, and 'oci'.
* **driverClass** - If no 'driver' is specified this allows usage of a userland implementation of Doctrine\DBAL\Driver.
* **pdo** - If PDO is already instantiated for Mysql, SqLite or PgSQL this key can be used to pass this instance into Doctrine.
* **wrapperClass** - By default Doctrine\DBAL\Connection is wrapped around each driver, however this option allows to specify a userland sub-class.
Driver Configuration Options can be different for each Database Driver, here are some of the driver specific ones:
* **platform** - An instance of `Doctrine\DBAL\Platforms\AbstractPlatform`. This is only required for userland implementations, each driver shipped with Doctrine 2 has a default platform.
* **user** - Username required to connect to the database.
* **password** - Password required to connect to the database.
* **driverOptions** - Array of options passed to the driver instance on calling to Driver::connect.
\ No newline at end of file
The DBAL contains several methods for executing queries against your configured database for data retrieval and manipulation. Below we'll introduce these methods and provide some examples for each of them.
++ prepare($sql)
Prepare a given sql statement and return the `\Doctrine\DBAL\Driver\Statement` instance:
[php]
$statement = $conn->prepare('SELECT * FROM user');
$statement->execute();
$users = $statement->fetchAll();
/*
array(
0 => array(
'username' => 'jwage',
'password' => 'changeme
)
)
*/
++ executeUpdate($sql, array $params)
Executes a prepared statement with the given sql and parameters and returns the affected rows count:
[php]
$count = $conn->executeUpdate('UPDATE user SET username = ? WHERE id = ?', array('jwage', 1));
echo $count; // 1
++ execute($sql, array $params)
Creates a prepared statement for the given sql and passes the parameters to the execute method, then returning the statement:
[php]
$statement = $conn->execute('SELECT * FROM user WHERE username = ?', array('jwage'));
$user = $statement->fetch();
/*
array(
0 => 'jwage',
1 => 'changeme
)
*/
++ fetchAll($sql, array $params)
Execute the query and fetch all results into an array:
[php]
$users = $conn->fetchAll('SELECT * FROM user');
/*
array(
0 => array(
'username' => 'jwage',
'password' => 'changeme
)
)
*/
++ fetchArray($sql, array $params)
Numeric index retrieval of first result row of the given query:
[php]
$user = $conn->fetchArray('SELECT * FROM user WHERE username = ?', array('jwage'));
/*
array(
0 => 'jwage',
1 => 'changeme
)
*/
++ fetchColumn($sql, array $params, $colnum)
Retrieve only the given column of the first result row.
[php]
$username = $conn->fetchColumn('SELECT username FROM user WHERE id = ?', array(1), 'username');
echo $username; // jwage
++ fetchRow($sql, array $params)
Retrieve assoc row of the first result row.
[php]
$user = $conn->fetchRow('SELECT * FROM user WHERE username = ?', array('jwage'));
/*
array(
'username' => 'jwage',
'password' => 'changeme
)
*/
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.
[php]
$conn->delete('user', array('id' => 1));
// DELETE FROM user WHERE id = ? (1)
++ insert($tableName, array $data)
Insert a row into the given table name using the key value pairs of data.
[php]
$conn->insert('user', array('username' => 'jwage'));
// INSERT INTO user (username) VALUES (?) (jwage)
++ update($tableName, array $data, array $identifier)
Update all rows for the matching key value identifiers with the given data.
[php]
$conn->update('user', array('username' => 'jwage'), array('id' => 1));
// UPDATE user (username) VALUES (?) WHERE id = ? (jwage, 1)
By default the Doctrine DBAL does no escaping. Escaping is a very tricky business to do automatically, 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:
[php]
$quoted = $conn->quote('value');
++ quoteIdentifier($identifier)
Quote an identifier according to the platform details.
[php]
$quoted = $conn->quoteIdentifier('id');
\ No newline at end of file
This diff is collapsed.
Both `Doctrine\DBAL\DriverManager` and `Doctrine\DBAL\Connection` accept an instance of `Doctrine\Common\EventManager`. The EventManager has a couple of events inside the DBAL layer that are triggered for the user to listen to.
++ PostConnect Event
`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:
[php]
$evm = new EventManager(),
$evm->addEventSubscriber(new MysqlSessionInit('UTF-8'));
$conn = DriverManager::getConnection($connectionParams, null, $evm);
\ No newline at end of file
The Doctrine 2 database layer can be used independently of the object-relational mapping. It offers a lightweight abstraction layer around a PDO like API and allows optional access to lots of convenience functionality as the ability to generate platform independent SQL and DDL statements.
In order to use the DBAL all you need is the `Doctrine\Common` and `Doctrine\DBAL` namespaces. Once you have the Common and DBAL namespaces you must setup a class loader to be able to autoload the classes:
[php]
use Doctrine\Common\ClassLoader;
require '/path/to/doctrine';
$classLoader = new ClassLoader('Doctrine', '/path/to/doctrine');
$classLoader->register();
Now you are able load classes that are in the `/path/to/doctrine` directory like `/path/to/doctrine/Doctrine/DBAL/DriverManager.php` which we will use later in this documentation to configure our first Doctrine DBAL connection.
\ No newline at end of file
Platforms abstract query generation and specifics of the RDBMS feature sets. In most cases you don't need to interact with this package a lot, 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.
[php]
$platform = $conn->getDatabasePlatform();
When creating a connection you can specify a `platform` key to pass the platform
you want the connection to use:
[php]
$myPlatform = new MyPlatform();
$options = array(
'driver' => 'pdo_sqlite',
'path' => 'database.sqlite',
'platform' => $myPlatform
);
$conn = DriverManager::getConnection($options);
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.
\ No newline at end of file
A Schema Manager instance helps you with the abstraction of the generation of SQL assets such as Tables, Sequences, Foreign Keys and Indexes.
To retrieve the `SchemaManager` for your connection you can use the `getSchemaManager()` method:
[php]
$sm = $conn->getSchemaManager();
Now with the `SchemaManager` instance in `$em` you can use the available methods to learn about your database schema:
++ listDatabases()
Retrieve an array of databases on the configured connection:
[php]
$databases = $sm->listDatabases();
++ listSequences($database = null)
Retrieve an array of `Doctrine\DBAL\Schema\Sequence` instances that exist for a database:
[php]
$sequences = $sm->listSequences();
Or if you want to manually specify a database name:
[php]
$sequences = $sm->listSequences('dbname');
Now you can loop over the array inspecting each sequence object:
[php]
foreach ($sequences as $sequence) {
echo $sequence->getName() . "\n";
}
++ listTableColumns($tableName)
Retrieve an array of `Doctrine\DBAL\Schema\Column` instances that exist for the given table:
[php]
$columns = $sm->listTableColumns('user');
Now you can loop over the array inspecting each column object:
[php]
foreach ($columns as $column) {
echo $column->getName() . ': ' . $column->getType() . "\n";
}
++ listTableDetails($tableName)
Retrieve a single `Doctrine\DBAL\Schema\Table` instance that encapsulates all the details of the given table:
[php]
$table = $sm->listTableDetails('user');
Now you can call methods on the table to manipulate the in memory schema for that table. For example we can add a new column:
[php]
$table->addColumn('email_address', 'string');
++ listTableForeignKeys($tableName)
Retrieve an array of `Doctrine\DBAL\Schema\ForeignKeyConstraint` instances that exist for the given table:
[php]
$foreignKeys = $sm->listTableForeignKeys('user');
Now you can loop over the array inspecting each foreign key object:
[php]
foreach ($foreignKeys as $foreignKey) {
echo $foreignKey->getName() . ': ' . $foreignKey->getLocalTableName() ."\n";
}
++ listTableIndexes($tableName)
Retrieve an array of `Doctrine\DBAL\Schema\Index` instances that exist for the given table:
[php]
$indexes = $sm->listTableIndexes('user');
Now you can loop over the array inspecting each index object:
[php]
foreach ($indexes as $index) {
echo $index->getName() . ': ' . ($index->isUnique() ? 'unique' : 'not unique') . "\n";
}
++ listTables()
Retrieve an array of `Doctrine\DBAL\Schema\Table` instances that exist in the connections database:
[php]
$tables = $sm->listTables();
Each `Doctrine\DBAl\Schema\Table` instance is populated with information provided by all the above methods. So it encapsulates an array of `Doctrine\DBAL\Schema\Column` instances that can be retrieved with the `getColumns()` method:
[php]
foreach ($tables as $table) {
echo $table->getName() . " columns:\n\n";
foreach ($table->getColumns() as $column) {
echo ' - ' . $column->getName() . "\n";
}
}
++ listViews()
Retrieve an array of `Doctrine\DBAL\Schema\View` instances that exist in the connections database:
[php]
$views = $sm->listViews();
Now you can loop over the array inspecting each view object:
[php]
foreach ($views as $view) {
echo $view->getName() . ': ' . $view->getSql() . "\n";
}
++ createSchema()
For a complete representation of the current database you can use the `createSchema()` method which returns an instance of `Doctrine\DBAL\Schema\Schema`, which you can use in conjunction with the SchemaTool or Schema Comparator.
[php]
$fromSchema = $sm->createSchema();
Now we can clone the `$fromSchema` to `$toSchema` and drop a table:
[php]
$toSchema = clone $fromSchema;
$toSchema->dropTable('user');
Now we can compare the two schema instances in order to calculate the differences between them and return the sql required to make the changes on the database:
[php]
$sql = $fromSchema->getMigrateToSql($toSchema);
The `$sql` array should give you a sql query to drop the user table:
[php]
print_r($sql);
/*
array(
0 => 'DROP TABLE user'
)
*/
\ No newline at end of file
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:
[php]
$schema = new \Doctrine\DBAL\Schema\Schema();
$myTable = $schema->createTable("my_table");
$myTable->addColumn("id", "integer", array("unsigned" => true));
$myTable->addColumn("username", "string", array("length" => 32));
$myTable->setPrimaryKey(array("id"));
$myTable->addUniqueIndex(array("username"));
$schema->createSequence("my_table_seq");
$myForeign = $schema->createTable("my_foreign");
$myForeign->addColumn("id", "integer");
$myForeign->addColumn("user_id", "integer");
$myForeign->addForeignKeyConstraint($myTable, array("user_id"), array("id"), array("onUpdate" => "CASCADE"));
$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();
$schemaDiff = $comparator->compare($fromSchema, $toSchema);
$queries = $schemaDiff->toSql($myPlatform); // queries to get from one to another schema.
$saveQueries = $schemaDiff->toSaveSql($myPlatform);
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.
\ No newline at end of file
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:
* `\Doctrine\Tests\DBAL\Platforms\AbstractPlatformTestCase`
* `\Doctrine\Tests\DBAL\Functional\Schema\AbstractSchemaManagerTestCase`
We would be very happy if any support for new databases would be contributed back to Doctrine to make it an even better product.
\ No newline at end of file
Doctrine handles transactions with a PDO like API, having methods for `beginTransaction()`, `commit()` and `rollBack()`. For consistency across different drivers Doctrine also handles the nesting of transactions internally. You can call `beginTransaction()` more than once, and only a matching amount of calls to `commit()` triggers the commit to the database.
The Doctrine connection also has a method to set the transaction isolation level of the connection as supported by the underlying database. Here are the available isolation level constants that can be used:
[php]
class Connection
{
/**
* Constant for transaction isolation level READ UNCOMMITTED.
*/
const TRANSACTION_READ_UNCOMMITTED = 1;
/**
* Constant for transaction isolation level READ COMMITTED.
*/
const TRANSACTION_READ_COMMITTED = 2;
/**
* Constant for transaction isolation level REPEATABLE READ.
*/
const TRANSACTION_REPEATABLE_READ = 3;
/**
* Constant for transaction isolation level SERIALIZABLE.
*/
const TRANSACTION_SERIALIZABLE = 4;
}
Then you can use the above constants when configuring your connection instance:
[php]
$conn->setTransactionIsolationLevel(Connection::TRANSACTION_SERIALIZABLE);
try{
$conn->beginTransaction();
// do stuff
$conn->commit();
} catch(\Exception $e) {
$conn->rollback();
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment