spatie/laravel-database-mail-templates
Render Laravel mailables using email templates stored in your database. Map templates to mailable classes, use variables like {{ name }}, and optionally wrap HTML in a custom layout. Update subjects and content without redeploying.
Installation:
composer require spatie/laravel-database-mail-templates
Publish the migration and config:
php artisan vendor:publish --provider="Spatie\DatabaseMailTemplatesServiceProvider"
php artisan migrate
Define a Template: Create a template via Tinker or a seeder:
use Spatie\DatabaseMailTemplates\Models\MailTemplate;
MailTemplate::create([
'name' => 'Welcome Email',
'subject' => 'Welcome to {{ app_name }}',
'body' => '<h1>Hello {{ user_name }}!</h1><p>Welcome to our platform.</p>',
'markdown_body' => null, // Optional
'html_body' => null, // Optional
'from_address' => 'welcome@example.com',
'from_name' => 'Support Team',
'cc' => null,
'bcc' => null,
'reply_to' => null,
'template_data' => json_encode(['app_name' => 'MyApp', 'user_name' => '{{ user }}']),
]);
First Use Case: Send an email dynamically:
use Spatie\DatabaseMailTemplates\Facades\MailTemplate;
$template = MailTemplate::where('name', 'Welcome Email')->first();
$data = ['user' => 'John Doe'];
MailTemplate::send($template, $data, 'user@example.com');
Dynamic Email Templates:
{{ user_name }}).$template = MailTemplate::find(1);
MailTemplate::send($template, ['user_name' => 'Alice'], 'alice@example.com');
Multi-Format Support:
html_body or markdown_body for formatted emails:
$template->markdown_body = 'Welcome {{ user }}!';
$template->html_body = '<h1>Welcome {{ user }}!</h1>';
Template Inheritance:
base.html stored in the DB) by merging with dynamic data:
$baseTemplate = MailTemplate::where('name', 'Base Template')->first();
$dynamicData = ['user' => 'Bob'];
MailTemplate::send($baseTemplate, $dynamicData, 'bob@example.com');
Queueing Emails:
MailTemplate::queue($template, $data, $recipient);
Localization:
subject_en, subject_es) and switch dynamically:
app()->setLocale('es');
$template->subject; // Returns translated subject
Validation:
Use Laravel’s validation to ensure template_data is valid JSON before sending:
$template->template_data = json_encode(['valid' => true]);
Events: Trigger events for template updates or sends:
MailTemplate::sent(function ($template, $data, $recipient) {
// Log or analyze sent emails
});
APIs: Expose templates via API for frontend management:
Route::get('/templates', function () {
return MailTemplate::all();
});
Testing: Mock templates in tests:
$template = MailTemplate::factory()->create();
$this->actingAs($user)->post('/send-email', ['template_id' => $template->id]);
Placeholder Conflicts:
template_data keys and Laravel’s reserved variables (e.g., {{ app }}).{{ user_data.name }}).JSON Serialization:
template_data must be valid JSON. Invalid data breaks rendering.$data = ['key' => 'value'];
if (json_encode($data) === false) {
throw new \InvalidArgumentException('Invalid template data');
}
Caching:
$template = Cache::remember("template_{$id}", now()->addHours(1), function () use ($id) {
return MailTemplate::find($id);
});
Markdown vs. HTML:
markdown_body or html_body is used. Set both if supporting both formats.MailTemplate::render() to preview:
$rendered = $template->render(['user' => 'Alice']);
Queue Failures:
php artisan queue:work --sleep=3 --tries=3
Log Rendered Emails: Add a listener to log rendered content:
MailTemplate::sent(function ($template, $data, $recipient) {
\Log::debug('Sent email to ' . $recipient, [
'template' => $template->name,
'data' => $data,
]);
});
Template Preview:
Use MailTemplate::render() to debug placeholders:
$rendered = $template->render(['user' => 'Alice']);
dd($rendered); // Inspect output
Custom Renderers:
Extend the Spatie\DatabaseMailTemplates\MailTemplate model to add custom logic:
namespace App\Models;
use Spatie\DatabaseMailTemplates\Models\MailTemplate as BaseMailTemplate;
class MailTemplate extends BaseMailTemplate {
public function render(array $data): string {
$content = parent::render($data);
// Add custom processing
return str_replace('{{ custom_tag }}', 'Replaced!', $content);
}
}
Dynamic Attachments:
Override the send method to add attachments dynamically:
MailTemplate::extend(function ($sender) {
$sender->afterSend(function ($template, $data, $recipient) {
if (isset($data['attach_file'])) {
\Mail::send($template, $data, function ($message) use ($data) {
$message->attach($data['attach_file']);
});
}
});
});
Template Versioning:
Add a version column to track template changes and rollback if needed:
$template->increment('version');
Webhooks: Trigger external actions (e.g., Slack notifications) when templates are sent:
MailTemplate::sent(function ($template, $data, $recipient) {
Http::post('https://slack.com/api/chat.postMessage', [
'text' => "Email sent to {$recipient} via template {$template->name}",
]);
});
How can I help you explore Laravel packages today?