Laravel Cafè #3 - Creazione ed Invio di Mail

Oggi andiamo sul semplice. Filippo, il nostro dev-bartender, ci racconta come iniziare a lavorare sull'invio di email con Laravel!
francesco
Filippo Galante
09/11/2016 in Tutorial


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L'idea è di creare un punto di discussione. Se hai qualche perplessità sull'argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po' di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L'Argomento

Nel mondo del web 2.0 l'invio di mail automatizzate è parte integrante della maggioranza delle applicazioni. L'esempio più semplice è la mail di conferma che ci arriva dopo aver effettuato la registrazione ad un portale web, un forum, o a un social network, che viene inviata automaticamente dall'applicativo. Oggi, per la nostra abituale pausa caffè del mercoledì, vediamo come implementare questo servizio su Laravel 5.3.

N.B.: Laravel 5.3 ha diverse novità rispetto ai suoi predecessori e alcune di queste riguardano proprio le API per le mail, ma il codice che vi propongo è tranquillamente adattabile alle versioni antecedenti con un controller apposito.

Il Codice

Tralascerò la parte di configurazione del servizio (e vi rimando direttamente alla documentazione ufficiale) dato che può variare a seconda delle necessità del developer. Come di consueto vi propongo prima di tutto la struttura del filesystem:

app/
 |-- Http/Controllers 
     |-- MailController.php     <-- Controller per l'invio delle mail
 |-- Mail/                      <-- Cartella base dei modelli "mailable" https://laravel.com/docs/5.3/mail#generating-mailables
     |-- Contact.php            <-- Classe che si occupa del build della mail di richiesta informazioni
resources/views/                <-- Cartella viste
     |-- emails                 <-- Cartella omissibile
         |-- contact.blade.php  <-- View che fa da template per mail richiesta informazioni

Utilizzando il comando php artisan make:mail Contact Laravel ci aiuta ad automatizzare la creazione della classe che rappresenta in modo astratto quanto l'utente fanale riceverà nella propria casella di posta. A mio avviso se automatizzasse anche la creazione della vista base non sarebbe male, ma potrebbe essere uno spunto per qualche plugin o una futura feature di Laravel.

Dopo aver lanciato il comando precedentemente descritto, il risultato sarà la creazione di un'estensione della classe Illuminate\Mail\Mailable e contenente il metodo build() che ci permette di impostare sia tutte le informazioni riguardanti mittenti e destinatari, la vista da utilizzare come template ed eventualmente implementare anche code di invio.

Contact.php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class Contact extends Mailable
{

    use Queueable,
        SerializesModels;

    /**
     * Array with sender informations.
     * [ 'name' => 'John', 'mail' => 'john@doe.com' ]
     *
     * @var array
     */
    private $sender;

    /**
     * Collection with recipients informations.
     * Collection: [
     *      [ 'name' => 'stewie', 'mail' => 'stewie@griffin.com' ]
     *      [ 'name' => 'Peter', 'mail' => 'peter@griffin.com' ]
     *      [ 'name' => 'Lois', 'mail' => 'lois@griffin.com' ]
     *      [ 'name' => 'Chris', 'mail' => 'chris@griffin.com' ]
     *      [ 'name' => 'Brian', 'mail' => 'brian@griffin.com' ]
     *    // No one care about Meg
     * ]
     * @var Collection
     */
    private $recipients;

    /**
     * Mail view template.
     *
     * @var String
     */
    private $template;

    /**
     * Collection with other attributes for the view.
     *
     * @var mixex
     */
    public $attributes;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($sender, $recipients, $template, $attributes)
    {
        $this->sender = $sender;
        $this->email = $recipients;
        $this->template = $template;
        $this->attributes = $attributes;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        // Senders addresses
        $this->from($this->sender['name'], $this->sender['mail']);
        
        // Recipients addresses
        foreach($recipients as $recipient) {
            $this->to($recipient['name'], $recipient['mail']);
        }
        
        // Subject
        $this->subject("Richiesta informazioni");
                        
        // Return view
        return $this->view($this->template);
    }

}

Come potete vedere la classe contiene tre variabili private ($sender, $recipients, $template) e una variabile pubblica $attributes. Le classi Mailable hanno questa caratteristica (vedi la documentazione.

any public property defined on your mailable class will automatically be made available to the view.

Tradotto:

qualsiasi proprietà pubblica definita nella tua classe verrà resa automaticamente disponibile alla view.

Quindi, nella view descritta di seguito, avrò a disposizione un array $attributes che conterrà tutte le variabili dinamiche da passare ad un determinato template, a differenza delle variabili $sender, $recipients, $template, utilizzate solo per impostare la testata della mail. Ovviamente dato che de gustibus non est disputandum, se avete suggerimenti o volete aggiungere la vostra implementazione della classe, vi invito a continuare la discussione!

contact.blade.php

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <h4>Nuova richiesta informazioni da parte di {{ $attributes['name'] }}</h4>
        <p>{{ $attributes['name'] }} ti ha invito il seguente messaggio:</p>
        <p>{{ {{ $attributes['message'] }} }}</p>
        <h4>Ulteriori informazioni:</h4>
        <ul>
            @foreach($attributes['infos'] as $key => $info)
                <li><strong>{{ $key }}:</strong> {{ $info }}</li>
            @endforeach
        </ul>
    </body>
</html>

Il Test

La pausa caffè sta per terminare, quindi verifichiamo che tutto funzioni creando un piccolo controller, richiamato, ad esempio, da un form di richiesta informazioni.

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use Mail;
use Collection;
use App\Mail\Contact;

class MailController extends Controller
{

    public function contact(Request $request)
    {
        // validation and other stuff...
        [...]
        
        // sender
        $sender = [ 'name' => $request->name, 'mail' => $request->mail ];

        // recipients
        $recipients = new Collection;
        $recipients->push(['name' => 'Laravel Italia', 'mail' => 'info@laravel-italia.it']);
        
        // attributes
        $attributes = [
            'name' => $request->name,
            'message' => $request->message,
            'infos' => [
                'telefono' => $request->phone,
                'indirizzo' => $request->address,
                // [...]
            ]
        ];

        Mail::send(new Contact($sender, $recipients, 'mail.contact', $attributes));

        // return result
        [...]
    }

}

Questo controller si occuperà di inviare la mail, richiamando il metodo build() della classe Contact, il risultato? Beh guardate la casella di posta per scoprirlo!

... ed ora?

Cosa ne pensate di queste feature? Implementate anche voi le classi Mailable o lavorate in maniera differente?

In questo articolo ho tralasciato la parte di configurazione dei vari driver mail, l'aggiunta di allegati e l'invio tramite una coda. Se volete avere maggiori informazioni riguardo questi argomenti, vi invito a continuare la discussione in tutti i canali di Laravel Italia così da riuscire rendere più completo l'esempio proposto!

Fateci sapere cosa ne pensate, e non dimenticate che c'è sempre il nostro bellissimo Slack, pieno di gente che discute del framework più bello del mondo!