di Fabien Potencier
L'invio di ~email~ con symfony è facile e potente, grazie all'uso della libreria SwiftMailer. Nonostante ~SwiftMailer~ renda semplice l'invio di email, symfony aggiungendo un piccolo wrapper sulla libreria ne semplifica e potenzia le funzionalità. Questo capitolo spiegherà tutta la potenza messa a disposizione dal framework.
symfony 1.3 include la versione 4.1 di Swift Mailer.
Introduzione
La gestione delle email in symfony è incentrata sull'oggetto mailer. E come per molti altri oggetti del core di symfony, il mailer è una factory. Questo è inizializzato nel file di configurazione factories.yml
ed è sempre disponibile tramite le context instance:
$mailer = sfContext::getInstance()->getMailer();
Diversamente da altre factory, il mailer è inizializzato e caricato solo su richiesta. Quindi se non usato non inficierà le performance in nessun modo.
Questo tutorial spiega l'integrazione di SwiftMailer in symfony. Se si vuole conoscere ogni singolo dettaglio della libreria, si consiglia di fare riferimento alla documentazione ufficiale.
Inviare un'email da un'azione
Recuperare, in un'azione, una istanza dell'oggetto mailer è facile grazie al metodo di scorciatoia getMailer()
:
$mailer = $this->getMailer();
Il modo più veloce
Inviare un'email è reso semplice dall'uso del metodo ~sfAction::composeAndSend()
~:
$this->getMailer()->composeAndSend( '[email protected]', '[email protected]', 'Subject', 'Body' );
Il metodo composeAndSend()
accetta i seguenti parametri:
- l'indirizzo email del mittente (
from
); - il contenitore degli indizzi di destinazione (
to
); - il soggetto del messaggio;
- il corpo del messaggio.
Ogni volta che un metodo accetta un indirizzo email come parametro, allora lo accetterà nella forma di stringa o array:
$address = '[email protected]'; $address = array('[email protected]' => 'Fabien Potencier');
Inoltre, si può inviare un array di indirizzi email come secondo parametro del metodo, per inviare l'email a più destinatari contemporaneamente;
$to = array( '[email protected]', '[email protected]', ); $this->getMailer()->composeAndSend('[email protected]', $to, 'Subject', 'Body'); $to = array( '[email protected]' => 'Mr Foo', '[email protected]' => 'Miss Bar', ); $this->getMailer()->composeAndSend('[email protected]', $to, 'Subject', 'Body');
La via flessibile
Se si cerca la flessibilità, si può usare anche il metodo ~sfAction::compose()
~ per creare un messaggio, personalizzarlo ed eventualmente spedirlo. Questo metodo è molto utile quando è necessario aggiungere un ~allegato|email attachment~ così come mostrato nel codice successivo:
// crea un oggetto messaggio $messaggio = $this->getMailer() ->compose('[email protected]', '[email protected]', 'Subject', 'Body') ->attach(Swift_Attachment::fromPath('/path/to/a/file.zip')) ; // invio del messaggio $this->getMailer()->send($messaggio);
La via espressiva
È anche possibile creare un oggetto message direttamente dalla classe sfMailerMessage per ottenere ulteriore flessibilità:
$messaggio = sfMailerMessage::newInstance() ->setFrom('[email protected]') ->setTo('[email protected]') ->setSubject('Subject') ->setBody('Body') ->attach(Swift_Attachment::fromPath('/path/to/a/file.zip')) ; $this->getMailer()->send($messaggio);
Le sezioni "Creating Messages" e "Message Headers" della documentazione ufficiale di SwiftMailer descrivono tutto quello che è necessario sapere per la creazione dei messaggi.
Usare le viste di Symfony
Inviare le email dalle azioni permetterà di sfruttare la flessibilità dei partial e dei component abbastanza facilmente.
$message->setBody($this->getPartial('partial_name', $arguments));
Configurazione
Come qualsiasi altro factory di symfony, la classe mailer può essere configurata nel file di configurazione factories.yml
. La configurazione predefinita è la seguente:
---
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:
Quando viene creata una nuova applicazione, il file di configurazione factories.yml
locale sovrascrive quello predefinito modificando alcune variabili associate agli ambienti predefiniti prod
, env
e test
:
---
test:
mailer:
param:
delivery_strategy: none
dev:
mailer:
param:
delivery_strategy: none
Le strategie di spedizione
Una delle funzionalità più utili derivate dall'integrazione di SwiftMailer in symfony è la strategia di spedizione. La strategia di spedizione permette di specificare a symfony come inviare le email ed è configurata tramite il parametro ~delivery_strategy
~ del file factories.yml
. La strategia permette di cambiare il modo con il quale il metodo ~send()
|sfMailer::send()
~ agisce. Le strategie predefinite disponibili sono quattro e possono sopperire a tutte le comuni necessità:
realtime
: I messaggi sono spediti in tempo reale.single_address
: I messaggi sono spediti a un singolo indirizzo di posta elettronica.spool
: I messaggi sono salvati in una lista.none
: I messaggi sono semplicemente ignorati.
La strategia ~realtime
~
La strategia realtime
è quella predefinita ed è la più semplice da configurare, in quanto non c'è nulla da fare.
I messaggi di posta elettronica sono inviati tramite il transport configurato nella sezione transport
del file di configurazione factories.yml
(guardare la prossima sezione per maggiori informazioni su come configurare il transport dell'email).
La strategia di ~single_address
~
Con la strategia di single_address
, tutti i messaggi sono inviati a un unico indirizzo di posta elettronica configurato tramite il parametro delivery_address
.
Questa strategia è molto comoda nell'ambiente di sviluppo per evitare di inviare messaggi a utenti reali, ma permette a uno sviluppatore di controllare comunque come viene visualizzata in un client di posta elettronica.
Se bisogna verificare i campi originali
to
,cc
ebcc
, saranno reperibili rispettivamente come valori dei seguenti header:X-Swift-To
,X-Swift-Cc
eX-Swift-Bcc
.
I messaggi email sono inviati tramite lo stesso transport utilizzato per la strategia realtime
.
La strategia ~spool
~
Nella strategia spool
, i messaggi sono salvati in una coda.
Questa è la migliore strategia per l'ambiente di produzione, in quanto le richieste web non devono aspettare affinché tutte le email siano inviate.
La classe di spool
è configurata tramite il parametro ~spool_class
~. Symfony espone tre classi predefinite:
-
~
Swift_FileSpool
~: I messaggi sono salvati sul filesystem. -
~
Swift_DoctrineSpool
~: I messaggi sono salvati in un modello Doctrine. -
~
Swift_PropelSpool
~: I messaggi sono salvate in un modello Propel.
Quando lo spool è istanziato, il parametro ~spool_arguments
~ è utilizzato come costruttore dei parametri. A seguire le opzioni disponibili per le classi predefinite per creare code:
-
Swift_FileSpool
:- Il percorso assoluto della cartella delle code (i messaggi sono salvati in questa cartella)
-
Swift_DoctrineSpool
:-
Il modello Doctrine da usare per salvare i messaggi (Il valore predefinito è
MailMessage
) -
Il nome della colonna da usare per salvare il messaggio (Il valore predefinito è
message
) -
Il nome del metodo da chiamare per recuperare i messaggi da spedire (opzionale). Riceve la coda di opzioni come parametro.
-
-
Swift_PropelSpool
:-
Il modello Propel da usare per salvare i messaggi (Il valore predefinito è
MailMessage
) -
Il nome della colonna da usare per salvare il messaggio (Il valore predefinito è
message
) -
Il nome del metodo da chiamare per recuperare i messaggi da spedire (opzionale). Riceve la coda di opzioni come parametro.
-
Qui di seguito una configurazione di esempio per uno spool con Doctrine:
#---
# configurazione del file factories.yml
mailer:
class: sfMailer
param:
delivery_strategy: spool
spool_class: Swift_DoctrineSpool
spool_arguments: [ MailMessage, message, getSpooledMessages ]
E la stessa configurazione per lo spool con Propel:
#---
# configurazione del file factories.yml
dev:
mailer:
param:
delivery_strategy: spool
spool_class: Swift_PropelSpool
spool_arguments: [ MailMessage, message, getSpooledMessages ]
Per inviare un messaggio salvato in coda bisogna usare il task ~project:send-emails
~ (da notare che questo task è totalmente indipendente dal tipo di coda utilizzata e dalle opzioni scelte):
$ php symfony project:send-emails
Il task
project:send-emails
accetta come parametri l'application
e l'env
.
Quando viene invocato il task project:send-emails
, le email sono inviate con lo stesso transport usato per la stategia realtime
.
Notare che il task
project:send-emails
può essere lanciato su qualsiasi computer, non necessariamente sulla macchina che ha creato il messaggio. Il tutto funziona perché ogni cosa è memorizzata nell'oggetto del messaggio, anche i file in allegato.
L'implementazione predefinita della gestione della coda è molto semplice. Invia le email senza gestione degli errori, così come succederebbe nella strategia
realtime
. Ovviamente, le classi predefinite di gestione delle code possono essere estese per implementare logiche personalizzate e gestioni degli errori.
Il task project:send-emails
accetta due parametri opzionali:
-
message-limit
: Limita il numero dei messaggi da spedire -
time-limit
: Limita il tempo necessario a spedire i messaggi (in secondi).
Entrambi i parametri possono essere usati insieme:
$ php symfony project:send-emails --message-limit=10 --time-limit=20
Il comando precedente bloccherà l'invio dei messaggio quando saranno spediti 10 messaggi o saranno passati 20 secondi.
Usando la strategia spool
potrebbe essere necessario inviare un messaggio immediatamente senza salvarlo il lista di attesa. Questo è possibile usando il metodo speciale sendNextImmediately()
della classe mailer:
$this->getMailer()->sendNextImmediately()->send($message);
Nel precedente esempio il $message
non sarà salvato in lista e sarà spedito immediatamente. Questo significa, che il metodo sendNextImmediately()
, è utilizzato per spedire il solo messaggio passato.
Il metodo
sendNextImmediately()
non non effetti particolari quando il metodo di spedizione non èspool
.
La strategia ~none
~
Questa strategia è utile nell'ambiente di sviluppo per evitare l'invio di email a veri utenti. I messaggi sono disponibili all'interno della web debug toolbar (maggiori informazioni nella sezione succesiva riguardante il pannello di gestione del mailer nella web debug toolbar).
È anche la migliore strategia per gli altri ambienti, dove l'oggetto sfTesterMailer
permette di analizzare il messaggio senza necessariamente spedirlo (ulteriori informazioni saranno presenti nella sezione relativa ai test).
Il Mail Transport
I messaggi di posta sono spediti utilizzando un transport. Il transport è configurato nel file di configurazione factories.yml
e il valore predefinito è un server SMTP sulla macchina locale:
---
transport:
class: Swift_SmtpTransport
param:
host: localhost
port: 25
encryption:
username:
password:
Swift Mailer viene distribuito con tre differenti classi di transport:
-
~
Swift_SmtpTransport
~: Usa un server SMTP per inviare i messaggi. -
~
Swift_SendmailTransport
~: Usasendmail
per inviare i messaggi. -
~
Swift_MailTransport
~: Usa la funzione nativa di PHPmail()
per inviare i messaggi.
La sezione della documentazione ufficiale di swift Mailer sul "tipo di Transport" descrive tutto quello che c'è da sapere a proposito delle classi transport predefinite e dei rispettivi parametri.
L'invio di email da un task
L'invio di una mail da un task è molto simile all'invio di una email da una azione, in quanto il sistema del task prevede anche un metodo getMailer()
.
Quando si crea il mailer, il sistema del task si basa sulla configurazione attuale. Quindi, se si desidera utilizzare la configurazione di una specifica applicazione, è necessario aggiungere l'opzione --application
(si veda il capitolo sui task per maggior informazioni su questo argomento).
Si noti che il task utilizza la stessa configurazione dei controller. Quindi, se si desidera forzare la consegna quando è usata la strategia spool
, usare sendNextImmediately()
:
$this->getMailer()->sendNextImmediately()->send($message);
Debug
Tradizionalmente, il debug delle email è sempre stato un incubo. Con symfony, invece, è molto semplice grazie alla ~web debug toolbar~.
Direttamente dal browser è possibile, semplicemente e velocemente, vedere come i messaggi sono stati inviati dall'azione corrente:
Dopo aver cliccato sull'icona dell'email, il messaggio spedito sarà visualizzato nel pannello nel suo formato originale, così come mostrato nell'immagine qui sotto.
Ogni volta che una email viene spedita, symfony aggiunge un messaggio nel log.
Test
Sicuramente, l'integrazione non sarebbe completa senza un modo per testare le email. Symfony registra, automaticamente, un tester mailer
(~sfMailerTester
~) per testare le email nei test funzionali.
Il metodo ~hasSent()
~ controlla il numero di email inviate da una azione:
$browser-> get('/foo')-> with('mailer')-> hasSent(1) ;
Il codice precedente controlla che l'URL /foo
invii solo una email.
Ogni email inviata può essere testata ulteriormente usando i metodi ~checkHeader()
~ e ~checkBody()
~:
$browser-> get('/foo')-> with('mailer')->begin()-> hasSent(1)-> checkHeader('Subject', '/Subject/')-> checkBody('/Body/')-> end() ;
Il secondo parametro del metodo checkHeader()
e il primo parametro del metodo checkBody()
possono essere:
-
una stringa che corrisponda esattamente a quella restituita;
-
un'espressione regolare da confrontare con i risultati;
-
un'espressione regolare negativa (cioè una espressione regolare preceduta dal carattere
!
) per confermare che il risultato non corrisponda ai valori controllati.
I test, se non è stato specificato diversamente, controllano solo la prima email inviata. Se sono state inviate diverse email, allora bisognerà utilizzare il metodo ~withMessage()
~ per scegliere su quale email discriminare:
$browser-> get('/foo')-> with('mailer')->begin()-> hasSent(2)-> withMessage('[email protected]')-> checkHeader('Subject', '/Subject/')-> checkBody('/Body/')-> end() ;
Il metodo withMessage()
accetta un destinatario come primo parametro. Inoltre accetta un secondo parametro per indicare quali email controllare nel caso lo stesso destinatario ne abbia ricevute diverse.
Per finire, il metodo ~debug()
~ mostra il messaggio inviato per individuare i problemi nel caso di fallimento di un test:
$browser-> get('/foo')-> with('mailer')-> debug() ;
Messaggi come classi
Nell'introduzione di questo capitolo, abbiamo imparato come inviare email da una azione. Questo è probabilmente il modo più semplice per inviare email in una applicazione symfony e probabilmente il migliore quando si necessita di inviare alcuni semplici messaggi.
Ma quando l'applicazione necessita di gestire un gran numero di email differenti, bisogna utilizzare un approccio diverso.
Come valore aggiuntivo, usare classi per i messaggi di posta elettronica significa che, la stessa email, può essere utilizzata in diverse applicazioni; per una istanza di frontend o di backend.
Siccome i messaggi sono semplici oggetti PHP, il miglior modo di organizzarli è creando una classe per ognuno di loro:
// lib/email/ProjectConfirmationMessage.class.php class ProjectConfirmationMessage extends Swift_Message { public function __construct() { parent::__construct('Subject', 'Body'); $this ->setFrom(array('[email protected]' => 'My App Bot')) ->attach('...') ; } }
Inviare un messaggio da una azione, o da ovunque sia necessario, è semplicemente una questione di instanziare la giusta classe di messaggio:
$this->getMailer()->send(new ProjectConfirmationMessage());
Naturalmente, aggiungere una classe base dove centralizzare gli header condivisi, come il From
, o aggiungere una firma comune a tutte, è utile:
// lib/email/ProjectConfirmationMessage.class.php class ProjectConfirmationMessage extends ProjectBaseMessage { public function __construct() { parent::__construct('Subject', 'Body'); // header specifici, allegati, ... $this->attach('...'); } } // lib/email/ProjectBaseMessage.class.php class ProjectBaseMessage extends Swift_Message { public function __construct($subject, $body) { $body .= <<<EOF -- Email inviata dal Mio App Bot EOF ; parent::__construct($subject, $body); // set all shared headers $this->setFrom(array('[email protected]' => 'My App Bot')); } }
Se un messaggio dipende da qualche modello, si può ovviamente passare questi ultimi come parametri del costruttore:
// lib/email/ProjectConfirmationMessage.class.php class ProjectConfirmationMessage extends ProjectBaseMessage { public function __construct($user) { parent::__construct('Confirmation for '.$user->getName(), 'Body'); } }
Ricette
Inviare una email con ~Gmail~
Se non si ha a disposizione un server SMTP ma solo un account Gmail, è possibile usare quest'ultimo per inviare e archiviare i messaggi:
---
transport:
class: Swift_SmtpTransport
param:
host: smtp.gmail.com
port: 465
encryption: ssl
username: il_tuo_username_su_gmail_qui
password: la_tua_password_di_gmail_qui
Configurare le voci username
e password
con le credenziali di Gmail per poterne utilizzare i server.
Personalizzare l'oggetto Mailer
Se configurare il mailer tramite il file factories.yml
non è abbastanza, è necessario ascoltare l'evento ~mailer.configure
~, per poi personalizzare l'oggetto mailer.
È possibile connettersi a questo evento tramite la classe ProjectConfiguration
così come illustato qui di seguito:
class ProjectConfiguration extends sfProjectConfiguration { public function setup() { // ... $this->dispatcher->connect( 'mailer.configure', array($this, 'configureMailer') ); } public function configureMailer(sfEvent $event) { $mailer = $event->getSubject(); // fare qualcosa col mailer } }
La seguente sezione mostra un uso utile di questa tecnica.
Usare i ~Plugin di Swift Mailer~
Per usare i plugin di Swift Mailer, bisogna innanzitutto ascoltare l'evento mailer.configure
(come spiegato precedentemente):
public function configureMailer(sfEvent $event) { $mailer = $event->getSubject(); $plugin = new Swift_Plugins_ThrottlerPlugin( 100, Swift_Plugins_ThrottlerPlugin::MESSAGES_PER_MINUTE ); $mailer->registerPlugin($plugin); }
La sezione "Plugin" della documentazione ufficiale di Swift Mailer descrive tutto quello che è necessario sapere a proposito dei plugin predefiniti.
Personalizzare il comportamento di Spool
L'implementazione predefinita degli spool è molto semplice. Ogni spool prende tutte le email da una lista d'attesa in un ordine casuale e poi le spedisce.
È possibile configurare lo spool per limitare il tempo impiegato per spedire le email (in secondi), o per limitare il numero di messaggi da spedire:
$spool = $mailer->getSpool(); $spool->setMessageLimit(10); $spool->setTimeLimit(10);
In questa sezione si implementerà un sistema di priorità per le spedizioni. Questo darà le basi necessarie per implementare logiche personalizzate.
Per prima cosa, aggiungere una colonna priority
allo schema:
Durante la spedizione dell'email, si imposterà l'header di priorità (dove 1 significa massima priorità):
$message = $this->getMailer() ->compose('[email protected]', '[email protected]', 'Subject', 'Body') ->setPriority(1) ; $this->getMailer()->send($message);
Quindi, si sovrascriverà il metodo predefinito setMessage()
per cambiare la priorità dell'oggetto MailMessage
:
// per Propel class MailMessage extends BaseMailMessage { public function setMessage($message) { $msg = unserialize($message); $this->setPriority($msg->getPriority()); return parent::setMessage($message); } } // per Doctrine class MailMessage extends BaseMailMessage { public function setMessage($message) { $msg = unserialize($message); $this->priority = $msg->getPriority(); return $this->_set('message', $message); } }
Bisogna notare che il messaggio è serializzato all'interno della lista, quindi per recuperare il valore della priorità dovrà essere de-serializzato. Andrà quindi creato un metodo per ordinare i messaggi in base alle rispettive priorità:
// per Propel class MailMessagePeer extends BaseMailMessagePeer { static public function getSpooledMessages(Criteria $criteria) { $criteria->addAscendingOrderByColumn(self::PRIORITY); return self::doSelect($criteria); } // ... } // per Doctrine class MailMessageTable extends Doctrine_Table { public function getSpooledMessages() { return $this->createQuery('m') ->orderBy('m.priority') ; } // ... }
L'ultimo passo è quello di definire il metodo di recupero all'interno del file di configurazione factories.yml
per cambiare il comportamento predefinito con il quale i messaggi sono ottenuti dalla lista d'attesa:
---
spool_arguments: [ MailMessage, message, getSpooledMessages ]
A questo punto, ogni volta che sarà eseguito il task project:send-emails
, ogni email verrà spedita in base alla propria priorità.
Personalizzare lo Spool con un qualsiasi criterio
Il precedente esempio usa un header standard dei messaggi, la priorità. Ma se bisogna utilizzare un qualsiasi criterio, o se non si può modificare il messaggio inviato, si può anche salvare il criterio come header personalizzato, per poi rimuoverlo subito prima di inviare il messaggio.
Per prima cosa si aggiunge l'header personalizzato al messaggio da spedire:
public function executeIndex() { $message = $this->getMailer() ->compose('[email protected]', '[email protected]', 'Subject', 'Body') ; $message->getHeaders()->addTextHeader('X-Queue-Criteria', 'foo'); $this->getMailer()->send($message); }Poi si ottiene il valore dall'header durante la fase di salvataggio in lista d'attesa e si rimuove immediatamente:
public function setMessage($message) { $msg = unserialize($message); $headers = $msg->getHeaders(); $criteria = $headers->get('X-Queue-Criteria')->getFieldBody(); $this->setCriteria($criteria); $headers->remove('X-Queue-Criteria'); return parent::_set('message', serialize($msg)); }
インデックス
Document Index
関連ページリスト
Related Pages
Introduzione
Utilizzo avanzato delle rotte
Migliorare la propria produttività
Email
Widget e validatori personalizzati
L'utilizzo avanzato dei form
Estendere la Web Debug Toolbar
Uso avanzato di Doctrine
Sfruttare l'ereditarietà delle tabelle di Doctrine
Symfony all'interno
Windows e symfony
Sviluppare su Facebook
Sfruttare la potenza della linea di comando
Lavorare con la cache della configurazione di symfony
Lavorare con la comunità di symfony
Appendice A - codice JavaScript per sfWidgetFormGMapAddress
A proposito degli autori
Appendice B - Esempio di installazione personalizzata
Appendice C - Licenza
A proposito dei traduttori

日本語ドキュメント
Japanese Documents
2011/01/18 Chapter 17 - Extending Symfony
2011/01/18 The generator.yml Configuration File
2011/01/18 Les tâches
2011/01/18 Emails
2010/11/26 blogチュートリアル(8) ビューの作成
リリース情報
Release Information
- 2.0 : 2.0.15(2011/05/30)
Symfony2日本語ドキュメント - 1.4 : 1.4.18(2012/05/30)
Changelog

