> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Envío de correo

> Cubrimos desde los conceptos básicos de envío con la clase Mailable de Laravel hasta correos en Markdown y envío en cola.

## Funcionalidad de correo en Laravel

La funcionalidad de correo de Laravel se construye sobre [Symfony Mailer](https://symfony.com/doc/current/mailer.html) y admite drivers como SMTP, Mailgun, Postmark, Resend, Amazon SES o sendmail.

<Info>
  El antiguo SwiftMailer usado en versiones anteriores está discontinuado. Desde Laravel 9 se utiliza Symfony Mailer.
</Info>

La configuración se gestiona en `config/mail.php`. Puedes cambiar el driver con el archivo `.env`.

```ini theme={null}
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="hello@example.com"
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`.

```shell theme={null}
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 theme={null}
<?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()`.

```php theme={null}
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;

public function envelope(): Envelope
{
    return new Envelope(
        from: new Address('shop@example.com', '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.

```php theme={null}
'from' => [
    'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
    '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.

```php theme={null}
public function content(): Content
{
    return new Content(
        view: 'mail.orders.shipped',
    );
}
```

Crea la plantilla Blade correspondiente (`resources/views/mail/orders/shipped.blade.php`).

```blade theme={null}
<!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`.

```php theme={null}
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.

```mermaid theme={null}
flowchart TD
    A["Mail::to()->send(new OrderShipped())"] --> B{"¿Implementa<br>ShouldQueue?"}
    B -- "No" --> C["Envío síncrono<br>al Mailer"]
    B -- "Sí" --> D["Se encola<br>Envío en segundo plano"]
    D --> C
    C --> E{"Driver MAIL"}
    E --> F["smtp"]
    E --> G["ses / mailgun<br>/ postmark"]
    E --> H["log<br>(desarrollo)"]
```

```php theme={null}
<?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.

```php theme={null}
Mail::to($request->user())
    ->cc($ccUsers)
    ->bcc($bccUsers)
    ->send(new OrderShipped($order));
```

Para enviar con un mailer concreto, usa `mailer()`.

```php theme={null}
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.

```php theme={null}
Mail::to($request->user())->queue(new OrderShipped($order));
```

<Warning>
  Para usar la cola necesitas configurarla previamente y tener workers corriendo.
</Warning>

También puedes retrasar el envío.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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.

```shell theme={null}
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
```

Usa el parámetro `markdown` en `content()`.

```php theme={null}
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.

```blade theme={null}
<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                                                     |

<Tip>
  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.
</Tip>

## 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.

```php theme={null}
// 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](https://github.com/axllent/mailpit).

```ini theme={null}
# .env (Mailpit)
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
```

`Laragon` o `Laravel Herd` incluyen Mailpit por defecto.

<Info>
  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.
</Info>

```ini theme={null}
MAIL_MAILER=log
```

## 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()`               |


## Related topics

- [Verificación de correo electrónico (Email Verification)](/es/verification.md)
- [Construir una SPA con Inertia.js](/es/blog/inertia-introduction.md)
- [Restablecimiento de contraseña](/es/passwords.md)
- [Colas y jobs](/es/queues.md)
- [Laravel Console Starter](/es/packages/laravel-console-starter/index.md)
