Symfony Mailer transport that uses WordPress's wp_mail() function, allowing you to use Laravel's Mail facade while leveraging WordPress email plugins.
Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.
- Clean Laravel Syntax - Use
Mail::to(),Mail::send(), etc. anywhere in your code - WordPress Plugin Support - Works seamlessly with WP Mail SMTP, SendGrid, Mailgun, and other WP email plugins
- Zero Configuration - Works out of the box with sensible defaults
- Framework Agnostic - Not tied to Acorn, works across different Laravel+WordPress stacks
- Full Symfony Mailer API - Support for attachments, HTML emails, CC, BCC, custom headers
- PHP >= 8.2
- WordPress >= 6.0
- Laravel Illuminate/Support ^10.0|^11.0|^12.0
- Symfony Mailer ^6.0|^7.0
In your Laravel + WordPress project (Sage theme, WP Starter, etc.):
composer require wp-spaghetti/wp-mail-transportThe package auto-registers via service provider discovery.
Update your config/mail.php:
<?php
return [
'default' => env('MAIL_MAILER', 'wp-mail'),
'mailers' => [
'wp-mail' => [
'transport' => 'wp-mail',
],
// Keep other transports for fallback
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', '127.0.0.1'),
'port' => env('MAIL_PORT', 2525),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
],
],
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
];Update your .env:
MAIL_MAILER=wp-mailThat's it! The transport will now use WordPress's wp_mail() function.
use Illuminate\Support\Facades\Mail;
Mail::raw('Email body content', function ($message) {
$message->to('user@example.com')
->subject('Test Email');
});use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
Mail::to($user->email)->send(new WelcomeEmail($user));Mail::send('emails.welcome', ['user' => $user], function ($message) use ($user) {
$message->to($user->email)
->subject('Welcome to our application');
});Mail::send('emails.invoice', $data, function ($message) use ($user, $pdf) {
$message->to($user->email)
->subject('Your Invoice')
->attach($pdf);
});Mail::to('user1@example.com')
->cc('user2@example.com')
->bcc('admin@example.com')
->send(new OrderShipped($order));Mail::raw('Email body', function ($message) {
$message->to('user@example.com')
->subject('Test')
->getHeaders()
->addTextHeader('X-Custom-Header', 'value');
});// In app/Controllers/ContactController.php
use Illuminate\Support\Facades\Mail;
public function submit(Request $request)
{
Mail::to('info@example.com')->send(
new ContactFormSubmission($request->all())
);
return back()->with('success', 'Message sent!');
}// In your custom plugins or theme
use Illuminate\Support\Facades\Mail;
add_action('user_register', function($user_id) {
$user = get_userdata($user_id);
Mail::to($user->user_email)->send(
new WelcomeEmail($user)
);
});use Corcel\Model\User;
use Illuminate\Support\Facades\Mail;
$users = User::published()->get();
foreach ($users as $user) {
Mail::to($user->user_email)->send(
new NewsletterEmail($user)
);
}Laravel Mail::to()->send()
↓
Symfony Mailer
↓
WpMailTransport
↓
wp_mail()
↓
WordPress Email Plugins
├─ WP Mail SMTP
├─ SendGrid
├─ Mailgun
├─ Amazon SES
└─ Other plugins
↓
Email Delivery
Email Conversion:
- Converts Symfony Message to Email object
- Extracts headers, recipients, subject, body
- Handles HTML and plain text bodies
- Processes attachments
Header Handling:
- Filters out headers that
wp_mail()adds automatically (From,To,Subject,Content-Type) - Preserves custom headers (CC, BCC, Reply-To, X-* headers)
- Properly formats From header with name and address
Attachment Processing:
- Creates temporary files for attachments
- Passes file paths to
wp_mail() - Cleans up temporary files after sending
Error Handling:
- Throws
TransportExceptionif sending fails - Proper exception chaining for debugging
- Graceful cleanup on errors
The transport automatically works with popular WordPress email plugins:
Configure WP Mail SMTP in WordPress admin, then use Laravel's Mail facade normally:
Mail::to('user@example.com')->send(new OrderConfirmation($order));
// Uses WP Mail SMTP configuration automaticallyInstall and configure the SendGrid WordPress plugin:
Mail::to($user->email)->send(new WelcomeEmail($user));
// Sends through SendGrid via WordPress pluginConfigure Mailgun in WordPress, then:
Mail::to($subscribers)->send(new Newsletter($content));
// Routes through Mailgun WordPress plugin# Sage/Acorn
wp acorn vendor:publish --tag=wp-mail-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wp-mail-configThis creates config/wp-mail.php with debugging options:
<?php
return [
'debug' => env('WP_MAIL_DEBUG', false),
];Enable debug logging to troubleshoot email delivery issues:
In .env:
WP_MAIL_DEBUG=trueOr in config/wp-mail.php:
'debug' => true,What gets logged:
- Email recipients
- Subject line
- Content type (HTML or plain text)
- Number of headers
- Number of attachments
- wp_mail() success/failure status
- Exception details if sending fails
Logging System:
Debug messages are logged using Laravel's Log facade at the debug level. Configure logging in config/logging.php:
// Log to daily files
'channels' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug', // Make sure debug level is included
'days' => 14,
],
],Log Format: Laravel automatically formats logs with timestamp and context:
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Sending email via wp_mail() {"to":["user@example.com"],"subject":"Order Confirmation","content_type":"text/html","headers_count":2,"attachments_count":1}
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Email sent successfully via wp_mail()
Log Channels:
You can specify different log channels in config/logging.php:
single- Single filedaily- Daily rotating filesslack- Send to Slacksyslog- System logerrorlog- PHP error logstack- Multiple channels
Note: Debug mode logs sensitive information (email addresses). Only enable in development or when troubleshooting specific issues.
You can use multiple transports in the same application:
// config/mail.php
'mailers' => [
'wp-mail' => [
'transport' => 'wp-mail',
],
'smtp' => [
'transport' => 'smtp',
// ... SMTP config
],
],Then switch between them:
// Use WP Mail transport (default)
Mail::to($user)->send(new Welcome($user));
// Use SMTP directly
Mail::mailer('smtp')->to($admin)->send(new Alert($data));Set globally in config/mail.php:
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'noreply@example.com'),
'name' => env('MAIL_FROM_NAME', 'My Application'),
],Or per-email:
Mail::to($user)
->from('custom@example.com', 'Custom Sender')
->send(new CustomEmail());- Check if wp_mail() is working:
$result = wp_mail('test@example.com', 'Test', 'Test message');
var_dump($result); // Should be true-
Check WordPress email plugins:
- Ensure WP Mail SMTP or similar plugin is configured
- Test sending from WordPress admin
-
Enable debug mode:
WP_MAIL_DEBUG=true
LOG_LEVEL=debugWith debug mode enabled, the transport will log detailed information about each email using Laravel's logging system. Check your logs in storage/logs/laravel.log:
What gets logged:
- Recipients
- Subject
- Content type (HTML/plain text)
- Number of headers
- Number of attachments
- Success/failure status
Example debug output:
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Sending email via wp_mail() {"to":["user@example.com"],"subject":"Welcome","content_type":"text/html","headers_count":3,"attachments_count":0}
[2024-01-15 10:30:45] local.DEBUG: [WP Mail Transport] Email sent successfully via wp_mail()
Configure logging channels in config/logging.php to send logs to different destinations (files, Slack, syslog, etc.).
Ensure your WordPress upload directory is writable:
$upload_dir = wp_upload_dir();
echo $upload_dir['basedir']; // Should be writableSome WordPress email plugins may strip custom headers. Check your plugin's settings or filters.
WordPress plugins may force a specific From address. Check:
// In your WordPress code
add_filter('wp_mail_from', function($email) {
return 'your-desired@example.com';
});composer test| Feature | wp-mail-transport | roots/acorn-mail | Others |
|---|---|---|---|
| Framework Support | ✅ Acorn, WP Starter, Corcel, Custom | ❌ Acorn only | ❌ Usually Acorn only |
| WordPress Plugin Integration | ✅ Yes | ❌ No (SMTP only) | |
| Configuration Required | ✅ Minimal | ||
| Attachment Support | ✅ Yes | ✅ Yes | ✅ Yes |
| HTML Email Support | ✅ Yes | ✅ Yes | ✅ Yes |
| Maintained | ✅ Active | ✅ Active |
Please see CHANGELOG for a detailed list of changes for each release.
We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.
- Major versions (1.0.0 → 2.0.0): Breaking changes
- Minor versions (1.0.0 → 1.1.0): New features, backward compatible
- Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible
All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.
For your contributions please use:
See CONTRIBUTING for detailed guidelines.
