如何在Bundle内加载服务配置
在Symfony中,你会发现你自己使用了很多的服务。这些服务在你应用程序的app/config/
目录中被注册。但当你想要解耦这个Bundle(译注,如:成为第三方bundle 或者分离出来的bundle),让它们使用在其他项目中时,你就会想要让 bundle 本身拥有服务配置。这篇文章将会教你如何实现这个目标。
创建一个Extension(扩展)类 ¶
为了加载服务配置,你要为你的Bundle创建一个依赖注入(DI)扩展。这个类有一些约定,以便symfony自动被检测到。但你稍后会看到如何将它更改为你自己喜好的样子。默认情况下,扩展必须遵守以下约定:
它必须位于 bundle 的 DependencyInjection
命名空间之中;
它的名称要和 bundle 的一样,只是后缀由 Bundle
换成了 Extension
(例如 AppBundle 的 Extension(扩展) 类就会叫做 AppExtension
,同时 AcmeHelloBundle 就会被叫做 AcmeHelloExtension
)。
Extension(扩展)类应该实现 ExtensionInterface,但通常你只需要继承Extension类:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.PHP
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class AcmeHelloExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
// ... you'll load the files here later
}
} |
手动注册一个Extension (扩展)类 ¶
当没有遵守这些约定时,你必须手动注册你的 Extension 类。要做到这一点,你应该重写Bundle::getContainerExtension()方法来返回extension(扩展)实例:
1 2 3 4 5 6 7 8 9 10 |
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
class AcmeHelloBundle extends Bundle
{
public function getContainerExtension()
{
return new UnconventionalExtensionClass();
}
} |
由于新的Extension (扩展)类名没有遵循命名约定,你要需要重写Extension::getAlias()来返回正确的DI 别名。DI 别名是用来标识容器中的 bundle 的(如,在app/config/config.yml
文件中)。默认情况下,通过移除Extension
后缀并转换这个类名加入下划线来完成(例如,AcmeHelloExtension
别名是acme_hello
)。
使用load()方法 ¶
在load()
方法里,与extension(扩展)有关的所有服务和参数都将被加载。这个方法没有得到实际的容器实例,但是是一个副本。这个容器只有从实际容器中取来的参数。在加载服务和参数之后,副本就会合并复制到实际的容器,来保证所有的服务和参数也被添加到实际的容器之中。
在load()
方法里,您可以使用PHP代码注册服务定义,但更常见的做法是把这些定义放到配置文件中(可使用 Yaml, XML 或者 PHP)。幸运的是,您可以在extension(扩展中)使用文件加载器!
例如,假设你有一个名为services.xml
的文件在你bundle的Resources/config
目录,你的加载方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
// ...
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader(
$container,
new FileLocator(__DIR__.'/../Resources/config')
);
$loader->load('services.xml');
} |
你可以用其他加载器YamlFileLoader
,PhpFileLoader
和 IniFileLoader
。
IniFileLoader
只能用于加载参数,它只能将参数作为字符串加载。
如果你删除用来做服务定义的默认文件(如, app/config/services.yml
),也一定要确保从app/config/config.yml
中移除了它的imports
键。
使用配置去改变这个服务 ¶
Extension这个类也是处理 bundle 特定配置(例如 app/config/config.yml
中的配置)。想阅读更多关于它的信息,请看“如何给Bundle创建友好的配置”
添加要编译的类 ¶
Symfony在缓存目录创建了一个大的classes.php
文件,集合了每一个请求所使用的PHP类的内容。他减少了I/O操作,并提高了应用程序的性能。
你的Bundle也能添加他们自己的类到这个文件,多亏了addClassesToCompile()
方法。把要编译的类定义为一个全限定类名的数组:
1 2 3 4 5 6 7 8 9 10 11 |
// ...
public function load(array $configs, ContainerBuilder $container)
{
// ...
$this->addClassesToCompile(array(
'AppBundle\\Manager\\UserManager',
'AppBundle\\Utils\\Slugger',
// ...
));
} |
如果一些类从其他类继承,他所有的父类将自动被包含到要编译类的列表。
注意,这种技术在某些情况下不能使用:
当类包含注释,比如控制器有
@Route
注释和实体有@ORM
和@Assert
注释,因为它们被加入到classes.php
,php反射获取的文件位置已经变化了;当类使用了
__DIR__
和__FILE__
常量,因为当从classes.php
文件加载这些类时,他们的值会发生改变。