datascaled / laravel-m365-mailer
Laravel mail transport driver for Microsoft 365 / Microsoft Graph with optional GDPR-conscious logging.
v0.1.1
2026-03-31 12:20 UTC
Requires
- php: ^8.3
- illuminate/contracts: ^11.0||^12.0
- illuminate/mail: ^11.0||^12.0
- illuminate/support: ^11.0||^12.0
- microsoft/microsoft-graph: ^1.109
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^9.0||^10.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
README
A Laravel mail transport driver for Microsoft 365 / Microsoft Graph with optional GDPR-conscious database logging.
Features
- Full Laravel mailer transport (
transport: m365) for Mailables, Notifications, and queued mails. - Client-credentials flow (
tenant_id,client_id,client_secret). - Optional DB-backed message status history (
queued,sending,sent,failed). - GDPR-oriented defaults: recipient masking + hashing; plaintext recipient storage is opt-in.
- Built-in retention cleanup command:
m365-mail:prune.
Requirements
- PHP 8.3+
- Laravel 11 or 12
Installation
composer require datascaled/laravel-m365-mailer
Quick Start (No DB Logging)
- Configure an
m365mailer inconfig/mail.php.
'mailers' => [ // ... 'm365' => [ 'transport' => 'm365', 'tenant_id' => env('M365_TENANT_ID'), 'client_id' => env('M365_CLIENT_ID'), 'client_secret' => env('M365_CLIENT_SECRET'), 'timeout' => env('M365_MAIL_TIMEOUT', 15), ], ],
- Use it as default mailer or explicitly when sending:
Mail::mailer('m365')->to('user@example.com')->send(new WelcomeMail());
With default config, package logging is disabled and no package-specific DB writes are made.
Advanced Setup (Enable DB Logging)
- Publish package config and migrations:
php artisan vendor:publish --tag="m365-mailer-config" php artisan vendor:publish --tag="m365-mailer-migrations" php artisan migrate
- Enable logging in
config/m365-mailer.phpor.env:
M365_MAIL_LOGGING_ENABLED=true M365_MAIL_LOGGING_REQUIRE_DATABASE=true M365_MAIL_LOGGING_RETENTION_DAYS=30 M365_MAIL_LOGGING_STORE_RECIPIENTS_PLAINTEXT=false
Configuration Reference
config/m365-mailer.php contains:
timeoutsave_to_sent_itemscache_storelogging.enabledlogging.retention_dayslogging.store_recipients_plaintextlogging.require_databaselogging.recipient_hash_key
Per-mailer credentials remain in config/mail.php under mail.mailers.m365.
The sender is always taken from mail.from.address (MAIL_FROM_ADDRESS).
Logging Behavior
logging.enabled = false: package performs no DB logging.logging.enabled = true+require_database = true: missing tables cause a hard fail before sending.- If Graph accepts the message but post-send DB logging fails, sending is not rethrown (prevents duplicate deliveries on queue retry).
Retention / Pruning
Delete old log data:
php artisan m365-mail:prune
Override retention window:
php artisan m365-mail:prune --days=14
Testing
composer test
License
MIT