跳到內容

如何簡化多個 Bundles 的設定

編輯此頁

在建構可重複使用且可擴展的應用程式時,開發者經常面臨一個選擇:建立單一大型 bundle 或多個較小的 bundle。建立單一 bundle 的缺點是用戶無法移除未使用的功能。建立多個 bundle 的缺點是設定變得更加繁瑣,並且設定經常需要在多個 bundle 中重複。

透過啟用單一 Extension 來預先設定任何 bundle 的設定,有可能消除多個 bundle 方法的缺點。它可以使用在 config/* 檔案中定義的設定來預先設定設定,就像它們是由使用者在應用程式設定中明確寫入一樣。

例如,這可以用於設定在多個 bundle 中使用的實體管理器名稱。或者它可以被用於啟用一個可選功能,該功能也取決於另一個 bundle 是否已載入。

為了給予 Extension 執行此操作的能力,它需要實作 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\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class AcmeHelloExtension extends Extension implements PrependExtensionInterface
{
    // ...

    public function prepend(ContainerBuilder $container): void
    {
        // ...
    }
}

prepend() 方法中,開發者可以完全存取 ContainerBuilder 實例,就在每個已註冊 bundle 的 Extensions 呼叫 load() 方法之前。為了將設定預先設定到 bundle extension,開發者可以使用 prependExtensionConfig() 方法在 ContainerBuilder 實例上。由於此方法僅預先設定設定,因此在 config/* 檔案中明確完成的任何其他設定都將覆蓋這些預先設定的設定。

以下範例說明如何在多個 bundle 中預先設定配置設定,以及在未註冊特定其他 bundle 的情況下,如何在多個 bundle 中停用標誌

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
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
public function prepend(ContainerBuilder $container): void
{
    // get all bundles
    $bundles = $container->getParameter('kernel.bundles');
    // determine if AcmeGoodbyeBundle is registered
    if (!isset($bundles['AcmeGoodbyeBundle'])) {
        // disable AcmeGoodbyeBundle in bundles
        $config = ['use_acme_goodbye' => false];
        foreach ($container->getExtensions() as $name => $extension) {
            match ($name) {
                // 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 config/services.yaml
                // then the setting would in the end be true and not false
                'acme_something', 'acme_other' => $container->prependExtensionConfig($name, $config),
                default => null
            };
        }
    }

    // get the configuration of AcmeHelloExtension (it's a list of configuration)
    $configs = $container->getExtensionConfig($this->getAlias());

    // iterate in reverse to preserve the original order after prepending the config
    foreach (array_reverse($configs) as $config) {
        // check if entity_manager_name is set in the "acme_hello" configuration
        if (isset($config['entity_manager_name'])) {
            // prepend the acme_something settings with the entity_manager_name
            $container->prependExtensionConfig('acme_something', [
                'entity_manager_name' => $config['entity_manager_name'],
            ]);
        }
    }
}

以上內容相當於在 AcmeGoodbyeBundle 未註冊的情況下,將以下內容寫入 config/packages/acme_something.yaml,並且 acme_helloentity_manager_name 設定設定為 non_default

1
2
3
4
5
6
7
8
9
# config/packages/acme_something.yaml
acme_something:
    # ...
    use_acme_goodbye: false
    entity_manager_name: non_default

acme_other:
    # ...
    use_acme_goodbye: false

在 Bundle 類別中預先設定 Extension

如果您從 AbstractBundle 類別擴展並定義 prependExtension() 方法,您也可以直接在您的 Bundle 類別中預先設定 extension 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class FooBundle extends AbstractBundle
{
    public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
    {
        // prepend
        $containerBuilder->prependExtensionConfig('framework', [
            'cache' => ['prefix_seed' => 'foo/bar'],
        ]);

        // prepend config from a file
        $containerConfigurator->import('../config/packages/cache.php');
    }
}

注意

prepend() 一樣,prependExtension() 方法僅在編譯時呼叫。

7.1

從 Symfony 7.1 開始,在 prependExtension() 內部呼叫 import() 方法將會預先設定給定的配置。在先前的 Symfony 版本中,此方法附加了配置。

或者,您可以使用 extension() 方法的 prepend 參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class FooBundle extends AbstractBundle
{
    public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
    {
        // ...

        $containerConfigurator->extension('framework', [
            'cache' => ['prefix_seed' => 'foo/bar'],
        ], prepend: true);

        // ...
    }
}

7.1

extension() 方法的 prepend 參數是在 Symfony 7.1 中新增的。

多個 Bundle 使用 PrependExtensionInterface

如果有多個 bundle 預先設定相同的 extension 並定義相同的鍵,則首先註冊的 bundle 將具有優先權:後續的 bundle 將不會覆蓋此特定配置設定。

這項工作,包括程式碼範例,根據 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本