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

Giorno 16: Inviare email

1.4 / Propel
Symfony version
1.2
Language ORM

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.

note

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');
  }
 
  // ...
}

note

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().

tip

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

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.

sidebar

La classe sfContext

L'oggetto sfContext contiene riferimenti a oggetti di symfony come request, response, user, ecc. Dal momento che sfContext agisce come un singleton, si può utilizzare sfContext::getInstance() da qualsiasi parte e quindi avere accesso a qualsiasi oggetto del nucleo di symfony:

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

Ogni volta che si vuole utilizzare sfContext::getInstance() in una delle proprie classi, sarebbe meglio pensarci due volte, perché introduce un forte accoppiamento. È quasi sempre meglio passare l'oggetto necessario come parametro.

È anche possibile utilizzare sfContext come un registro e aggiungere i propri oggetti usando i metodi set(). Accetta un nome e un oggetto come parametri e il metodo get() può essere usato in seguito per recuperare l'oggetto in base al nome:

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

Strategia di consegna

Come molti altri oggetti del nucleo 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.

tip

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.

note

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.

tip

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.