Method: sendEmail
A robust wrapper for PHPMailer that handles HTML content, attachments, and SMTP configurations seamlessly. Ensure high deliverability for your transactional emails.
SMTP Configuration
Configure these environment variables in your .env file to enable email transport.
| Variable | Description |
|---|---|
| SMTP_HOST | The SMTP server address (e.g., smtp.gmail.com). |
| SMTP_USERNAME | Your SMTP username / email address. |
| SMTP_PASSWORD | App password or SMTP password. |
| SMTP_PORT | Port (typically 587 for TLS or 465 for SSL). |
| MAIL_FROM | Default sender email address. |
Parameters
-
recipientEmail
Target email address string.
-
subject
Email subject line.
-
body
HTML content string. Can include inline CSS.
Return Value
Boolean
Returns true on success.
Throws an Exception on failure containing the PHPMailer error info.
Example Implementation
This example demonstrates using the improved Mailer class with its fluent API within a route handler.
<?php
use PP\Attributes\Exposed;
use PP\PHPMailer\Mailer;
use PP\Rule;
use PP\Validator;
#[Exposed(limits: '5/min')]
function sendEmail($data)
{
$name = Validator::string($data->name ?? '');
$email = Validator::email($data->email ?? '');
$subject = Validator::string($data->subject ?? '');
$message = Validator::string($data->message ?? '');
$errors = [];
if (Validator::withRules($name, Rule::required()->min(2)) !== true) {
$errors['name'] = 'Please tell us your name.';
}
if ($email === null) {
$errors['email'] = 'A valid email address is required.';
}
if (Validator::withRules($message, Rule::required()->min(10)) !== true) {
$errors['message'] = 'Please write at least 10 characters.';
}
// Routine validation feedback is data, not an exception.
if ($errors !== []) {
return ['success' => false, 'errors' => $errors];
}
$body = <<<HTML
<h1>New message from {$name}</h1>
<p>Email: {$email}</p>
<p>Message: {$message}</p>
HTML;
(new Mailer())
->to($email, $name)
->subject($subject !== '' ? $subject : 'Contact form')
->html($body)
->send();
return ['success' => true, 'errors' => []];
}
?>
<div class="w-full max-w-md mx-auto p-6">
<form class="space-y-4" onsubmit="submitForm(event)">
<h1 class="text-2xl font-bold">Contact Us</h1>
<p hidden="{!status}" class="{success ? 'text-chart-2' : 'text-destructive'}">{status}</p>
<input name="name" placeholder="Name"
value="{form.name}" oninput="setField('name', event.target.value)" />
<p hidden="{!errors.name}" class="text-sm text-destructive">{errors.name}</p>
<input name="email" placeholder="Email"
value="{form.email}" oninput="setField('email', event.target.value)" />
<p hidden="{!errors.email}" class="text-sm text-destructive">{errors.email}</p>
<input name="subject" placeholder="Subject"
value="{form.subject}" oninput="setField('subject', event.target.value)" />
<textarea name="message" oninput="setField('message', event.target.value)">{form.message}</textarea>
<p hidden="{!errors.message}" class="text-sm text-destructive">{errors.message}</p>
<button type="submit" disabled="{sending}">
{sending ? 'Sending...' : 'Send Email'}
</button>
</form>
<script>
const blank = { name: '', email: '', subject: '', message: '' };
const [form, setForm] = pp.state(blank);
const [errors, setErrors] = pp.state({});
const [status, setStatus] = pp.state('');
const [success, setSuccess] = pp.state(false);
const [sending, setSending] = pp.state(false);
function setField(key, value) {
setForm({ ...form, [key]: value });
}
async function submitForm(event) {
event.preventDefault();
setSending(true);
try {
const response = await pp.rpc('sendEmail', form);
setErrors(response.errors ?? {});
setSuccess(response.success);
setStatus(response.success ? 'Email sent successfully.' : '');
if (response.success) setForm(blank);
} catch (error) {
setSuccess(false);
setStatus(`Failed to send: ${error.message}`);
} finally {
setSending(false);
}
}
</script>
</div>
Loading state
Hold the pending flag in pp.state and clear it in a
finally block, so the button re-enables even when the call rejects.
Binding disabled to that flag keeps the form honest without any extra
directive. See pp.rpc.