por Fabien Potencier
O envio de e-mails com o symfony é simples e poderoso, graças ao uso da biblioteca Swift Mailer. Apesar do Swift Mailer tornar fácil o envio de e-mails, o symfony fornece uma camada adicional acima dele para tornar o envio de e-mails ainda mais flexível e poderoso. Este capítulo vai ensiná-lo como ter todo o seu poder à sua disposição.
note
O symfony 1.3 vem com o Swift Mailer versão 4.1.
Introdução
A gestão de e-mails no symfony é centrada no objeto mailer. E, como muitos
outros objetos do núcleo do symfony, o mailer é uma factory. Ele é configurado no
arquivo factories.yml, e está sempre disponível através da instância do contexto (context):
$mailer = sfContext::getInstance()->getMailer();
tip
Ao contrário de outras factories, o mailer é carregado e inicializado por demanda. Se você não utilizá-lo, não há qualquer impacto no desempenho.
Este tutorial explica a integração do Swift Mailer no symfony. Se você quiser aprender os pequenos detalhes da biblioteca Swift Mailer, veja a documentação dedicada.
Enviando e-mails a partir de uma ação
Em uma ação, é simples recuperar a instância do mailer
utilizando o método atalho getMailer():
$mailer = $this->getMailer();
A forma mais rápida
Enviar um e-mail é tão simples como usar o método sfAction::composeAndSend():
$this-getMailer->()->composeAndSend( 'from@example.com', 'fabien@example.com', 'Subject', 'Body' );
O método composeAndSend() possui quatro argumentos:
- O endereço de e-mail do remetente(
from); - O endereço de e-mail do destinatário(s) (
to); - O assunto da mensagem;
- O corpo da mensagem.
Sempre que um método tem um endereço de email como parâmetro, você pode passar uma string ou um array:
$address = 'fabien@example.com'; $address = array('fabien@example.com' => 'Fabien potencier');
Claro, você pode enviar um e-mail para várias pessoas ao mesmo tempo, passando um array de e-mails como o segundo argumento do método:
$to = array ( 'foo@example.com', 'bar@example.com', ); $this->getMailer->()->composeAndSend('from@example.com', $to, 'Assunto', 'Corpo'); $to = array ( 'foo@example.com' => 'Sr. Foo', 'bar@example.com' => 'Miss Bar', ); $this->getMailer->()->composeAndSend('from@example.com', $to, 'Assunto', 'Corpo');
A forma Flexível
Se precisar de mais flexibilidade, você também pode usar o método sfAction::compose()
para criar uma mensagem, personalizá-la do jeito que você quiser, e eventualmente enviá-la.
Isso é útil, por exemplo, quando você precisa adicionar um
anexo (attachment) como mostrado abaixo:
// Cria um objeto de mensagem $mensagem = $this->getMailer() ->compose('from@example.com','fabien@example.com','Assunto','Corpo') ->attach(Swift_Attachment::fromPath('/caminho/para/um/arquivo.zip/')) ; // Envia a mensagem $this->getMailer()->send($mensagem);
A Forma Poderosa
Você também pode criar um objeto de mensagem diretamente, para uma flexibilidade ainda maior:
$mensagem = Swift_Message:: newInstance() ->setFrom('from@example.com') ->setTo('to@example.com') ->setSubject('Assunto') ->setBody('Corpo') ->attach(Swift_Attachment::fromPath('caminho/para/um/arquivo.zip')) ; $this->getMailer()->send($mensagem);
tip
As sessões "Criando mensagens" e "Cabeçalhos das mensagens" da documentação oficial do Swift Mailer descrevem tudo o que você precisa saber sobre a criação de mensagens.
Usando a camada de Visão do Symfony (Symfony View)
Enviar e-mails de suas ações permite aproveitar o poder das parciais (partials) e componentes (components) com bastante facilidade.
$mensagem->setBody($this->getPartial('partial_name', $arguments));
Configuração
Como qualquer outra factory do symfony, o mailer pode ser configurado no
arquivo de configuração factories.yml. A configuração padrão é a
seguinte:
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: ~
Ao criar uma nova aplicação, o arquivo local de configuração factories.yml
substitui a configuração padrão com alguns padrões sensíveis aos
ambientes prod, env e test:
test:
mailer:
param:
delivery_strategy: none
dev:
mailer:
param:
delivery_strategy: none
A Estratégia de Entrega
Uma das características mais úteis da integração do Swift Mailer no symfony é
a estratégia de entrega. A estratégia de entrega permite-lhe dizer como o symfony
entregará as mensagens de e-mail e, é configurado através da configuração delivery_strategy
no factories.yml. A estratégia muda a forma do comportamento do
método send()|sfMailer::send(). Por padrão, quatro estratégias estão disponíveis,
que deverá atender todas as necessidades comuns:
realtime: As mensagens são enviadas em tempo real.single_address: As mensagens são enviadas para um único endereço.spool: As mensagens são armazenadas em uma fila (queue).none: As mensagens são simplesmente ignoradas.
A Estratégia realtime
A estratégia realtime é a estratégia de entrega padrão, e mais fácil de
configurar, pois não há nada de especial a fazer.
As mensagens são enviadas através do transporte configurado na seção transport
do arquivo de configuração factories.yml (veja a próxima seção para
mais informações sobre como configurar o transporte de correio (mail)).
A Estratégia single_address
Com a estratégia single_address, todas as mensagens são enviadas para um único endereço,
configurado através da definição delivery_address.
Esta estratégia é muito útil no ambiente de desenvolvimento para evitar o envio de mensagens para os usuários reais, mas ainda permite ao desenvolvedor verificar a renderização da mensagem em um leitor de e-mail.
tip
Se você precisar verificar os destinatários to, cc e bcc originais, eles estão
disponíveis como valores dos seguintes cabeçalhos (headers): X-Swift-To, X-Swift-Cc e
X-Swift-BCC, respectivamente.
As mensagens são enviadas através do mesmo transporte de e-mail utilizado para a
estratégia realtime.
A Estratégia spool
Com a estratégia spool, as mensagens são armazenadas em uma fila (queue).
Esta é a melhor estratégia para o ambiente de produção, pois as requisções web não precisam esperar os e-mails serem enviados.
A classe spool é configurada com a configuração spool_class. Por padrão,
o symfony já vem com três delas:
Swift_FileSpool: As mensagens são armazenadas no sistema de arquivos.Swift_DoctrineSpool: As mensagens são armazenadas em um modelo do Doctrine.Swift_PropelSpool: As mensagens são armazenadas em um modelo do Propel.
Quando o spool é instanciado, a configuração spool_arguments é utilizada com
os argumentos do construtor. Aqui estão as opções disponíveis para a classe de filas integradas:
Swift_FileSpool:- O caminho absoluto do diretório de fila (as mensagens são armazenadas neste diretório)
Swift_DoctrineSpool:O modelo do Doctrine que será usado para armazenar as mensagens (
MailMessagepor padrão)O nome da coluna que será usada para o armazenamento das mensagens (
messagepor padrão)O método a ser chamado para recuperar as mensagens a serem enviadas (opcional). Ele recebe as opções da fila como argumento.
Swift_PropelSpool:O modelo do Propel que será usado para armazenar as mensagens (
MailMessagepor padrão)O nome da coluna que será usada para armazenamento das mensagens (
messagepor padrão)O método a ser chamado para recuperar as mensagens a serem enviadas (opcional). Ele recebe as opções da fila como argumento.
Aqui está uma configuração clássica para um spool do Doctrine:
# Configuração do esquema no schema.yml
MailMessage:
actAs: {Timestampable: ~}
columns:
message: {type: clob, notnull: true}
# Configuração no factories.yml
mailer:
class: sfMailer
param:
delivery_strategy: spool
spool_class: Swift_DoctrineSpool
spool_arguments: [MailMessage, message, getSpooledMessages]
E a mesma configuração para um spool do Propel:
# Configuração do esquema no schema.yml
mail_message:
message {type: clob, required: true}
created_at: ~
# Configuração no factories.yml
dev:
mailer:
param:
delivery_strategy: spool
spool_class: Swift_PropelSpool
spool_arguments: [MailMessage, message, getSpooledMessages]
Para enviar a mensagem armazenada em uma fila (queue), você pode usar a tarefa project:send-emails
(note que essa tarefa é totalmente independente da implementação da fila (queue),
e as suas opções):
$ php symfony project:send-emails
note
A tarefa project:send-emails recebe as opções application e env
Ao chamar a tarefa project:send-emails,as mensagens de e-mail são enviadas através do
mesmo transporte que é utilizado para a estratégia realtime.
tip
Note que a tarefa project:send-emails pode ser executada em qualquer máquina, não
necessariamente na mesma máquina que criou a mensagem. Ela funciona porque
tudo é armazenado no objeto da mensagem, até mesmo os anexos de arquivo.
note
A implementação integrada das filas é muito simples. Elas enviam e-mails
sem qualquer controle de erro, exatamente como elas teriam sido enviadas se você tivesse usado
a estratégia realtime. Obviamente, as classes de fila (queue) padrão podem ser estendidas
para implementar sua própria lógica e gerenciamento de erros.
A tarefa project:send-emails recebe duas opções opcionais:
message-limit: Limita o número de mensagens enviadas.time-limit: Limita o tempo gasto para enviar as mensagens (em segundos).
Ambas as opções podem ser combinadas:
$ php symfony project:send-emails -message-limit=10 --time-limit=20
O comando acima irá parar de enviar mensagens quando 10 mensagens forem enviadas ou após 20 segundos.
Mesmo quando se utiliza a estratégia spool, talvez você precise enviar uma mensagem
imediatamente, sem armazená-la na fila. Isso é possível usando o
método especial sendNextImmediately() do mailer:
$this->getMailer()->sendNextImmediately()->send($mensagem);
No exemplo anterior, a $mensagem não será armazenada na fila e
será enviada imediatamente. Como o próprio nome sugere, o método sendNextImmediately()
afeta somente a próxima mensagem a ser enviada.
note
O método sendNextImmediately() não tem efeito especial, quando a
estratégia de envio não é spool.
A Estratégia none
Esta estratégia é útil no ambiente de desenvolvimento para evitar que os e-mails sejam enviados para os usuários reais. As mensagens ainda estão disponíveis na barra de ferramentas para debug web (web debug toolbar) (mais informações na seção abaixo sobre o painel mailer da barra de ferramentas para debug web).
É também a melhor estratégia para o ambiente de teste, onde o
objeto sfTesterMailer permite a introspecção das mensagens sem a necessidade
de realmente enviá-las (mais informações na seção abaixo sobre teste).
O transporte de e-mail
As mensagens de e-mail são realmente enviadas por um transporte. O transporte está configurado no
arquivo de configuração factories.yml, e a configuração padrão utiliza
o servidor SMTP da máquina local:
transport:
class: Swift_SmtpTransport
param:
host: localhost
port: 25
encryption: ~
username: ~
password: ~
O Swift Mailer vem com três classes de transporte diferentes:
Swift_SmtpTransport: Usa um servidor SMTP para enviar mensagens.Swift_SendmailTransport: Usa osendmailpara enviar mensagens.Swift_MailTransport: Usa a função nativa do PHPmail()para enviar as mensagens
tip
A seção "Tipos de Transportes" da documentação oficial do Swift Mailer descreve tudo o que você precisa saber sobre as classes integradas de transporte e seus diferentes parâmetros.
Enviando um E-mail Através de uma Tarefa
O envio de e-mail através de uma tarefa é bastante semelhante ao envio de um e-mail em uma
ação, pois o sistema de tarefas também oferece o método getMailer().
Ao criar o mailer, o sistema de tarefas depende da configuração atual.
Assim, se você quiser usar uma configuração de uma aplicação específica, você deve
passar a opção --application (veja no capítulo sobre tarefas para mais
informações sobre esse assunto).
Observe que a tarefa usa a mesma configuração dos controladores (controllers). Assim, se
deseja forçar a entrega quando a estratégia spool é usada, utilize o
sendNextImmediately():
$this->getMailer()->sendNextImmediately()->send($mensagem);
Depuração
Tradicionalmente, a depuração de e-mails tem sido um pesadelo. Com o symfony, é muito fácil, graças à web debug toolbar (barra de ferramentas para debug web).
A partir do conforto do seu navegador, você pode fácil e rapidamente ver quantas mensagens foram enviadas pela ação atual:

