Symfony по-умолчанию содержит в себе один из лучших решений в PHP для организации рассылки — Swift Mailer. Конечно же библиотека полностью интегрирована с Symfony и включает в себя некоторые дополнительные функции. Давайте начнем с отправки простого письма с токеном, в котором партнеру сообщается, что его аккаунт был активирован. Но сначала надо настроить несколько параметров в файле parameters.yml
:
# ...
# ...
mailer_transport: gmail
mailer_host: ~
mailer_user: [email protected]
mailer_password: your_password
# ...
Чтобы код работал корректно вам понадобится заменить [email protected] на настоящий email адрес. После этого очистите кеш для dev и prod сред:
php app/console cache:clear --env=dev
php app/console cache:clear --env=prod
Так как в качестве транспорта мы указали gmail, почтовый адрес должен быть от google. Процесс создания письма в Symfony напоминает работу с обычным почтовым клиентом. Вы должны указать тему, тело и одного или нескольких получателей письма.
Вам потребуется выполнить следующие шаги:
- вызвать метод класс Swift_message
newInstance()
(В официальной документации Swift Mailer вы найдете подробную информацию об этом методе). - Указать адрес отправителя —
setFrom()
. - Указать тему письма —
setSubject()
. - Указать получателей при помощи одного из следующих методов —
setTo()
,setCc()
,setBcc()
. - Указать тело письма —
setBody()
.
Замените метод activate
на следующий код:
<?php
# src/App/JoboardBundle/Controller/AffiliateAdminController.php
// ...
public function activateAction($id)
{
if ($this->admin->isGranted('EDIT') === false) {
throw new AccessDeniedException();
}
$em = $this->getDoctrine()->getManager();
$affiliate = $em->getRepository('AppJoboardBundle:Affiliate')->findOneById($id);
try {
$affiliate->setIsActive(true);
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Токен партнёра')
->setFrom('[email protected]')
->setTo($affiliate->getEmail())
->setBody(
$this->renderView(
'AppJoboardBundle:Affiliate:email.txt.twig',
['affiliate' => $affiliate->getToken()]
))
;
$this->get('mailer')->send($message);
} catch(\Exception $e) {
$this->get('session')->setFlash('sonata_flash_error', $e->getMessage());
}
return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters()));
}
// ...
Отправка письма осуществляется методом send()
и передачи ему самого сообщения в качестве параметра.
Для тела сообщения мы создадим новый файл — email.txt.twig, который будет содержать тело письма.
<!-- src/App/JoboardBundle/Resources/views/Affiliate/email.txt.twig -->
Ваш партнёрский аккаунт был успешно активирован.
Ваш секретный ключ {{affiliate}}.
Вы можете просматривать новые вакансии по следующим ссылкам:
http://joboard.local/app_dev.php/api/{{affiliate}}/jobs.xml
или http://joboard.local/app_dev.php/api/{{affiliate}}/jobs.json
или http://joboard.local/app_dev.php/api/{{affiliate}}/jobs.yaml
Теперь добавим новый функционал в метод batchActionActivate
, чтобы мы могли одновременно сделать рассылку для нескольких партнеров:
<?php
# src/App/JoboardBundle/Controller/AffiliateAdminController.php
// ...
public function batchActionActivate(ProxyQueryInterface $selectedModelQuery)
{
// ...
try {
foreach($selectedModels as $selectedModel) {
$selectedModel->activate();
$modelManager->update($selectedModel);
$message = \Swift_Message::newInstance()
->setSubject('Токен партнёра')
->setFrom('[email protected]')
->setTo($selectedModel->getEmail())
->setBody(
$this->renderView(
'AppJoboardBundle:Affiliate:email.txt.twig',
['affiliate' => $selectedModel->getToken()]
))
;
$this->get('mailer')->send($message);
}
} catch(\Exception $e) {
$this->get('session')->setFlash('sonata_flash_error', $e->getMessage());
return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters()));
}
// ...
}
// ...
Тестирование
После того как мы добавили новый функционал для отправки писем, настало время создать тест для проверки, что все работает как положено.
Для проверки функционала мы должны быть авторизованы, а для этого нам понадобится имя пользователя и пароль. Поэтому создадим новый файл фикстур, где добавим пользователя с правами администратора.
<?php
namespace App\JoboardBundle\DataFixtures\ORM;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use App\JoboardBundle\Entity\User;
class LoadUserData implements FixtureInterface, OrderedFixtureInterface, ContainerAwareInterface
{
/**
* @var ContainerInterface
*/
private $container;
/**
* {@inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* @param \Doctrine\Common\Persistence\ObjectManager $em
*/
public function load(ObjectManager $em)
{
$user = new User();
$user->setUsername('admin');
$encoder = $this->container
->get('security.encoder_factory')
->getEncoder($user)
;
$encodedPassword = $encoder->encodePassword('111111', $user->getSalt());
$user->setPassword($encodedPassword);
$em->persist($user);
$em->flush();
}
public function getOrder()
{
return 4;
}
}
В этом тесте мы воспользуемся сборщиком swiftmaler, чтобы получить информацию об отправленных письмах в предыдущих запросах. Теперь проверим правильно ли отправляются сами сообщения:
<?php
namespace App\JoboardBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Input\ArrayInput;
use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\CreateSchemaDoctrineCommand;
class AffiliateAdminControllerTest extends WebTestCase
{
private $em;
private $application;
public function setUp()
{
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->application = new Application(static::$kernel);
// удаляем базу
$command = new DropDatabaseDoctrineCommand();
$this->application->add($command);
$input = new ArrayInput(array(
'command' => 'doctrine:database:drop',
'--force' => true
));
$command->run($input, new NullOutput());
// закрываем соединение с базой
$connection = $this->application->getKernel()->getContainer()->get('doctrine')->getConnection();
if ($connection->isConnected()) {
$connection->close();
}
// создаём базу
$command = new CreateDatabaseDoctrineCommand();
$this->application->add($command);
$input = new ArrayInput(array(
'command' => 'doctrine:database:create',
));
$command->run($input, new NullOutput());
// создаём структуру
$command = new CreateSchemaDoctrineCommand();
$this->application->add($command);
$input = new ArrayInput(array(
'command' => 'doctrine:schema:create',
));
$command->run($input, new NullOutput());
// получаем Entity Manager
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager();
// загружаем фикстуры
$client = static::createClient();
$loader = new \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader($client->getContainer());
$loader->loadFromDirectory(static::$kernel->locateResource('@AppJoboardBundle/DataFixtures/ORM'));
$purger = new \Doctrine\Common\DataFixtures\Purger\ORMPurger($this->em);
$executor = new \Doctrine\Common\DataFixtures\Executor\ORMExecutor($this->em, $purger);
$executor->execute($loader->getFixtures());
}
public function testActivate()
{
$client = static::createClient();
$client->enableProfiler();
$crawler = $client->request('GET', '/login');
$form = $crawler->selectButton('войти')->form([
'_username' => 'admin',
'_password' => '111111'
]);
$client->submit($form);
$client->followRedirect();
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
$crawler = $client->request('GET', '/admin/app/joboard/affiliate/list');
$link = $crawler->filter('.btn.edit_link')->link();
$client->click($link);
$mailCollector = $client->getProfile()->getCollector('swiftmailer');
// Check that an e-mail was sent
$this->assertEquals(1, $mailCollector->getMessageCount());
$collectedMessages = $mailCollector->getMessages();
$message = $collectedMessages[0];
// Asserting e-mail data
$this->assertInstanceOf('Swift_Message', $message);
$this->assertEquals('Токен партнёра', $message->getSubject());
$this->assertRegExp(
'/Ваш секретный ключ symfony/',
$message->getBody()
);
}
}
Скорей всего при выполнении теста, вы получите ошибку. Чтобы её исправить, проверьте включен ли у вас profiler
. для среды test
. Если значение enabled
выставлено в false
, то его надо изменить на true
:
# app/config/config_test.yml
# ...
framework:
test: ~
session:
storage_id: session.storage.mock_file
profiler:
enabled: true
collect: true
# ...
Теперь осталось только очистить кеш и выполнить тест.
phpunit -c app src/App/JoboardBundle/Tests/Controller/AffiliateAdminControllerTest