跳到內容

如何在 Bundle 內載入服務設定

編輯此頁面

Bundle 建立的服務並非定義在應用程式所使用的主要 config/services.yaml 檔案中,而是在 Bundle 本身中。本文說明如何使用 Bundle 目錄結構來建立和載入服務檔案。

有兩種不同的方法可以做到這一點:

  1. 在主要的 Bundle 類別中載入您的服務:建議用於新的 Bundle 以及遵循建議目錄結構的 Bundle;
  2. 建立 Extension 類別來載入服務設定檔:這是傳統的做法,但現在僅建議用於遵循舊版目錄結構的 Bundle。

直接在您的 Bundle 類別中載入服務

在擴展 AbstractBundle 類別的 Bundle 中,您可以定義 loadExtension() 方法,從組態檔載入服務定義

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ...
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeHelloBundle extends AbstractBundle
{
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        // load an XML, PHP or YAML file
        $container->import('../config/services.xml');

        // you can also add or replace parameters and services
        $container->parameters()
            ->set('acme_hello.phrase', $config['phrase'])
        ;

        if ($config['scream']) {
            $container->services()
                ->get('acme_hello.printer')
                    ->class(ScreamingPrinter::class)
            ;
        }
    }
}

此方法的工作方式與下面說明的 Extension::load() 方法類似,但它使用新的更簡單的 API 來定義和匯入服務設定。

注意

Extension::load() 中的 $configs 參數相反,$config 參數已經由 AbstractBundle 合併和處理。

注意

loadExtension() 僅在編譯時呼叫。

建立 Extension 類別

這是 Bundle 中載入服務定義的傳統方式。對於新的 Bundle,建議在主要的 Bundle 類別中載入您的服務,但建立 Extension 類別的傳統方式仍然有效。

依賴注入 Extension 定義為遵循以下慣例的類別(稍後您將學習如何在需要時跳過它們):

  • 它必須位於 Bundle 的 DependencyInjection 命名空間中;
  • 它必須實作 ExtensionInterface,這通常透過擴展 Extension 類別來實現;
  • 名稱等於 Bundle 名稱,其中 Bundle 字尾替換為 Extension(例如,AcmeBundle 的 Extension 類別將稱為 AcmeExtension,而 AcmeHelloBundle 的 Extension 類別將稱為 AcmeHelloExtension)。

以下是 AcmeHelloBundle 的 Extension 應有的外觀:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;

class AcmeHelloExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container): void
    {
        // ... you'll load the files here later
    }
}

手動註冊 Extension 類別

當不遵循慣例時,您將必須手動註冊您的 Extension。為此,您應該覆寫 Bundle::getContainerExtension() 方法以傳回 Extension 的實例

1
2
3
4
5
6
7
8
9
10
11
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;

class AcmeHelloBundle extends Bundle
{
    public function getContainerExtension(): ?ExtensionInterface
    {
        return new UnconventionalExtensionClass();
    }
}

此外,當新的 Extension 類別名稱不遵循命名慣例時,您還必須覆寫 Extension::getAlias() 方法,以傳回正確的 DI 別名。DI 別名是用於在容器中引用 Bundle 的名稱(例如,在 config/packages/ 檔案中)。預設情況下,這是透過移除 Extension 字尾並將類別名稱轉換為底線來完成的(例如,AcmeHelloExtension 的 DI 別名為 acme_hello)。

使用 load() 方法

load() 方法中,將載入與此 Extension 相關的所有服務和參數。此方法不會取得實際的容器實例,而是取得副本。此容器僅具有來自實際容器的參數。載入服務和參數後,副本將合併到實際容器中,以確保所有服務和參數也新增到實際容器。

load() 方法中,您可以使用 PHP 程式碼來註冊服務定義,但如果您將這些定義放在組態檔中(使用 YAML、XML 或 PHP 格式),則更為常見。

例如,假設您的 Bundle 的 config/ 目錄中有一個名為 services.xml 的檔案,您的 load() 方法看起來像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

// ...
public function load(array $configs, ContainerBuilder $container): void
{
    $loader = new XmlFileLoader(
        $container,
        new FileLocator(__DIR__.'/../../config')
    );
    $loader->load('services.xml');
}

其他可用的載入器是 YamlFileLoaderPhpFileLoader

使用組態來變更服務

Extension 也是處理該特定 Bundle 組態的類別(例如,config/packages/<bundle_alias>.yaml 中的組態)。若要閱讀更多相關資訊,請參閱「如何為 Bundle 建立友善的組態」文章。

新增要編譯的類別

Bundle 可以提示 Symfony 哪些類別包含註解,以便在產生應用程式快取以提高整體效能時編譯它們。在 addAnnotatedClassesToCompile() 方法中定義要編譯的已註解類別的清單:

1
2
3
4
5
6
7
8
9
10
11
12
13
public function load(array $configs, ContainerBuilder $container): void
{
    // ...

    $this->addAnnotatedClassesToCompile([
        // you can define the fully qualified class names...
        'Acme\\BlogBundle\\Controller\\AuthorController',
        // ... but glob patterns are also supported:
        'Acme\\BlogBundle\\Form\\**',

        // ...
    ]);
}

注意

如果某些類別從其他類別擴展而來,則其所有父類別都會自動包含在要編譯的類別清單中。

模式會使用 Composer 產生的 classmap 轉換為實際的類別命名空間。因此,在使用這些模式之前,您必須執行 Composer 的 dump-autoload 命令來產生完整的 classmap。

警告

當要編譯的類別使用 __DIR____FILE__ 常數時,無法使用此技術,因為從 classes.php 檔案載入這些類別時,它們的值會變更。

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