我将用一个例子来解释:
共有2个包:Foo\SecurityBundle
和Foo\MenuBundle
Foo\MenuBundle
的Menu类看起来像这样:
namespace Foo\MenuBundle;
use Foo\SecurityBundle\MenuSecurer; //note this
class Menu{
protected $securer;
public function __construct(MenuSecurer $securer = null){
$this->securer = $securer;
}
public function buildMenu(){
//build the $menu ...
//...
if($this->securer != null)
$securer->secure($menu);
}
}
安全包会自动注入$ menuSecurer(如果已安装),
但是问题是当没有安装安全包时,它的类也没有被定义,所以即使我没有真正使用它,我也不能在MenuBundle中use Foo\SecurityBundle...
。这是正确的方法吗?
答案 0 :(得分:1)
symfony docs中有一节处理这种情况: http://symfony.com/doc/current/book/service_container.html#optional-dependencies-setter-injection
如果你有一个类的可选依赖项,那么“setter injection” 可能是更好的选择。
根据这个,你的课可能看起来像这样:
namespace Foo\MenuBundle;
class Menu{
protected $securer;
public function setSecurer($securer) {
$this->securer = $securer;
}
public function buildMenu(){
//build the $menu ...
//...
if($this->securer != null)
$securer->secure($menu);
}
}
# config.yml
menu_service:
class: Foo\MenuBundle\Menu
calls:
- [setMailer, ["@securer"]]
像这样...... 遗憾的是,如果没有您知道存在的接口,您仍然无法使用use语句。
答案 1 :(得分:1)
有很多方法可以解决这个问题,但我认为一个好的方法是为类/服务添加一个配置设置,以便注入构造函数的第一个参数。
例如,在Foo\MenuBundle\Menu
类中(假设已将其定义为服务),您可以在捆绑包的配置中添加一个附加项,以定义{{1}的默认服务然后,如果需要,可以选择在配置中覆盖它。
在配置类($securer
)中:
DependecyInjection\Configuration.php
在扩展程序类(public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('foo_menu');
$rootNode
->children()
->arrayNode('service')
->addDefaultsIfNotSet()
->children()
->scalarNode('menu_securer')->defaultValue('foo_security.menu_securer')->end()
>end()
->end()
->end();
return $treeBuilder;
}
)中:
DependecyInjection\FooMenuExtension.php
您的服务定义看起来就像这样......
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
// ...
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
// This is what sets 'foo_menu.menu_securer' to the service you want
foreach ($config['service'] as $key => $service) {
$container->setAlias($this->getAlias() . '.' . $key, $service);
}
$loader->load('services.xml');
$container->getDefinition('foo_menu.menu.menu')
->replaceArgument(0, new Reference('foo_menu.menu_securer'));
}
现在在您的<service id="foo_menu.menu.menu" class="%foo_menu.menu.menu.class%">
<argument /> <!-- foo_menu.menu_securer -->
</service>
中,您可以通过在...下面定义服务来切换您想要使用的服务。
config.yml
编辑:关于类型提示,正如Markus所提到的,实现foo_menu:
service:
menu_securer: 'some_other.service'
必须实现的接口可能是个好主意。