Caution: You are browsing the legacy symfony 1.x part of this website.

День 16: Почта

1.4 / Doctrine
Symfony version
1.2
Language ORM

Вчера мы добавили веб-сервис для Jobeet. Партнеры теперь могут создать аккаунт, но он нуждается в активировании администратором, прежде чем он может быть использован. Поскольку партнеры должны получать свои токены (token), мы по-прежнему нуждаемся в том, чтобы реализовать email-уведомления. Это то, чем мы сегодня займемся.

Фреймворк Symfony поставляется с одним из лучших решений PHP для электронной почты: Swift Mailer. Конечно, библиотека полностью интегрирована в Symfony, с несколькими полезными возможностями, ставшими первыми в списке основных возможностей.

note

Symfony 1.3/1.4 использует Swift Mailer версии 4.1.

Отправка простых email-сообщений

Давайте начнем с отправки простого email-сообщения, чтобы уведомить партнера, что его аккаунт подтвержден и дать ему партнерский токен.

Заменим действие activate следующим кодом:

// apps/backend/modules/affiliate/actions/actions.class.php
class affiliateActions extends autoAffiliateActions
{
  public function executeListActivate()
  {
    $affiliate = $this->getRoute()->getObject();
    $affiliate->activate();
 
    // send an email to the affiliate
    $message = $this->getMailer()->compose(
      array('[email protected]' => 'Jobeet Bot'),
      $affiliate->getEmail(),
      'Jobeet affiliate token',
      <<<EOF
Your Jobeet affiliate account has been activated.
 
Your token is {$affiliate->getToken()}.
 
The Jobeet Bot.
EOF
    );
 
    $this->getMailer()->send($message);
 
    $this->redirect('jobeet_affiliate');
  }
 
  // ...
}

note

Чтобы код работал правильно, Вы должны заменить адрес [email protected] на реальный.

Управление почтой в Symfony централизовано вокруг объекта Mailer, который может быть получен в любом действии контроллера при помощи метода getMailer().

Метод compose() принимает четыре параметра и возвращает объект email сообщения:

  • email-адрес отправителя (from);
  • email-адрес(а) получателя (to);
  • тема сообщения;
  • текст сообщения.

Затем, отправка сообщения делается простым вызовом метода send() экземпляра объекта Mailer с объектом email-сообщения в качестве аргумента. Более быстрый способ - создать и отправить email одним действием путем вызова метода composeAndSend().

tip

Email-сообщение является экземпляром класса Swift_Message. Обратитесь к официальной документации Swift Mailer, чтобы узнать больше об этом объекте, и о том, как использовать более продвинутые возможности, например, отправку файлов.

Настройка

По умолчанию метод send() пытается использовать локальный SMTP-сервер, чтобы отправить сообщение получателю. Разумеется, как и многое в Symfony, это полностью настраиваемо.

Фабрики (Factories)

В течение предыдущих дней, мы уже говорили об основополагающих объектах Symfony таких, как пользователь (user), запрос (request), ответ (response) или маршрутизация (routing). Эти объекты автоматически создаются, настраиваются и управляются фреймворком Symfony. Они всегда доступны благодаря объекту sfContext, и как многие вещи во фреймворке они настраиваются в конфигурационном файле: factories.yml. Этот файл содержит настройки для различных окружений.

Когда объект sfContext инициализирует основные фабрики, он читает файл factories.yml, чтобы определить имена классов (class) и параметры (param), которые будут переданы в конструктор:

response:
  class: sfWebResponse
  param:
    send_http_headers: false

В приведенном фрагменте, чтобы создать фабрику запросов, Symfony создает объекты sfWebResponse и передает значение опции send_http_headers в качестве параметра.

sidebar

Класс sfContext

Объект sfContext содержит ссылки на основные объекты Symfony: запрос, ответ, пользователь и другие. Поскольку sfContext существует как одиночка (singleton), Вы можете использовать выражение sfContext::getInstance(), чтобы получить его из любого места, и затем получить доступ к любым основым объектам Symfony:

$mailer = sfContext::getInstance()->getMailer();

Каждый раз, когда Вы хотите использовать sfContext::getInstance() в одном из своих классов, подумайте дважды, поскольку это приведет к сильному сопряжению. Всегда гораздо лучше передать необходимый объект в качестве аргумента.

Вы можете даже использовать sfContext как реестр для добавления собственных объектов, посредством метода set(). Он принимает имя и объект в качестве аргументов и метод get() может быть позже использован для извлечения объекта по имени:

sfContext::getInstance()->set('job', $job);
$job = sfContext::getInstance()->get('job');

Стратегия отправки

Так же, как многие другие основныек объекты Symfony, Mailer - это фабрика. Таким образом, она настраивается в файле factories.yml. Конфигурация по умолчанию выглядит так:

