如何简化多个Bundle的配置
在构建可复用和可扩展的程序时,开发人员经常面临一个选择:要么创建一个大bundle,要么创建多个小bundle。创建一个很大的bundle有一个欠点,那就是对用户来说“选择性地移除他们不需要的功能”是不可能的。创建多个bundle的欠点,则是配置会变得乏味,而各种bundle的设置经常发生重复。
使用下面的方法,通过启用一个单一的扩展(Extension)来预先设置好所有bundle,就可能消除多个bundle方式的缺点。扩展可以使用 app/config/config.yml
中定义的设置进行预设置,如同它们已经被用户在程序配置中显式地写出来一样。
例如,可以用它来配置entity manager的名称,以便能够在多个bundle中使用。或者,用它来启用一个“取决于另一个同样被加载的bundle”的可选功能。
要令扩展拥有此项能力,需要实现PrependExtensionInterface
接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.PHP
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AcmeHelloExtension extends Extension implements PrependExtensionInterface
{
// ...
public function prepend(ContainerBuilder $container)
{
// ...
}
} |
在prepend()
方法内部,开发者在“每个已注册bundle的扩展”的load()
方法被调用之前,拥有对ContainerBuilder
实例的完整控制。为了对bundle的扩展进行预先设置,开发人员可以使用ContainerBuilder
实例的prependExtensionConfig()
方法。由于这个方法只是在准备设置,任何在app/config/config.yml
中显式指定的配置,都会覆盖这些“预设置”。
下例展示了如何在多个bundle中对配置信息进行预设定,同时在“某个特定bundle没有被注册”时,如何在多个bundle中添加禁用旗标(flag):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public function prepend(ContainerBuilder $container)
{
// get all bundles 获取所有bundle
$bundles = $container->getParameter('kernel.bundles');
// determine if AcmeGoodbyeBundle is registered 判断AcmeGoodbyeBundle是否被注册
if (!isset($bundles['AcmeGoodbyeBundle'])) {
// disable AcmeGoodbyeBundle in bundles 在bundles中禁用AcmeGoodbyeBundle
$config = array('use_acme_goodbye' => false);
foreach ($container->getExtensions() as $name => $extension) {
switch ($name) {
case 'acme_something':
case 'acme_other':
// set use_acme_goodbye to false in the config of
// acme_something and acme_other note that if the user manually
// configured use_acme_goodbye to true in the app/config/config.yml
// then the setting would in the end be true and not false
// 在acme_something的配置信息中,将use_acme_goodbye设为false,
// 那么如果用户在app/config/config.yml中,
// 手动配置use_acme_goodbye为true的话,
// acme_other会收到这个信息,然后这个设置最终将是true,而非false
$container->prependExtensionConfig($name, $config);
break;
}
}
}
// process the configuration of AcmeHelloExtension
// AcmeHelloExtension的配置过程
$configs = $container->getExtensionConfig($this->getAlias());
// use the Configuration class to generate a config array with
// the settings "acme_hello"
// 使用Configuration类去生成带有“acme_hello”设置选项的配置数组
$config = $this->processConfiguration(new Configuration(), $configs);
// check if entity_manager_name is set in the "acme_hello" configuration
// 检查在“acme_hello”配置信息中,是否设置了entity_manager_name
if (isset($config['entity_manager_name'])) {
// prepend the acme_something settings with the entity_manager_name
// 在acme_something之前,设置entity_manager_name
$config = array('entity_manager_name' => $config['entity_manager_name']);
$container->prependExtensionConfig('acme_something', $config);
}
} |
上面这种方法,等同于把配置信息直接写入到app/config/config.yml
,如果AcmeGoodbyeBundle没有被注册并且acme_hello
选项的entity_manager_name
被设置成non_default
的话:
1 2 3 4 5 6 7 8 9 |
# app/config/config.yml
acme_something:
# ...
use_acme_goodbye: false
entity_manager_name: non_default
acme_other:
# ...
use_acme_goodbye: false |
1 2 3 4 5 6 |
<!-- app/config/config.xml -->
<acme-something:config use-acme-goodbye="false">
<acme-something:entity-manager-name>non_default</acme-something:entity-manager-name>
</acme-something:config>
<acme-other:config use-acme-goodbye="false" /> |
1 2 3 4 5 6 7 8 9 10 |
// app/config/config.php
$container->loadFromExtension('acme_something', array(
// ...
'use_acme_goodbye' => false,
'entity_manager_name' => 'non_default',
));
$container->loadFromExtension('acme_other', array(
// ...
'use_acme_goodbye' => false,
)); |