Giorno 16: Inviare ~email|Email~

Ieri abbiamo aggiunto un web service di sola lettura a Jobeet. Gli affiliati ora possono creare un account, ma quest'ultimo ha bisogno di essere attivato dall'amministratore prima di poter essere utilizzato. Per far si che l'affiliato ottenga il suo token, abbiamo ancora bisogno di implementare la notifica via ~email~. Questo è quello che inizieremo a fare oggi.

Il framework symfony comprende una delle migliori soluzioni PHP per le email: SwiftMailer. Naturalmente la libreria è pienamente integrata con symfony e comprende alcune interessanti caratteristiche aggiunte sopra a quelle standard.

Symfony 1.3/1.4 usa la versione 4.1 di ~Swift Mailer~.

Invio semplice delle email

Iniziamo inviando una semplice mail per notificare all'affiliato quando il suo account è stato confermato e per dargli il token dell'affiliato.

Modifichiamo l'azione activate con il seguente codice:

// apps/backend/modules/affiliate/actions/actions.class.php
class affiliateActions extends autoAffiliateActions
{
  public function executeListActivate()
  {
    $affiliate = $this->getRoute()->getObject();
    $affiliate->activate();
 
    // invio di un'email all'affiliato
    $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');
  }
 
  // ...
}
 

Per far funzionare il codice, occorre cambiare [email protected] in un indirizzo email esistente.

La gestione delle email in symfony è incentrata intorno a un oggetto mailer, che può essere recuperato da una azione con il metodo ~getMailer()~.

Il metodo ~compose()~ acceetta quattro parametri e restituisce un oggetto messaggio email:

  • l'indirizzo email del mittente (from);
  • l'indirizzo email del destinatario o dei destinatari (to);
  • l'oggetto del messaggio;
  • il corpo del messaggio.

L'invio del messaggio è semplice come chiamare il metodo send() sull'istanza di mailer, passando il messaggio come parametro. Come scorciatoia, si può comporre e inviare una mail in un unico passo, utilizzando il metodo ~composeAndSend()~.

Il messaggio email è una istanza della classe Swift_Message. Fare riferimento alla documentazione ufficiale di Swift Mailer per imparare di più su questo oggetto e per fare cose più avanzate, come ad esempio allegare un file.

Configurazione

Per impostazione predefinita, il metodo send() prova a usare un server locale SMTP per inviare il messaggio al destinatario. Naturalmente, come molte altre cose in symfony, questo è completamente configurabile.

~Factories|Factory~

Nei giorni precedenti, abbiamo già parlato degli oggetti principali di symfony come user, request, response, o il routing. Questi oggetti sono creati, configurati e gestiti automaticamente dal framework symfony. Inoltre sono sempre accessibili dall'oggetto ~sfContext~ e, come molte cose nel framework, sono configurabili attraverso un file di configurazione: ~factories.yml~. Questo file è configurabile per l'ambiente.

Quando sfContext inizializza i factory principali, legge il file factories.yml per i nomi della classe (class) e i parametri (param) da passare al costruttore:

---
response:
  class: sfWebResponse
  param:
    send_http_headers: false

In questo frammento di codice, per creare il factory di risposta, symfony istanzia un oggetto sfWebResponse e passa l'opzione send_http_headers come parametro.

~Strategia di consegna~

Come molti altri oggetti del core di symfony, il mailer è un factory. Quindi è configurato 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 si crea una nuova applicazione, il file di configurazione locale factories.yml sovrascrive la configurazione predefinita con alcuni ragionevoli valori predefiniti per gli ambienti env e test:

---
test:
  mailer:
    param:
      delivery_strategy: none

dev:
  mailer:
    param:
      delivery_strategy: none

L'impostazione delivery_strategy dice a symfony come inviare le email. Per impostazione predefinita, symfony ha quattro differenti strategie:

  • realtime: I messaggi sono inviati in tempo reale.
  • single_address: I messaggi sono inviati a un unico indirizzo.
  • spool: I messaggi sono memorizzati in una coda.
  • none: I messaggi sono semplicemente ignorati.

Qualunque sia la strategia, le email sono sempre salvate in un log e disponibili nel pannello "mailer" della web debug toolbar.

~Trasporto della mail~

I messaggi mail vengono inviati effettivamente da un mezzo di trasporto. Il mezzo è configurato nel file di configurazione factories.yml e la configurazione predefinita utilizza il server SMTP della macchina locale:

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

Swift Mailer è fornito con tre differenti classi per il trasporto:

  • ~Swift_SmtpTransport~: Usa un server SMTP per l'invio dei messaggi.

  • ~Swift_SendmailTransport~: Usa sendmail per l'invio dei messaggi.

  • ~Swift_MailTransport~: Usa la funzione PHP nativa mail() per l'invio dei messaggi.

La sezione "Tipi di trasporto" della documentazione ufficiale di Swift Mailer descrive tutto quello che c'è da sapere sulle classi e i loro diversi parametri.

Testare le email

Ora che abbiamo visto come inviare una email con il mailer di symfony, scriviamo alcuni test funzionali per assicurarci di aver fatto bene le cose. Per impostazione predefinita, symfony registra un tester mailer (~sfMailerTester~), che facilita il test delle mail nei test funzionali.

Innanzitutto, sostituire la configurazione del factory mailer per l'ambiente test, se il proprio server web non disponde in un server locale SMTP. Occorre sostituire la classe Swift_SmtpTransport con Swift_MailTransport:

---
# apps/backend/config/factories.yml
test:

  # ...

  mailer:
    param:
      delivery_strategy: none
      transport:
        class:  Swift_MailTransport

Poi, aggiungere un nuovo file test/fixtures/administrators.yml, con le seguenti definizioni YAML:

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

Infine, sostituire il file del test funzionale affiliate per l'applicazione di backend con il seguente codice:

// test/functional/backend/affiliateActionsTest.php
include(dirname(__FILE__).'/../../bootstrap/functional.php');
 
$browser = new JobeetTestFunctional(new sfBrowser());
$browser->loadData();
 
$browser->
  info('1 - When validating an affiliate, an email must be sent with its token')->
 
  get('/affiliate/new')->
  click('activate', array(), array('position' => 1))->
  with('mailer')->begin()->
    checkHeader('Subject', '/Jobeet affiliate token/')->
    checkBody('/Your token is symfony/')->
  end()
;
 

Ogni email inviata può essere testata con l'aiuto dei metodi ~checkHeader()~ e ~checkBody()~. Il secondo parametro di checkHeader() e il primo parametro di checkBody() possono essere uno dei seguenti:

  • una stringa su cui verificare una corrispondenza esatta
  • un'espressione regolare su cui verificare il valore
  • un'espressione regolare negata (un'espressione regolare che inizia con un !) per verificare che il valore non corrisponda.

Per impostazione predefinita, i controlli vengono effettuati sulla prima e-mail inviata. Se sono state inviate varie email, è possibile scegliere quella che si vuole testare con il metodo ~withMessage()~. withMessage() accetta un destinatario come primo parametro. Accetta anche un secondo parametro per indicare quale email si vuole testare, se allo stesso destinatario ne sono state inviate più di una.

Come altri tester disponibili, è possibile vedere il messaggio sorgente, chiamando il metodo debug().

Ci vediamo domani

Domani implementeremo l'ultima caratteristica mancante al sito web di Jobeet, il motore di ricerca.

ORM