[DoctrineMongoDBBundle] Initial use of the new Configuration class for DoctrineMongoDBExtension.

This commit is contained in:
Ryan Weaver 2011-02-19 10:08:35 -06:00
parent 94da3127b9
commit a13500459f
4 changed files with 435 additions and 35 deletions

View File

@ -0,0 +1,202 @@
<?php
namespace Symfony\Bundle\DoctrineMongoDBBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
/**
* FrameworkExtension configuration structure.
*
* @author Ryan Weaver <ryan@thatsquality.com>
*/
class Configuration
{
/**
* Generates the configuration tree.
*
* @param boolean $kernelDebug The kernel.debug DIC parameter
* @return \Symfony\Component\DependencyInjection\Configuration\NodeInterface
*/
public function getConfigTree()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('doctrinemongodb', 'array');
$this->addSingleDocumentManagerSection($rootNode);
$this->addDocumentManagersSection($rootNode);
$this->addSingleConnectionSection($rootNode);
$this->addConnectionsSection($rootNode);
$rootNode
->scalarNode('proxy_namespace')->defaultValue(null)->end()
->scalarNode('auto_generate_proxy_classes')->defaultValue(null)->end()
->scalarNode('hydrator_namespace')->defaultValue(null)->end()
->scalarNode('auto_generate_hydrator_classes')->defaultValue(null)->end()
;
return $treeBuilder->buildTree();
}
/**
* Builds the nodes responsible for the config that supports the single
* document manager.
*/
private function addSingleDocumentManagerSection(NodeBuilder $rootNode)
{
$rootNode
->scalarNode('default_document_manager')->defaultValue('default')->end()
->scalarNode('default_database')->defaultValue('default')->end()
->builder($this->getMetadataCacheDriverNode())
->fixXmlConfig('mapping')
->builder($this->getMappingsNode())
;
}
/**
* Configures the "document_managers" section
*/
private function addDocumentManagersSection(NodeBuilder $rootNode)
{
$rootNode
->fixXmlConfig('document_manager')
->arrayNode('document_managers')
->useAttributeAsKey('id')
->prototype('array')
->performNoDeepMerging()
->treatNullLike(array())
->builder($this->getMetadataCacheDriverNode())
->scalarNode('default_database')->end()
->scalarNode('connection')->end()
->scalarNode('database')->end()
->fixXmlConfig('mapping')
->builder($this->getMappingsNode())
->end()
->end()
;
}
/**
* Configures the single-connection section:
* * default_connection
* * server
* * options
*/
private function addSingleConnectionSection(NodeBuilder $rootNode)
{
$rootNode
->scalarNode('default_connection')->defaultValue('default')->end()
->builder($this->addConnectionServerNode())
->builder($this->addConnectionOptionsNode())
;
}
/**
* Adds the configuration for the "connections" key
*/
private function addConnectionsSection(NodeBuilder $rootNode)
{
$rootNode
->fixXmlConfig('connection')
->arrayNode('connections')
->useAttributeAsKey('id')
->prototype('array')
->performNoDeepMerging()
->builder($this->addConnectionServerNode())
->builder($this->addConnectionOptionsNode())
->end()
->end()
;
}
/**
* Returns the array node used for "mappings".
*
* This is used in two different parts of the tree.
*
* @param NodeBuilder $rootNode The parent node
* @return NodeBuilder
*/
protected function getMappingsNode()
{
$node = new Nodebuilder('mappings', 'array');
$node
->useAttributeAsKey('name')
->prototype('array')
// I believe that "null" should *not* set the type
// it's guessed in AbstractDoctrineExtension::detectMetadataDriver
->treatNullLike(array())
->beforeNormalization()
// if it's not an array, then the scalar is the type key
->ifTrue(function($v) { return !is_array($v); })
->then(function($v){ return array('type' => $v); })
->end()
->scalarNode('type')->end()
->scalarNode('dir')->end()
->scalarNode('prefix')->end()
->scalarNode('alias')->end()
->performNoDeepMerging()
->end()
;
return $node;
}
/**
* Adds the NodeBuilder for the "server" key of a connection.
*/
private function addConnectionServerNode()
{
$node = new NodeBuilder('server', 'scalar');
$node
->defaultValue(null)
->end();
return $node;
}
/**
* Adds the NodeBuilder for the "options" key of a connection.
*/
private function addConnectionOptionsNode()
{
$node = new NodeBuilder('options', 'array');
$node
->performNoDeepMerging()
->addDefaultsIfNotSet() // adds an empty array of omitted
// options go into the Mongo constructor
// http://www.php.net/manual/en/mongo.construct.php
->booleanNode('connect')->end()
->scalarNode('persist')->end()
->scalarNode('timeout')->end()
->booleanNode('replicaSet')->end()
->scalarNode('username')->end()
->scalarNode('password')->end()
->end();
return $node;
}
private function getMetadataCacheDriverNode()
{
$node = new NodeBuilder('metadata_cache_driver', 'array');
$node
->beforeNormalization()
// if scalar
->ifTrue(function($v) { return !is_array($v); })
->then(function($v) { return array('type' => $v); })
->end()
->scalarNode('type')->end()
->scalarNode('class')->end()
->scalarNode('host')->end()
->scalarNode('port')->end()
->scalarNode('instance_class')->end()
->end();
return $node;
}
}