mailer:
  class: sfMailer
  param:
    logging:           %SF_LOGGING_ENABLED%
    charset:           %SF_CHARSET%
    delivery_strategy: realtime
    transport:
      class: Swift_SmtpTransport
      param:
        host:       localhost
        port:       25
        encryption: ~
        username:   ~
        password:   ~

Когда создается новое приложение, локальный конфигурационный файл factories.yml перекрывает конфигурацию по умолчанию некоторыми более специфическими настройками для сред env и test:

test:
  mailer:
    param:
      delivery_strategy: none
 
dev:
  mailer:
    param:
      delivery_strategy: none

Настройка delivery_strategy говорит Symfony, как отправлять почту. По умолчанию, Symfony содержит четыре разных стратегии:

  • realtime: Сообщения отправляются в реальном времени.
  • single_address: Сообщения отправляются на один адрес.
  • spool: Сообщения ставятся в очередь на отправку.
  • none: Сообщения просто игнорируются.

Какой бы ни была стратегия, электронные сообщения всегда логгируются и доступны на вкладке "mailer" панели web-дебага.

Почтовый транспорт

Почтовые сообщения отправляются посредством транспорта. Транспорт настраивается в конфигурационном файле factories.yml, и конфигурация по умолчанию использует SMTP-сервер локальной машины:

transport:
  class: Swift_SmtpTransport
  param:
    host:       localhost
    port:       25
    encryption: ~
    username:   ~
    password:   ~

Swift Mailer поставляется с тремя стандартными классами транспорта:

  • Swift_SmtpTransport: Использует SMTP-сервер для отправки сообщений.

  • Swift_SendmailTransport: Использует объект sendmail для отправки сообщений.

  • Swift_MailTransport: Использует стандартную функцию PHP mail() для отправки сообщений.

tip

Секция "Типы транспорта" официальной документации Swift Mailer описывает все, что Вам нужно знать о встроенных классах транспорта и их разнообразных параметрах.

Тестирование работы с почтой

Теперь, поскольку мы увидели, как отправить email при помощи Symfony, давайте напишем функциональные тесты, чтобы убедиться, что все сделали правильно. По умолчанию, Symfony регистрирует тестер mailer (sfMailerTester) для облегчения тестирования работы с почтой в функциональных тестах.

First, change the mailer factory configuration for the test environment if your web server does not have a local SMTP server. We have to replace the current Swift_SmtpTransport class by Swift_MailTransport:

# apps/backend/config/factories.yml
test:
 
  # ...
 
  mailer:
    param:
      delivery_strategy: none
      transport:
        class:  Swift_MailTransport

Then, add a new test/fixtures/administrators.yml file containing the following YAML definition:

sfGuardUser:
  admin:
    email_address: [email protected]
    username: admin
    password: admin
    first_name: Fabien
    last_name: Potencier
    is_super_admin: true

Finally, replace the affiliate functional test file for the backend application with the following code:

// test/functional/backend/affiliateActionsTest.php
include(dirname(__FILE__).'/../../bootstrap/functional.php');
 
$browser = new JobeetTestFunctional(new sfBrowser());
$browser->loadData();
 
$browser->
  info('1 - Authentication')->
  get('/affiliate')->
  click('Signin', array(
    'signin' => array('username' => 'admin', 'password' => 'admin'),
    array('_with_csrf' => true)
  ))->
  with('response')->isRedirected()->
  followRedirect()->
 
  info('2 - When validating an affiliate, an email must be sent with its token')->
  click('Activate', array(), array('position' => 1))->
  with('mailer')->begin()->
    checkHeader('Subject', '/Jobeet affiliate token/')->
    checkBody('/Your token is symfony/')->
  end()
;

Каждое отправленное email-сообщение может быть протестировано при помощи методов checkHeader() и checkBody(). Второй аргумент checkHeader() и первый аргумент checkBody() может иметь одно из следующих значений:

  • строка, чтобы проверить точное совпадение;

  • регулярное выражение, которому должно удовлетворять значение;

  • обратное регулярное выражение (регулярное выражение, начинающееся с "!"), чтобы легко указать, с чем не должно совпадать значение.

note

По умолчанию, проверки выполняются для первого отправленного сообщения. Если несколько сообщений отправлено, Вы можете выбрать одно, которое хотите протестировать методом withMessage(). Метод withMessage() принимает адрес получателя в качестве первого аргумента. Также он принимает и второй аргумент, для указания, какое сообщение Вы хотите тестировать, если одному получателю отправлено несколько сообщений.

tip

Так же, как и для других встроенных тестеров, Вы можете увидеть полное сообщение, вызвав метод debug().

Увидимся завтра!

Завтра мы реализуем последнюю заявленную возможность сайта Jobeet - поисковый движок.