Tutorial
Invia email con Laravel
Invia le email di Laravel tramite Emailit con il transport e la facade di emailit/emailit-laravel, o con il semplice SMTP, e verifica i webhook di Emailit.
Questa guida mostra due modi per inviare le email di Laravel tramite Emailit: il pacchetto emailit/emailit-laravel, che aggiunge un transport di posta emailit e una facade Emailit, e il semplice SMTP con il mailer integrato di Laravel. Si conclude con una route webhook che verifica le firme.
Prerequisiti
- PHP 8.1 o versioni successive e Laravel 10, 11 o 12.
- Un dominio di invio verificato, ad esempio
acme.com. - Una chiave API. Basta una chiave di solo invio limitata al tuo dominio.
- Finché il workspace non ha l’accesso alla produzione, puoi inviare solo agli indirizzi email degli account dei membri del workspace.
Opzione 1: il pacchetto Laravel di Emailit
Il pacchetto invia i Mailable esistenti, i Markdown mailable, le notifiche e le email in coda tramite l’API di Emailit, senza modifiche al codice di posta.
Installa il pacchetto
composer require emailit/emailit-laravelIl service provider viene rilevato automaticamente.
Configura il transport
In .env, aggiungi la chiave e imposta Emailit come mailer predefinito:
EMAILIT_API_KEY=secret_••••••••••••••••••••••••••••••••
MAIL_MAILER=emailit
MAIL_FROM_ADDRESS=hello@acme.com
MAIL_FROM_NAME="Acme"Registra il mailer in config/mail.php:
'mailers' => [
// ...
'emailit' => [
'transport' => 'emailit',
],
],MAIL_FROM_ADDRESS deve appartenere a un dominio di invio verificato. Per cambiare l’URL di base dell’API, pubblica il file di configurazione con php artisan vendor:publish --tag=emailit-config; per l’uso normale non è necessario.
Invia un Mailable
Crea un Mailable come al solito:
php artisan make:mail WelcomeEmailnamespace App\Mail;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class WelcomeEmail extends Mailable
{
use Queueable, SerializesModels;
public function __construct(public User $user) {}
public function envelope(): Envelope
{
return new Envelope(subject: 'Welcome to Acme');
}
public function content(): Content
{
return new Content(view: 'emails.welcome');
}
}Invialo, oppure mettilo in coda così la richiesta non attende l’API:
use App\Mail\WelcomeEmail;
use Illuminate\Support\Facades\Mail;
Mail::to($user)->send(new WelcomeEmail($user));
Mail::to($user)->queue(new WelcomeEmail($user));Usa la facade per le funzioni dell’API
La facade Emailit espone l’intero SDK PHP, per le funzioni che il mailer di Laravel non prevede, come i template salvati e gli invii programmati:
use Emailit\Laravel\Facades\Emailit;
$email = Emailit::emails()->send([
'from' => 'Acme <hello@acme.com>',
'to' => $user->email,
'template' => 'welcome',
'variables' => ['first_name' => $user->first_name],
'scheduled_at' => 'tomorrow at 9am',
]);
$email->id; // em_…La facade copre anche domini, contatti, liste, soppressioni, webhook e le altre risorse. Se preferisci la dependency injection, indica il tipo Emailit\EmailitClient in un controller o in un job e Laravel lo risolve con la chiave configurata.
Intercetta le eccezioni tipizzate per gestire gli errori:
use Emailit\Exceptions\ApiErrorException;
use Emailit\Exceptions\RateLimitException;
try {
Emailit::emails()->send($payload);
} catch (RateLimitException $e) {
// 429: release the job back to the queue and try again later
} catch (ApiErrorException $e) {
report($e); // $e->getHttpStatus() has the status code
}Opzione 2: semplice SMTP
Se preferisci non aggiungere un pacchetto, indirizza il mailer SMTP di Laravel al relay di Emailit:
MAIL_MAILER=smtp
MAIL_HOST=smtp.emailit.com
MAIL_PORT=587
MAIL_USERNAME=emailit
MAIL_PASSWORD=secret_••••••••••••••••••••••••••••••••
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=hello@acme.com
MAIL_FROM_NAME="Acme"Sulla porta 587 il mailer di Laravel aggiorna la connessione con STARTTLS. I file config/mail.php meno recenti leggono MAIL_ENCRYPTION e quelli più recenti lo ignorano, quindi puoi tenerlo senza problemi. Per il TLS implicito, usa la porta 465; se il tuo host blocca la 587, usa la 2525 o la 2587. Mailable, notifiche e code funzionano esattamente come con il pacchetto. Vedi Impostazioni SMTP.
Via SMTP non puoi usare i template salvati né scheduled_at; per queste funzioni usa la facade o l’API.
Ricevi i webhook
Crea un webhook che punta a https://your-app.com/webhooks/emailit, poi salva il suo secret di firma:
EMAILIT_WEBHOOK_SECRET=whsec_••••••••'emailit' => [
'webhook_secret' => env('EMAILIT_WEBHOOK_SECRET'),
],Aggiungi un controller che controlla la firma sul corpo grezzo:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class EmailitWebhookController extends Controller
{
public function __invoke(Request $request)
{
$payload = $request->getContent();
$signature = (string) $request->header('X-Emailit-Signature');
$timestamp = (string) $request->header('X-Emailit-Timestamp');
$expected = hash_hmac('sha256', $timestamp.'.'.$payload, config('services.emailit.webhook_secret'));
if (abs(time() - (int) $timestamp) > 300 || ! hash_equals($expected, $signature)) {
abort(401, 'Invalid signature');
}
// The body is a JSON array of up to 100 events.
foreach (json_decode($payload, true) as $event) {
match ($event['type']) {
'email.bounced', 'email.complained' => $this->stopEmailing($event['data']['object']['to']),
default => null,
};
}
return response()->noContent();
}
private function stopEmailing(string $address): void
{
// Mark the address as undeliverable in your database.
}
}Registra la route ed escludila dalla protezione CSRF, perché Emailit non può inviare un token CSRF:
use App\Http\Controllers\EmailitWebhookController;
Route::post('/webhooks/emailit', EmailitWebhookController::class);->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: ['webhooks/emailit']);
})Con Laravel 10, aggiungi invece 'webhooks/emailit' all’array $except in app/Http/Middleware/VerifyCsrfToken.php. Restituisci un 2xx entro 30 secondi e sposta il lavoro lento in una coda. Vedi Firma delle richieste.
Consigli per la produzione
- Metti in coda le email. Usa
queue()oShouldQueuecosì le richieste web non attendono le email, e limita la velocità dei job massivi (ad esempio conRedis::throttle) per restare entro i limiti di invio. Per impostazione predefinita, i nuovi workspace possono inviare 2 email al secondo. - Metti in cache la configurazione in modo sicuro. Dopo
php artisan config:cache,env()funziona solo all’interno dei file di configurazione. Leggi la chiave tramite la configurazione, come fa il pacchetto. - Usa una chiave dedicata. Dai a ogni app e a ogni ambiente la propria chiave di solo invio, così puoi ruotarne una senza toccare le altre. Vedi Chiavi API.
- Elimina i webhook duplicati. Salva ogni
event_idche elabori e salta le ripetizioni, perché le consegne non riuscite vengono ritentate.