Se você clicar no ícone e-mail, as mensagens enviadas são exibidas no painel em sua forma bruta, conforme mostrado abaixo.

note
Cada vez que uma mensagem é enviada, o symfony também adiciona uma mensagem no log.
Testando
Naturalmente, a integração não seria completa sem uma forma de testar
as mensagens de e-mail. Por padrão, o symfony registra um mailer de teste
(sfMailerTester) para facilitar o teste de e-mails com testes funcionais.
O método hasSent() testa o número de mensagens enviadas durante o pedido (request) atual:
$browser-> get('/foo')-> with('mailer')-> hasSent(1) ;
O código anterior verifica se a URL /foo envia apenas um e-mail.
Cada e-mail enviado ainda pode ser testado com a ajuda dos métodos (checkHeader)
e checkBody() :
$browser-> get('/foo')-> with('mailer')->begin()-> hasSent(1)-> checkHeader('Subject', '/Subject/')-> checkBody('/Body/')-> end() ;
O segundo argumento do checkHeader() e o primeiro argumento do checkBody()
pode ser um dos seguintes:
Uma string para buscar uma correspondência exata;
Uma expressão regular para verificar o valor;
Uma expressão regular negativa (uma expressão regular iniciando com um
!)) para verificar se o valor não corresponde.
Por padrão, as verificações são feitas com a primeira mensagem enviada. Se várias mensagens
foram enviadas, você pode escolher qual deseja testar com o
método withMessage():
$browser-> get('/foo')-> with('mailer')->begin()-> hasSent(2)-> withMessage('foo@example.com')-> checkHeader('Assunto', '/Assunto/')-> checkBody('/Corpo/')-> end() ;
O withMessage() tem um destinatário como seu primeiro argumento. Ele também possui um
segundo argumento para indicar qual a mensagem que você deseja testar se várias
foram enviadas para o mesmo destinatário.
Por último, mas não menos importante, o método debug() descarrega as mensagens enviadas para verificar
os problemas quando um teste falhar:
$browser-> get('/foo')-> with('mailer')-> debug() ;
Mensagens de E-mail como Classes
Na introdução deste capítulo, você aprendeu como enviar e-mails a partir de uma ação. Esta é provavelmente a maneira mais fácil de enviar e-mails em uma aplicação symfony e, provavelmente, a melhor quando você só precisa enviar poucas mensagens simples.
Mas quando sua aplicação precisa gerenciar um grande número de diferentes mensagens de e-mail, você provavelmente utilizará uma estratégia diferente.
note
Como um bônus, utilizar classes para mensagens de email significa que a mesma mensagem de e-mail pode ser utilizada em diferentes aplicações; uma frontend e uma backend por exemplo.
As mensagens são objetos simples do PHP, a forma mais óbvia para organizar suas mensagens é criar uma classe para cada uma delas:
// lib/email/ProjectConfirmationMessage.class.php class ProjectConfirmationMessage extends Swift_Message { public function __construct() { parent::__construct('Assunto', 'Corpo'); $this ->setFrom(array('app@example.com' => 'Minha Aplicação Robô')) ->attach('...') ; } }
Enviar uma mensagem através de uma ação, ou de qualquer outro lugar, é uma simples questão de instanciar a classe certa da mensagem:
$this->getMailer()->send(new ProjectConfirmationMessage());
Claro, adicionando uma classe base para centralizar os cabeçalhos compartilhados como o
cabeçalho From, ou para adicionar uma assinatura comum pode ser conveniente:
// lib/email/ProjectConfirmationMessage.class.php class ProjectConfirmationMessage extend ProjectBaseMessage { public function __construct() { parent::__construct('Assunto', 'Corpo'); // Cabeçalhos específicos, anexos ... $this->attach('...'); } } // lib/email/ProjectConfirmationMessage.class.php class ProjectBaseMessage extends Swift_Message { public function __construct($assunto, $corpo) { $corpo.= <<<EOF -- E-mail enviado pela Minha Aplicação Robô EOF ; parent::__construct($assunto, $corpo); // Definir todos os cabeçalhos compartilhados $this->setFrom(array('app@example.com' => 'Minha Aplicação Robô')); } }
Se uma mensagem depende de alguns objetos do modelo, você pode, naturalmente, passá-las como argumentos para o construtor:
// lib/email/ProjectConfirmationMessage.class.php class ProjectConfirmationMessage extends ProjectBaseMessage { public function __construct($usuario) { parent::__construct('Confirmação para'. $usuario->getNome(), 'Corpo'); } }
Receitas
Enviar e-mails através do Gmail
Se você não tem um servidor SMTP, mas possui uma conta do Gmail, use a seguinte configuração para utilizar os servidores do Google para enviar e arquivar mensagens:
transport:
class: Swift_SmtpTransport
param:
host: smtp.gmail.com
port: 465
encryption: SSL
username: seu_usuario_do_gmail_vem_aqui
password: sua_senha_do_gmail_vem_aqui
Substitua o username e password com as suas credenciais do Gmail e você está
pronto.
Personalizando o Objeto Mailer
Se configurar o mailer através do factories.yml não for o suficiente, você pode
escutar (listen) o evento mailer.configure, e personalizar ainda mais o mailer.
Você pode se conectar à esse evento em sua classe ProjectConfiguration como mostrado
abaixo:
class ProjectConfiguration extends sfProjectConfiguration { public function setup() { // ... $this->dispatcher->connect( 'mailer.configure', array($this, 'configureMailer') ); } public function configureMailer(sfEvent $event) { $mailer = $event->getSubject(); // Fazer algo com o mailer } }
A seção a seguir ilustra um uso desta poderosa técnica.
Usando Plugins do Swift Mailer
Para usar os plugins do Swift Mailer, escute (listen) o evento mailer.configure (ver a
seção acima):
public function configureMailer(sfEvent $event) { $mailer = $event-> getSubject(); $plugin = new Swift_Plugins_ThrottlerPlugin( 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE ); $mailer->registerPlugin($plugin); }
tip
A seção "Plugins" da documentação oficial do Swift Mailer descreve tudo o que você precisa saber sobre os plugins integrados.
Personalizar o Comportamento do Spool
A implementação integrada dos spools é muito simples. Cada spool recupera todos os e-mails da fila em uma ordem aleatória e os envia.
Você pode configurar um spool para limitar o tempo gasto no envio de e-mails (em segundos), ou para limitar o número de mensagens a enviar:
$spool = $mailer->getSpool(); $spool->setMessageLimit(10); $spool->setTimeLimit(10);
Nesta seção, você vai aprender como implementar um sistema de prioridade para a fila (queue). Ele lhe dará todas as informações necessárias para implementar sua própria lógica.
Primeiro, adicione uma coluna priority no esquema:
# para o Propel
mail_message:
message: { type: clob, required: true }
created_at: ~
priority: { type: integer, default: 3 }
# para o Doctrine
MailMessage:
actAs: { Timestampable: ~ }
columns:
message: { type: clob, notnull: true }
priority: { type: integer }
Ao enviar um e-mail, defina o cabeçalho da prioridade (1 significa mais alto):
$mensagem = $this->getMailer() ->compose('john@doe.com', 'foo@example.com', 'Assunto', 'Body') ->setPriority(1) ; $this->getMailer()->send($mensagem);
Em seguida, sobrescreva o método padrão setMessage() para alterar a prioridade do
objeto MailMessage:
// para o Propel class MailMessage extends BaseMailMessage { public function setMessage($mensagem) { $msg = unserialize($mensagem); $this->setPriority($msg->getPriority()); return parent::setMessage($mensagem); } } // para o Doctrine class MailMessage extends BaseMailMessage { public function setMessage($mensagem) { $msg = unserialize($mensagem); $this->priority = $msg->getPriority(); return $this->_set('message', $mensagem); } }
Observe que a mensagem é serializada pela fila (queue), por isso tem que ser desserializada antes de obter o valor da prioridade. Agora, crie um método que ordena as mensagens por prioridade:
// para o Propel class MailMessagePeer extends BaseMailMessagePeer { static public function getSpooledMessages(Criteria $criteria) { $criteria->addAscendingOrderByColumn(self::PRIORITY); return self::doSelect($criteria); } // ... } // para o Doctrine class MailMessageTable extends Doctrine_Table { public function getSpooledMessages() { return $this->createQuery('m') ->orderBy('m.priority') ; } // ... }
O último passo é definir o método de recuperação no arquivo de configuração factories.yml
para alterar a forma padrão na qual as mensagens são obtidas
da fila (queue):
spool_arguments: [MailMessage, message, getSpooledMessages]
E isto resume todo o processo. Agora, cada vez que você executar a tarefa project:send-mails,
cada e-mail será enviado de acordo com a sua prioridade.
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.