如何简化多个Bundle的配置

3.4 版本
维护中的版本

在构建可复用和可扩展的程序时,开发人员经常面临一个选择:要么创建一个大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,
));

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)