Как я могу определить дополнительные зависимости для отдельных пакетов?

0

Я объясню на примере: Есть 2 пакета: Foo\SecurityBundle и Foo\MenuBundle.
Foo\MenuBundle имеет класс меню, который выглядит следующим образом:

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, если он установлен, однако проблема заключается в том, что когда пакет безопасности не установлен, тогда его классы также не определены, поэтому я не могу use Foo\SecurityBundle... в MenuBundle даже хотя я действительно не использую его. Какая правильная дорога вокруг этого?

Теги:
namespaces
dependency-injection

2 ответа

1

Существует немало способов сделать это, но я думаю, что хорошим подходом было бы добавить параметр конфигурации для класса/службы, который должен быть введен в первый аргумент конструктора.

Например, в классе Foo\MenuBundle\Menu (при условии, что это уже определено как служба) вы можете добавить дополнительный элемент в конфигурацию связки для определения услуги по умолчанию для $securer, а затем опционально переопределить ее в config if желательно.

В классе конфигурации (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 вы можете просто отключить услугу, которую хотите использовать, указав ее ниже...

foo_menu:
    service:
        menu_securer: 'some_other.service'

Изменение: Что касается намека типа, как упоминалось Маркусом, вероятно, было бы неплохо реализовать интерфейс, $securer должен реализовать $securer.

1

В документах symfony есть раздел, посвященный этой ситуации: http://symfony.com/doc/current/book/service_container.html#optional-dependencies-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"]]

Что-то вроде этого... К сожалению, вы все еще не можете использовать инструкцию использования без интерфейса, который, как вы знаете, существует.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню