In Laravel, what do a mailable's envelope(), content() and attachments() methods define, and how do you send that mailable to a customer?
answer
- one class per kind of email
- php artisan make:mail OrderConfirmed
- Envelope: subject, from, replyTo, tags
- Content: view, markdown, text, with
- public properties reach the view
basics
~10 sA mailable, generated by make:mail, splits one email into envelope() for subject and sender metadata, content() for the Blade or Markdown view, and attachments() for files; Mail::to($customer)->send(new OrderConfirmed($order)) delivers it.
solid answer
~40 s`php artisan make:mail OrderConfirmed` creates a class in `app/Mail` extending `Illuminate\Mail\Mailable` with three methods. `envelope()` returns an `Envelope` carrying the subject, `from`, `replyTo`, `cc`/`bcc`, tags and metadata. `content()` returns a `Content` naming a Blade `view`, a `markdown` template or a plain `text` view, plus optional `with` data; every **public property** declared on the mailable is also passed to the view automatically. `attachments()` returns an array of `Attachment` objects, such as the order's PDF receipt. You send it through the `Mail` facade: `Mail::to($order->customer)->send(new OrderConfirmed($order))`, chaining `cc()` or `bcc()` when needed. Passing a model to `to()` uses its `email` and `name` attributes, and when the envelope sets no sender the global `mail.from` address (`MAIL_FROM_ADDRESS`) is used.
code
php · 29 lines<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
class OrderConfirmed extends Mailable
{
public function __construct(public Order $order) {}
public function envelope(): Envelope
{
return new Envelope(subject: "Order #{$this->order->number} confirmed");
}
public function content(): Content
{
return new Content(markdown: 'mail.orders.confirmed');
}
public function attachments(): array
{
return [Attachment::fromStorage("receipts/{$this->order->id}.pdf")->as('receipt.pdf')];
}
}go deeper
Recall the three methods and what each returns, the make:mail command, and the Mail::to()->send() chain. Mention that public properties reach the view.
Explain how view data is gathered from public properties and with, how the global from address fills in, and why to() appending makes loop reuse dangerous.
Show you keep mailables thin and previewable: data prepared in the constructor, a local preview route, one instance per recipient, and a deliberate choice between sending now and queueing.
Frame mailables as the team's email contract: a class per message type makes copy review, localisation and provider changes cheap, while ad-hoc Mail::send calls scatter that knowledge.
## What a mailable is A **mailable** is a plain PHP class that describes one kind of email your application sends — "order confirmed", "invoice overdue", "shipment dispatched". It extends `Illuminate\Mail\Mailable`, lives in `app/Mail` by convention, and keeps everything about that email in one place: who it is from, what it says, which template renders it and which files ride along. The controller or job that sends it only decides *who* receives it and *when*. You generate one with Artisan: ```bash php artisan make:mail OrderConfirmed php artisan make:mail OrderConfirmed --markdown=mail.orders.confirmed ``` The first form writes a class whose `content()` points at a placeholder view; the second also writes a Markdown template and points `content()` at it. The generated class already uses the `Queueable` and `SerializesModels` traits, so it can be queued later without changes. ## The three methods | Method | Returns | What it defines | |---|---|---| | `envelope()` | `Illuminate\Mail\Mailables\Envelope` | subject, `from`, `replyTo`, `to`/`cc`/`bcc`, tags, metadata | | `content()` | `Illuminate\Mail\Mailables\Content` | `view` (HTML Blade view), `text` (plain-text view), `markdown` template, `htmlString`, `with` data | | `attachments()` | `array` of `Attachment` (or `Attachable` objects) | files attached to the message | An optional fourth method, `headers()`, returns a `Headers` object for a custom `Message-ID`, `References` or extra text headers. Named arguments keep these readable: `new Envelope(subject: 'Order #1042 confirmed')` and `new Content(view: 'mail.orders.confirmed')`. ## The envelope in more detail The `Envelope` is where message-level metadata lives, separate from the body: - `from:` accepts a string or an `Illuminate\Mail\Mailables\Address`, which carries a display name: `new Address('[email protected]', 'Shop Orders')`. - `replyTo:` sends customer replies to a support inbox instead of a no-reply sender. - `tags:` and `metadata:` are passed to providers that support them, so you can filter "order-confirmation" messages in the provider's dashboard or webhooks. - `to:`, `cc:` and `bcc:` can be fixed here, though recipients usually come from the call site. Keeping the subject in `envelope()` rather than in the controller means every place that sends the mailable gets the same, reviewable subject line — and it can use the mailable's own data, such as the order number. ## Getting data into the template There are two routes, and interviewers like to hear both: - **Public properties.** Every initialized public property declared on your mailable class is handed to the view under its own name. With constructor promotion, `public function __construct(public Order $order) {}` is enough for `{{ $order->number }}` to work in the template. - **The `with` argument.** `new Content(view: 'mail.orders.confirmed', with: ['total' => $this->order->formattedTotal()])` passes computed values; the property itself can stay `protected` if you do not want it exposed wholesale. The sender works the same way: if `envelope()` sets no `from`, Laravel falls back to the global `from` address in `config/mail.php`, read from `MAIL_FROM_ADDRESS` and `MAIL_FROM_NAME`. ## Sending it 1. Pick recipients with the `Mail` facade: `Mail::to($address)`. It accepts a string, an array, a model or a collection; for objects Laravel reads the `email` and `name` attributes. 2. Chain optional extras: `->cc($manager)`, `->bcc($archive)`. 3. Hand over the mailable: `->send(new OrderConfirmed($order))`. `send()` renders the view and passes the message to the configured mailer during the current request, returning an `Illuminate\Mail\SentMessage` (or `null` if a listener cancelled it). The same pending mail also offers `queue()` and `later()` for background delivery. ## A trap worth naming `to()`, `cc()` and `bcc()` **append** to the mailable's recipient lists rather than replacing them. If you build one mailable instance and reuse it in a loop — `foreach ($emails as $e) { Mail::to($e)->send($mail); }` — the second send goes to two people, the third to three, and so on. Create a fresh mailable inside the loop. ## Checking your work Because `Mailable` implements `Renderable`, you can return one from a local-only route (`return new OrderConfirmed($order);`) to see the HTML in a browser, or call `->render()` to get the string. Nothing is sent when you do this, which makes it the quickest way to iterate on layout before touching a transport. ## Summary - `make:mail` scaffolds the class; `envelope()`, `content()` and `attachments()` split metadata, body and files. - Public properties and `with` data feed the view. - `Mail::to(...)->send(...)` delivers now; the global `from` fills in a missing sender. - Never reuse one mailable instance across recipients in a loop.
- What goes wrong if you create one mailable instance before a loop and call Mail::to($email)->send($mail) for each address?`to()` appends addresses to the mailable's recipient list instead of replacing it, so every send also goes to all earlier recipients: the third customer's email is delivered to three people, which also leaks addresses. Create a new mailable inside the loop, one per recipient.
- How can you preview a mailable's HTML without sending anything?A mailable implements `Renderable`, so return it from a route (`return new OrderConfirmed($order);`), usually one registered only locally, and the browser shows the rendered HTML. Calling `->render()` returns the same HTML as a string. No mailer or transport is involved.
- Where does the From address come from when envelope() does not set one?From the global `from` entry in `config/mail.php`, which reads `MAIL_FROM_ADDRESS` and `MAIL_FROM_NAME` (the name defaults to `APP_NAME`). Setting `from:` on the `Envelope` overrides it for that mailable only.
saying these in an interview costs you the question
- The subject of a mailable is set in content() next to the view name.
- Only data passed through Content's with argument is visible in the view.
- Reusing one mailable instance in a loop sends each recipient exactly one email.
- An envelope without a from address sends the email with no sender at all.
- Mail::to() only accepts a plain email string, never a model.