View File

@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\FileLocator;
use Symfony\Bundle\DoctrineAbstractBundle\DependencyInjection\AbstractDoctrineExtension;
use Symfony\Component\Config\Definition\Processor;
/**
* Doctrine MongoDB ODM extension.
@ -60,23 +61,13 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
*/
public function load(array $configs, ContainerBuilder $container)
{
foreach ($configs as $config) {
$this->doMongodbLoad($config, $container);
}
}
// Load DoctrineMongoDBBundle/Resources/config/mongodb.xml
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('mongodb.xml');
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->process($configuration->getConfigTree(), $configs);
/**
* Loads the MongoDB ODM configuration.
*
* Usage example:
*
* <doctrine:mongodb server="mongodb://localhost:27017" />
*
* @param array $config An array of configuration settings
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function doMongodbLoad($config, ContainerBuilder $container)
{
$this->loadDefaults($config, $container);
$this->loadConnections($config, $container);
$this->loadDocumentManagers($config, $container);
@ -91,17 +82,10 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
*/
protected function loadDefaults(array $config, ContainerBuilder $container)
{
if (!$container->hasDefinition('doctrine.odm.mongodb.metadata.annotation')) {
// Load DoctrineMongoDBBundle/Resources/config/mongodb.xml
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('mongodb.xml');
}
// Allow these application configuration options to override the defaults
$options = array(
'default_document_manager',
'default_connection',
'metadata_cache_driver',
'proxy_namespace',
'auto_generate_proxy_classes',
'hydrator_namespace',
@ -112,11 +96,10 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
if (isset($config[$key])) {
$container->setParameter('doctrine.odm.mongodb.'.$key, $config[$key]);
}
$nKey = str_replace('_', '-', $key);
if (isset($config[$nKey])) {
$container->setParameter('doctrine.odm.mongodb.'.$key, $config[$nKey]);
}
if (isset($config['metadata_cache_driver'])) {
$container->setParameter('doctrine.odm.mongodb.metadata_cache_driver', $config['metadata_cache_driver']['type']);
}
}
@ -222,11 +205,7 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
$documentManagers = array();
if (isset($config['document-managers'])) {
$config['document_managers'] = $config['document-managers'];
}
if (isset($config['document_managers'])) {
if (count($config['document_managers'])) {
$configDocumentManagers = $config['document_managers'];
if (isset($config['document_managers']['document-manager'])) {
@ -255,8 +234,8 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
protected function loadDocumentManagerMetadataCacheDriver(array $documentManager, ContainerBuilder $container)
{
$metadataCacheDriver = $container->getParameter('doctrine.odm.mongodb.metadata_cache_driver');
$dmMetadataCacheDriver = isset($documentManager['metadata-cache-driver']) ? $documentManager['metadata-cache-driver'] : (isset($documentManager['metadata_cache_driver']) ? $documentManager['metadata_cache_driver'] : $metadataCacheDriver);
$type = is_array($dmMetadataCacheDriver) && isset($dmMetadataCacheDriver['type']) ? $dmMetadataCacheDriver['type'] : $dmMetadataCacheDriver;
$dmMetadataCacheDriver = isset($documentManager['metadata_cache_driver']) ? $documentManager['metadata_cache_driver'] : $metadataCacheDriver;
$type = is_array($dmMetadataCacheDriver) ? $dmMetadataCacheDriver['type'] : $dmMetadataCacheDriver;
if ('memcache' === $type) {
$memcacheClass = isset($dmMetadataCacheDriver['class']) ? $dmMetadataCacheDriver['class'] : sprintf('%%doctrine.odm.mongodb.cache.%s_class%%', $type);
@ -271,6 +250,7 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
} else {
$cacheDef = new Definition(sprintf('%%doctrine.odm.mongodb.cache.%s_class%%', $type));
}
$container->setDefinition(sprintf('doctrine.odm.mongodb.%s_metadata_cache', $documentManager['name']), $cacheDef);
}
@ -305,7 +285,7 @@ class DoctrineMongoDBExtension extends AbstractDoctrineExtension
$defaultConnection = $container->getParameter('doctrine.odm.mongodb.default_connection');
$connections = array();
if (isset($config['connections'])) {
if (count($config['connections'])) {
$configConnections = $config['connections'];
if (isset($config['connections']['connection']) && isset($config['connections']['connection'][0])) {
// Multiple connections

View File

@ -0,0 +1,178 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\DoctrineMongoDBBundle\Tests\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Bundle\DoctrineMongoDBBundle\DependencyInjection\Configuration;
use Symfony\Component\Config\Definition\Processor;
class ConfigurationTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider optionProvider
* @param array $configs The source array of configuration arrays
* @param array $correctValues A key-value pair of end values to check
*/
public function testMergeOptions(array $configs, array $correctValues)
{
$processor = new Processor();
$configuration = new Configuration();
$options = $processor->process($configuration->getConfigTree(), $configs);
foreach ($correctValues as $key => $correctVal)
{
$this->assertEquals($correctVal, $options[$key]);
}
}
public function optionProvider()
{
$cases = array();
// single config, testing normal option setting
$cases[] = array(
array(
array('default_document_manager' => 'foo'),
),
array('default_document_manager' => 'foo')
);
// single config, testing normal option setting with dashes
$cases[] = array(
array(
array('default-document-manager' => 'bar'),
),
array('default_document_manager' => 'bar')
);
// testing the normal override merging - the later config array wins
$cases[] = array(
array(
array('default_document_manager' => 'foo'),
array('default_document_manager' => 'baz'),
),
array('default_document_manager' => 'baz')
);
// the "options" array is totally replaced
$cases[] = array(
array(
array('options' => array('timeout' => 2000)),
array('options' => array('username' => 'foo')),
),
array('options' => array('username' => 'foo')),
);
// mappings are merged non-recursively.
$cases[] = array(
array(
array('mappings' => array('foomap' => array('type' => 'val1'), 'barmap' => array('dir' => 'val2'))),
array('mappings' => array('barmap' => array('prefix' => 'val3'))),
),
array('mappings' => array('foomap' => array('type' => 'val1'), 'barmap' => array('prefix' => 'val3'))),
);
// connections are merged non-recursively.
$cases[] = array(
array(
array('connections' => array('foocon' => array('server' => 'val1'), 'barcon' => array('options' => array('username' => 'val2')))),
array('connections' => array('barcon' => array('server' => 'val3'))),
),
array('connections' => array(
'foocon' => array('server' => 'val1', 'options' => array()),
'barcon' => array('server' => 'val3', 'options' => array())
)),
);
// managers are merged non-recursively.
$cases[] = array(
array(
array('document_managers' => array('foodm' => array('database' => 'val1'), 'bardm' => array('default_database' => 'val2'))),
array('document_managers' => array('bardm' => array('database' => 'val3'))),
),
array('document_managers' => array(
'foodm' => array('database' => 'val1', 'mappings' => array()),
'bardm' => array('database' => 'val3', 'mappings' => array()),
)),
);
return $cases;
}
/**
* @dataProvider getNormalizationTests
*/
public function testNormalizeOptions(array $config, $targetKey, array $normalized)
{
$processor = new Processor();
$configuration = new Configuration();
$options = $processor->process($configuration->getConfigTree(), array($config));
$this->assertSame($normalized, $options[$targetKey]);
}
public function getNormalizationTests()
{
return array(
// connection versus connections (id is the identifier)
array(
array('connection' => array(
array('server' => 'mongodb://abc', 'id' => 'foo'),
array('server' => 'mongodb://def', 'id' => 'bar'),
)),
'connections',
array(
'foo' => array('server' => 'mongodb://abc', 'options' => array()),
'bar' => array('server' => 'mongodb://def', 'options' => array()),
),
),
// document_manager versus document_managers (id is the identifier)
array(
array('document_manager' => array(
array('connection' => 'conn1', 'id' => 'foo'),
array('connection' => 'conn2', 'id' => 'bar'),
)),
'document_managers',
array(
'foo' => array('connection' => 'conn1', 'mappings' => array()),
'bar' => array('connection' => 'conn2', 'mappings' => array()),
),
),
// mapping versus mappings (name is the identifier)
array(
array('mapping' => array(
array('type' => 'yml', 'name' => 'foo'),
array('type' => 'xml', 'name' => 'bar'),
)),
'mappings',
array(
'foo' => array('type' => 'yml'),
'bar' => array('type' => 'xml'),
),
),
// mapping configuration that's beneath a specific document manager
array(
array('document_manager' => array(
array('id' => 'foo', 'connection' => 'conn1', 'mapping' => array(
'type' => 'xml', 'name' => 'foo-mapping'
)),
)),
'document_managers',
array(
'foo' => array('connection' => 'conn1', 'mappings' => array(
'foo-mapping' => array('type' => 'xml'),
)),
),
),
);
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\DoctrineMongoDBBundle\Tests\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Bundle\DoctrineMongoDBBundle\DependencyInjection\DoctrineMongoDBExtension;
use Symfony\Component\Config\Definition\Processor;
class DoctrineMongoDBExtensionTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider parameterProvider
*/
public function testParameterOverride($option, $parameter, $value)
{
$container = new ContainerBuilder();
$loader = new DoctrineMongoDBExtension();
$loader->load(array(array($option => $value)), $container);
$this->assertEquals($value, $container->getParameter('doctrine.odm.mongodb.'.$parameter));
}
public function parameterProvider()
{
return array(
array('proxy_namespace', 'proxy_namespace', 'foo'),
array('proxy-namespace', 'proxy_namespace', 'bar'),
);
}
}