Mailer Usage
Send a basic HTML email
For most application code, compose the message fluently and call send() once:
mailer()
->setFrom('noreply@example.com', 'Example App')
->setAddress('jane@example.com', 'Jane')
->setSubject('Welcome')
->setBody('<p>Thanks for signing up.</p>')
->send();
The package does not add a text alternative automatically. Every adapter sends HTML-oriented content.
Render the body from a PHP template
Use setTemplate() when you want the body to come from a PHP file:
mailer()
->setFrom('noreply@example.com', 'Example App')
->setAddress('jane@example.com', 'Jane')
->setSubject('Reset your password')
->setTemplate(base_dir() . DS . 'shared' . DS . 'resources' . DS . 'emails' . DS . 'reset-password')
->setBody([
'name' => 'Jane',
'resetUrl' => $url,
])
->send();
The package requires reset-password.php, extracts the body array into local variables, and captures the rendered output.
Send through a specific adapter
When you need a non-default transport, pass its adapter name explicitly:
use Quantum\Mailer\Enums\MailerType;
mailer(MailerType::RESEND)
->setFrom('noreply@example.com', 'Example App')
->setAddress('team@example.com')
->setSubject('Deployment complete')
->setBody('<p>Production is healthy.</p>')
->send();
This still returns the same Mailer wrapper shape. The adapter name selects the transport behind it.
Use SMTP-only features
Reply-to, CC, BCC, and attachments exist on the SMTP adapter:
use Quantum\Mailer\Enums\MailerType;
mailer(MailerType::SMTP)
->setFrom('noreply@example.com', 'Example App')
->setAddress('jane@example.com', 'Jane')
->setCC('manager@example.com', 'Manager')
->setBCC('audit@example.com')
->setAttachment(base_dir() . DS . 'storage' . DS . 'reports' . DS . 'report.pdf')
->setSubject('Monthly report')
->setBody('<p>Attached.</p>')
->send();
For inline content from memory, use setStringAttachment($content, $filename).
If you need reply-to, the method name is setReplay() in this package.
Enable local mail trapping
When mailer.mail_trap is truthy, send() saves a .eml file to shared/emails instead of contacting the transport.
That is useful for local development and tests where you want to inspect the generated message. Create the shared/emails directory as part of project setup so each saved message has a destination.
You can then parse a saved message with MailTrap:
use Quantum\Mailer\MailTrap;
use Quantum\Di\Di;
if (!Di::isRegistered(MailTrap::class)) {
Di::register(MailTrap::class);
}
$mail = Di::get(MailTrap::class)->parseMessage($messageId);
$subject = $mail->getParsedSubject();
$body = $mail->getParsedBody();
Reuse a cached adapter safely
mailer() reuses one wrapper per adapter name, so treat each send as a full composition step:
use Quantum\Mailer\Enums\MailerType;
$mailer = mailer(MailerType::RESEND);
foreach ($recipients as $recipient) {
$mailer
->setFrom('noreply@example.com', 'Example App')
->setAddress($recipient['email'], $recipient['name'])
->setSubject('Weekly digest')
->setBody('<p>Your digest is ready.</p>')
->send();
}
This keeps the current subject and body explicit on every message, which matches the package's shared-instance lifecycle.
Practical caveats
- Build the full message before calling
send(). The shared fluent fields reset after each attempt, and composing every field on every message keeps reused adapters predictable. - Create
shared/emailsbefore enabling mail trap so saved.emlfiles have a local destination. - In mail-trap mode, a non-template array body produces an empty generated body unless the adapter supplies its own rendered MIME message.
- Transport errors are mirrored into the debugger mails tab, so debugger output is the built-in place to inspect delivery details.
- Because message IDs are cached in static state, long-running workers should treat the generated local mail-trap filename as reusable state and provide their own unique naming strategy when needed.