Funcionalidad de correo en Laravel
La funcionalidad de correo de Laravel se construye sobre Symfony Mailer y admite drivers como SMTP, Mailgun, Postmark, Resend, Amazon SES o sendmail.
El antiguo SwiftMailer usado en versiones anteriores está discontinuado. Desde Laravel 9 se utiliza Symfony Mailer.
La configuración se gestiona en config/mail.php. Puedes cambiar el driver con el archivo .env.
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"
Crear una clase Mailable
En Laravel, cada correo que envía la aplicación se representa como una clase “Mailable”. Genera la clase con el comando Artisan make:mail.
php artisan make:mail OrderShipped
La clase se genera en el directorio app/Mail/.
Configuración del Mailable
La clase Mailable generada tiene tres métodos: envelope(), content() y attachments().
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
public function __construct(
public Order $order,
) {}
public function envelope(): Envelope
{
return new Envelope(
subject: 'Tu pedido ha sido enviado',
);
}
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
public function attachments(): array
{
return [];
}
}
Configurar el remitente
Indica el remitente en envelope().
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
public function envelope(): Envelope
{
return new Envelope(
from: new Address('[email protected]', 'Shop Support'),
subject: 'Tu pedido ha sido enviado',
);
}
Si configuras un remitente global en config/mail.php, puedes omitir la indicación en cada Mailable.
'from' => [
'address' => env('MAIL_FROM_ADDRESS', '[email protected]'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
Configurar el cuerpo del correo
Con content() indicas la plantilla Blade. Los datos que asignes a propiedades public en el constructor estarán disponibles automáticamente en la plantilla.
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
Crea la plantilla Blade correspondiente (resources/views/mail/orders/shipped.blade.php).
<!DOCTYPE html>
<html>
<body>
<h1>Tu pedido ha sido enviado</h1>
<p>Nº de pedido: {{ $order->id }}</p>
<p>Total: {{ number_format($order->total) }} €</p>
<p>Gracias por tu pedido.</p>
</body>
</html>
También puedes pasar los datos explícitamente con with. En ese caso, marca las propiedades como protected o private.
public function __construct(
protected Order $order,
) {}
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
with: [
'orderId' => $this->order->id,
'total' => $this->order->total,
],
);
}
Envío del correo
Con to() de la fachada Mail indicas el destinatario y con send() envías el correo.
<?php
namespace App\Http\Controllers;
use App\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderShipmentController extends Controller
{
public function store(Request $request): RedirectResponse
{
$order = Order::findOrFail($request->order_id);
Mail::to($request->user())->send(new OrderShipped($order));
return redirect('/orders');
}
}
También puedes indicar CC y BCC.
Mail::to($request->user())
->cc($ccUsers)
->bcc($bccUsers)
->send(new OrderShipped($order));
Para enviar con un mailer concreto, usa mailer().
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
Envío con cola
Como el envío de correo afecta al tiempo de respuesta, se recomienda enviarlo en segundo plano con una cola.
Mail::to($request->user())->queue(new OrderShipped($order));
Para usar la cola necesitas configurarla previamente y tener workers corriendo.
También puedes retrasar el envío.
Mail::to($request->user())
->later(now()->plus(minutes: 10), new OrderShipped($order));
Implementando ShouldQueue en la clase Mailable, incluso al llamar a send() se encola siempre.
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
Indicar la conexión y el nombre de la cola con atributos PHP
Con atributos de PHP puedes indicar directamente en la clase Mailable la conexión y el nombre de la cola. En lugar de encadenar on()->queue(), lo configuras de forma declarativa a nivel de clase.
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;
#[Connection('sqs')]
#[Queue('emails')]
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
De esta forma, dejas claro en la definición de la clase que este Mailable siempre se envía a la cola emails de la conexión SQS.
Crear un correo con Markdown
Los correos en Markdown te permiten aprovechar las plantillas HTML responsivas y elegantes que ofrece Laravel.
Generar un Mailable con Markdown
Añade la opción --markdown al generar el Mailable.
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
Usa el parámetro markdown en content().
use Illuminate\Mail\Mailables\Content;
public function content(): Content
{
return new Content(
markdown: 'mail.orders.shipped',
with: [
'url' => route('orders.show', $this->order),
],
);
}
Escritura de la plantilla Markdown
Crea el cuerpo del correo con los componentes Blade que ofrece Laravel.
<x-mail::message>
# Tu pedido ha sido enviado
Te informamos de que tu pedido ha sido enviado.
<x-mail::button :url="$url">
Ver pedido
</x-mail::button>
Gracias por usar nuestros servicios.<br>
{{ config('app.name') }}
</x-mail::message>
Los componentes disponibles son:
| Componente | Descripción |
|---|
<x-mail::button> | Botón de enlace (se puede indicar color con primary, success o error) |
<x-mail::panel> | Área en panel destacado |
<x-mail::table> | Tabla en formato Markdown |
Los correos en Markdown generan automáticamente también una versión en texto plano. Se pueden leer incluso con clientes que no soporten HTML.
Previsualización del correo
Si devuelves una instancia de Mailable desde una ruta, puedes previsualizar el correo en el navegador. Muy útil durante el desarrollo.
// routes/web.php
Route::get('/mailable', function () {
$order = App\Models\Order::first();
return new App\Mail\OrderShipped($order);
});
Correos en desarrollo local
Para evitar envíos accidentales en producción, en desarrollo conviene usar herramientas como Mailpit.
# .env (Mailpit)
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
Laragon o Laravel Herd incluyen Mailpit por defecto.
Con el driver log, el contenido del correo se registra en el archivo de log. Es la opción más sencilla si solo quieres comprobar el envío.
Resumen
| Objetivo | Método |
|---|
| Crear un Mailable | php artisan make:mail ClassName |
| Configurar el remitente | Parámetro from en envelope() |
| Usar una plantilla Blade | Parámetro view en content() |
| Usar una plantilla Markdown | Parámetro markdown en content() |
| Enviar un correo | Mail::to()->send() |
| Enviar con cola | Mail::to()->queue() |
| Envío diferido | Mail::to()->later() |