Статья рассказывает о том, как использовать теги в своих сервисах и о Compiler Passes, но для начала о возможностях Symfony.
Чтобы переоперделить/расширить сервисы, в Symfony существует 2 способа.
Первый способ - вы можете установить ваш собственный класс как параметр к классу сервиса в app/config/config.yml. Этот способ возможен только если имя класса определено как параметр в конфигурации бандла. Например, чтобы переопределить класс для Symfony сервиса translator, вы можете переопределить параметр translator.class (для translator, параметр определен и используется в Resources/config/translation.xml в ядре FrameworkBundle):
# app/config/config.yml
parameters:
translator.class: Acme\HelloBundle\Translation\Translator
* иногда чтобы найти какой-то параметр - надо немного погуглить.
Второй способ - используется, если класс не определен как параметр (в конфигурации). Вам придется переопределить класс определенного сервиса во время компиляции сервис-контейрена:
<?php
// src/Acme/DemoBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php
namespace Acme\DemoBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('original-service-id');
$definition->setClass('Acme\DemoBundle\YourService');
}
}
* 2 примера выше не относятся к коду ниже и приведены лишь в качестве демонстрации способов.
Создать сервис acme_mailer.transport_chain, который будет пытаться отправлять письмо несколькими способами (если одним способом не получилось - пытается следующим способом).
Как пример добавим следующие методы доставки (smtp, sendmail) в качестве сервисов:
# src/Acme/MailerBundle/Resources/config/services.yml
services:
acme_mailer.transport.smtp:
class: \Swift_SmtpTransport
arguments:
- %mailer_host%
tags:
- { name: acme_mailer.transport }
acme_mailer.transport.sendmail:
class: \Swift_SendmailTransport
tags:
- { name: acme_mailer.transport }
Как видите, сервисам мы указали свои теги и благодаря Symfony Compiler Passes мы обработаем эти теги.
Обратите внимание на тег acme_mailer.transport. Мы хотим чтобы бандл автоматически определял эти способы доставки и добавлял их в цепочку. Для того, чтобы этого добиться, надо добавить метод build() в класс AcmeMailerBundle:
<?php
namespace Acme\MailerBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Acme\MailerBundle\DependencyInjection\Compiler\TransportCompilerPass;
class AcmeMailerBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new TransportCompilerPass());
}
}
Вы наверняка заметили упоминание несуществующего пока класса TransportCompilerPass, давайте напишем его:
<?php
namespace Acme\MailerBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class TransportCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('acme_mailer.transport_chain')) {
return;
}
$definition = $container->getDefinition('acme_mailer.transport_chain');
foreach ($container->findTaggedServiceIds('acme_mailer.transport') as $id => $attributes) {
$definition->addMethodCall('addTransport', array(new Reference($id)));
}
}
}
Обратите внимание - сервисы, у которых есть тег acme_mailer.transport при помощи метода addTransport() добавились в реестр сервиса acme_mailer.transport_chain.
acme_mailer.transport_chain - сервис отправки почты, такой же как Symfony2 сервис mailer, но, который является реестром (коллекцией) классов (сервисов) которые реализовали интерфейс \Swift_Transport библиотеки Swift Mailer (библиотека отправки электронных писем, в Symfony2 она представлена как сервис mailer). Давайте объявим этот сервис:
# src/Acme/MailerBundle/Resources/config/services.yml
services:
acme_mailer.transport_chain:
class: Acme\MailerBundle\TransportChain
Теперь нам осталось реализовать TransportChain (транспортную цепочку - реестр).
<?php namespace Acme\MailerBundle; class TransportChain { private $transports; public function __construct() { $this->transports = array(); } public function addTransport(\Swift_Transport $transport) { $this->transports[] = $transport; } public function send(Swift_Mime_Message $message, &$failedRecipients = null) { /** @var \Swift_Transport $service */ foreach ($this->transports as $service) { if ($service->send($message , $failedRecipients)) { return 1; } } } }
Вот и все, теперь в своем контроллере Вы можете использовать данный сервис, как обычный сервис mailer:
$this->get('acme_mailer.transport_chain')->send($message);
Надеюсь, все хорошо разжевал, удачки.
p.s.
Подробности:
По соглашению, имена тегов должны начинаться с названия бандла (нижний регистр, подчеркивание в качестве разделителя), затем должна стоять точка, а в конце - “настоящее” название. Так, тег “transport” в бандле AcmeMailerBundle должен выглядеть следующим образом: acme_mailer.transport
Объявление скомпилированного сервиса
После пропуска через компилятор, в окончательной версии оболочки сервиса, появятся следующие строки. Если вы используете среду dev, то загляните в файл /cache/dev/appDevDebugProjectContainer.php и найдите там метод getTransportChainService(). Если вы всё сделали правильно, то он должен выглядеть следующим образом:
protected function getAcmeMailer_TransportChainService()
{
$this->services['acme_mailer.transport_chain'] = $instance = new \Acme\MailerBundle\TransportChain();
$instance->addTransport($this->get('acme_mailer.transport.smtp'));
$instance->addTransport($this->get('acme_mailer.transport.sendmail'));
return $instance;
}
Источник: 1
Комментарии
Комментариев пока нет.