如何在 Bundle 內載入服務設定
Bundle 建立的服務並非定義在應用程式所使用的主要 config/services.yaml
檔案中,而是在 Bundle 本身中。本文說明如何使用 Bundle 目錄結構來建立和載入服務檔案。
有兩種不同的方法可以做到這一點:
- 在主要的 Bundle 類別中載入您的服務:建議用於新的 Bundle 以及遵循建議目錄結構的 Bundle;
- 建立 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');
}
其他可用的載入器是 YamlFileLoader
和 PhpFileLoader
。
使用組態來變更服務
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
檔案載入這些類別時,它們的值會變更。