Commit 15b725ce authored by Fabien Potencier's avatar Fabien Potencier

refactored the Doctrine extension

parent 79b0e1b4
...@@ -37,3 +37,9 @@ ...@@ -37,3 +37,9 @@
[submodule "vendor/Symfony/Component/Process"] [submodule "vendor/Symfony/Component/Process"]
path = vendor/Symfony/Component/Process path = vendor/Symfony/Component/Process
url = https://github.com/symfony/Process url = https://github.com/symfony/Process
[submodule "vendor/doctrine-common"]
path = vendor/doctrine-common
url = https://github.com/doctrine/common
[submodule "vendor/doctrine-dbal"]
path = vendor/doctrine-dbal
url = https://github.com/doctrine/dbal
...@@ -11,7 +11,7 @@ The *DoctrineExtension* provides integration with the `Doctrine DBAL ...@@ -11,7 +11,7 @@ The *DoctrineExtension* provides integration with the `Doctrine DBAL
Parameters Parameters
---------- ----------
* **dbal.options**: Array of Doctrine DBAL options. * **db.options**: Array of Doctrine DBAL options.
These options are available: These options are available:
...@@ -32,29 +32,25 @@ Parameters ...@@ -32,29 +32,25 @@ Parameters
* **path**: Only relevant for ``pdo_sqlite``, specifies the path to * **path**: Only relevant for ``pdo_sqlite``, specifies the path to
the SQLite database. the SQLite database.
* **default**: (optional) When using multiple databases, allows you
to set the default connection to one other than the first
connection
These and additional options are described in detail in the `Doctrine DBAL These and additional options are described in detail in the `Doctrine DBAL
configuration documentation <http://www.doctrine-project.org/docs/dbal/2.0/en/reference/configuration.html>`_. configuration documentation <http://www.doctrine-project.org/docs/dbal/2.0/en/reference/configuration.html>`_.
* **dbal.dbal.class_path** (optional): Path to where the * **db.dbal.class_path** (optional): Path to where the
Doctrine DBAL is located. Doctrine DBAL is located.
* **dbal.common.class_path** (optional): Path to where * **db.common.class_path** (optional): Path to where
Doctrine Common is located. Doctrine Common is located.
Services Services
-------- --------
* **dbal**: The database connection, instance of * **db**: The database connection, instance of
``Doctrine\DBAL\Connection``. ``Doctrine\DBAL\Connection``.
* **dbal.config**: Configuration object for Doctrine. Defaults to * **db.config**: Configuration object for Doctrine. Defaults to
an empty ``Doctrine\DBAL\Configuration``. an empty ``Doctrine\DBAL\Configuration``.
* **dbal.event_manager**: Event Manager for Doctrine. * **db.event_manager**: Event Manager for Doctrine.
Registering Registering
----------- -----------
...@@ -63,12 +59,12 @@ Make sure you place a copy of *Doctrine DBAL* in ``vendor/doctrine-dbal`` ...@@ -63,12 +59,12 @@ Make sure you place a copy of *Doctrine DBAL* in ``vendor/doctrine-dbal``
and *Doctrine Common* in ``vendor/doctrine-common``:: and *Doctrine Common* in ``vendor/doctrine-common``::
$app->register(new Silex\Extension\DoctrineExtension(), array( $app->register(new Silex\Extension\DoctrineExtension(), array(
'dbal.options' => array( 'db.options' => array(
'driver' => 'pdo_sqlite', 'driver' => 'pdo_sqlite',
'path' => __DIR__.'/app.db', 'path' => __DIR__.'/app.db',
), ),
'dbal.dbal.class_path' => __DIR__.'/vendor/doctrine-dbal/lib', 'db.dbal.class_path' => __DIR__.'/vendor/doctrine-dbal/lib',
'dbal.common.class_path' => __DIR__.'/vendor/doctrine-common/lib', 'db.common.class_path' => __DIR__.'/vendor/doctrine-common/lib',
)); ));
Usage Usage
...@@ -79,30 +75,22 @@ example:: ...@@ -79,30 +75,22 @@ example::
$app->get('/blog/show/{id}', function ($id) use ($app) { $app->get('/blog/show/{id}', function ($id) use ($app) {
$sql = "SELECT * FROM posts WHERE id = ?"; $sql = "SELECT * FROM posts WHERE id = ?";
$post = $app['dbal']->fetchAssoc($sql, array((int) $id)); $post = $app['db']->fetchAssoc($sql, array((int) $id));
return "<h1>{$post['title']}</h1>". return "<h1>{$post['title']}</h1>".
"<p>{$post['body']}</p>"; "<p>{$post['body']}</p>";
}); });
Using multiple databases Using multiple databases
------------------------ ------------------------
The Doctrine extension can allow access to multiple databases. In order The Doctrine extension can allow access to multiple databases. In order to
configure these data sources you must remove the **db.options** from configure the data sources, replace the **db.options** with **dbs.options**.
your extension registration, and replace it with an array named **dbs**. **dbs.options** is an array of configurations where keys are connection names
and values are options::
Each key of the dbs array should contain a configuration of options.
Registering multiple database connections::
$app->register(new Silex\Extension\DoctrineExtension(), array( $app->register(new Silex\Extension\DoctrineExtension(), array(
'dbal.dbs' => array ( 'dbs.options' => array (
'sqlite' => array(
'driver' => 'pdo_sqlite',
'path' => __DIR__.'/app.db',
),
'mysql_read' => array( 'mysql_read' => array(
'driver' => 'pdo_mysql', 'driver' => 'pdo_mysql',
'host' => 'mysql_read.someplace.tld' 'host' => 'mysql_read.someplace.tld'
...@@ -118,33 +106,29 @@ Registering multiple database connections:: ...@@ -118,33 +106,29 @@ Registering multiple database connections::
'password' => 'my_password', 'password' => 'my_password',
), ),
), ),
'dbal.dbal.class_path' => __DIR__.'/vendor/doctrine-dbal/lib', 'db.dbal.class_path' => __DIR__.'/vendor/doctrine-dbal/lib',
'dbal.common.class_path' => __DIR__.'/vendor/doctrine-common/lib', 'db.common.class_path' => __DIR__.'/vendor/doctrine-common/lib',
)); ));
By default, the first connection registered is the default. This can simply be accessed as you would if there was only one connection. Given the above DB registration these two lines are equal: The first registered connection is the default and can simply be accessed as
you would if there was only one connection. Given the above configuration,
these two lines are equivalent::
$app['dbal']->fetchAssoc('SELECT * FROM table'); $app['db']->fetchAssoc('SELECT * FROM table');
$app['dbal.connection.sqlite']->fetchAssoc('SELECT * FROM table');
The default connection can be selected by setting the **default** option toggle. $app['dbs']['mysql_read']->fetchAssoc('SELECT * FROM table');
Using multiple connections:: Using multiple connections::
$app->get('/joined/{searchOne}/{searchTwo}, function ($searchOne, $searchTwo) use ($app)) { $app->get('/blog/show/{id}', function ($id) use ($app) {
$sqliteQuery = "SELECT * FROM table_one WHERE id = ?"; $sql = "SELECT * FROM posts WHERE id = ?";
$one = $app['dbal.connection.sqlite']->fetchAssoc($sqliteQuery, array((int) $searchOne)); $post = $app['dbs']['mysql_read']->fetchAssoc($mysqlQuery, array((int) $id));
$mysqlQuery = "SELECT * FROM table_two WHERE id = ?"; $mysqlUpdate = "UPDATE posts SET value = ? WHERE id = ?";
$two = $app['dbal.connection.mysql_read']->fetchAssoc($mysqlQuery, array((int) $searchTwo)); $app['dbs']['mysql_write']->execute($mysqlUpdate, array('newValue', (int) $id));
$mysqlUpdate = "UPDATE table_two SET value = ? WHERE id = ?"; return "<h1>{$post['title']}</h1>".
$app['dbal.connection.mysql_write']->execute($mysqlUpdate, array((int) $searchTwo, 'newValue')); "<p>{$post['body']}</p>";
return "<h1>{$one['column_from_sqlite']}</h1>".
"<p>{$two['column_from_mysql']}</p>";
}); });
For more information, consult the `Doctrine DBAL documentation For more information, consult the `Doctrine DBAL documentation
......
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex\Extension;
use Silex\Application;
use Silex\ExtensionInterface;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Configuration;
use Doctrine\Common\EventManager;
class DoctrineDbalExtension implements ExtensionInterface
{
public function register(Application $app)
{
if (isset($app['dbal.options'])) {
$app['dbal.dbs'] = array('default' => $app['dbal.options']);
}
if (isset($app['dbal.dbs']) && is_array($app['dbal.dbs'])) {
$firstConnection = true;
foreach ($app['dbal.dbs'] as $connection => $options) {
$app['dbal.connection.'.$connection.'.options'] = $options;
$app['dbal.connection.'.$connection] = $app->share(function () use ($app, $options, $connection) {
return DriverManager::getConnection($options, $app['dbal.connection.'.$connection.'.config'], $app['dbal.connection.'.$connection.'.event_manager']);
});
$app['dbal.connection.'.$connection.'.config'] = $app->share(function () {
return new Configuration();
});
$app['dbal.connection.'.$connection.'.event_manager'] = $app->share(function () {
return new EventManager();
});
if ($firstConnection || !empty($options['default'])) {
$app['dbal'] = function() use ($app, $connection) {
return $app['dbal.connection.'.$connection];
};
}
$firstConnection = false;
}
unset($app['dbal.dbs']);
}
if (isset($app['dbal.dbal.class_path'])) {
$app['autoloader']->registerNamespace('Doctrine\\DBAL', $app['dbal.dbal.class_path']);
}
if (isset($app['dbal.common.class_path'])) {
$app['autoloader']->registerNamespace('Doctrine\\Common', $app['dbal.common.class_path']);
}
}
}
\ No newline at end of file
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex\Extension;
use Silex\Application;
use Silex\ExtensionInterface;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Configuration;
use Doctrine\Common\EventManager;
class DoctrineExtension implements ExtensionInterface
{
public function register(Application $app)
{
$app['db.default_options'] = array(
'driver' => 'pdo_mysql',
'dbname' => null,
'host' => 'localhost',
'user' => 'root',
'password' => null,
);
$app['dbs.options.initializer'] = $app->protect(function () use ($app) {
static $initialized = false;
if ($initialized) {
return;
}
$initialized = true;
if (!isset($app['dbs.options'])) {
$app['dbs.options'] = array('default' => isset($app['db.options']) ? $app['db.options'] : array());
}
$tmp = $app['dbs.options'];
foreach ($tmp as $name => &$options) {
$options = array_replace($app['db.default_options'], $options);
if (!isset($app['dbs.default'])) {
$app['dbs.default'] = $name;
}
}
});
$app['dbs'] = $app->share(function () use ($app) {
$app['dbs.options.initializer']();
$dbs = new \Pimple();
foreach ($app['dbs.options'] as $name => $options) {
if ($app['dbs.default'] === $name) {
// we use shortcuts here in case the default has been overriden
$config = $app['db.config'];
$manager = $app['db.event_manager'];
} else {
$config = $app['dbs.config'][$name];
$manager = $app['dbs.event_manager'][$name];
}
$dbs[$name] = DriverManager::getConnection($options, $config, $manager);
}
return $dbs;
});
$app['dbs.config'] = $app->share(function () use ($app) {
$app['dbs.options.initializer']();
$configs = new \Pimple();
foreach ($app['dbs.options'] as $name => $options) {
$configs[$name] = new Configuration();
}
return $configs;
});
$app['dbs.event_manager'] = $app->share(function () use ($app) {
$app['dbs.options.initializer']();
$managers = new \Pimple();
foreach ($app['dbs.options'] as $name => $options) {
$managers[$name] = new EventManager();
}
return $managers;
});
// shortcuts for the "first" DB
$app['db'] = $app->share(function() use ($app) {
$dbs = $app['dbs'];
return $dbs[$app['dbs.default']];
});
$app['db.config'] = $app->share(function() use ($app) {
$dbs = $app['dbs.config'];
return $dbs[$app['dbs.default']];
});
$app['db.event_manager'] = $app->share(function() use ($app) {
$dbs = $app['dbs.event_manager'];
return $dbs[$app['dbs.default']];
});
if (isset($app['db.dbal.class_path'])) {
$app['autoloader']->registerNamespace('Doctrine\\DBAL', $app['db.dbal.class_path']);
}
if (isset($app['db.common.class_path'])) {
$app['autoloader']->registerNamespace('Doctrine\\Common', $app['db.common.class_path']);
}
}
}
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Silex\Tests\Extension;
use Silex\Application;
use Silex\Extension\DoctrineExtension;
/**
* DoctrineExtension test cases.
*
* Fabien Potencier <fabien@symfony.com>
*/
class DoctrineExtensionTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
if (!is_dir(__DIR__.'/../../../../vendor/doctrine-common/lib') || !is_dir(__DIR__.'/../../../../vendor/doctrine-dbal/lib')) {
$this->markTestSkipped('Doctrine Common/DBAL submodules were not installed.');
}
}
public function testSingleConnection()
{
$app = new Application();
$app->register(new DoctrineExtension(), array(
'db.common.class_path' => __DIR__.'/../../../../vendor/doctrine-common/lib',
'db.dbal.class_path' => __DIR__.'/../../../../vendor/doctrine-dbal/lib',
'db.options' => array('driver' => 'pdo_sqlite', 'memory' => true),
));
$db = $app['db'];
$params = $db->getParams();
$this->assertTrue(array_key_exists('memory', $params));
$this->assertTrue($params['memory']);
$this->assertInstanceof('Doctrine\DBAL\Driver\PDOSqlite\Driver', $db->getDriver());
$this->assertEquals(22, $app['db']->fetchColumn('SELECT 22'));
$this->assertSame($app['dbs']['default'], $db);
}
public function testMultipleConnections()
{
$app = new Application();
$app->register(new DoctrineExtension(), array(
'db.common.class_path' => __DIR__.'/../../../../vendor/doctrine-common/lib',
'db.dbal.class_path' => __DIR__.'/../../../../vendor/doctrine-dbal/lib',
'dbs.options' => array(
'sqlite1' => array('driver' => 'pdo_sqlite', 'memory' => true),
'sqlite2' => array('driver' => 'pdo_sqlite', 'path' => sys_get_temp_dir().'/silex'),
),
));
$db = $app['db'];
$params = $db->getParams();
$this->assertTrue(array_key_exists('memory', $params));
$this->assertTrue($params['memory']);
$this->assertInstanceof('Doctrine\DBAL\Driver\PDOSqlite\Driver', $db->getDriver());
$this->assertEquals(22, $app['db']->fetchColumn('SELECT 22'));
$this->assertSame($app['dbs']['sqlite1'], $db);
$db2 = $app['dbs']['sqlite2'];
$params = $db2->getParams();
$this->assertTrue(array_key_exists('path', $params));
$this->assertEquals(sys_get_temp_dir().'/silex', $params['path']);
}
}
Subproject commit 40f1bf16e84ddc5291a6a63aa00b9879c40e3500
Subproject commit 938502f9532330f961831da7fe03b4d5c924fb51
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