jeffersongoncalves/laravel-mail
Complete email management for Laravel: logging, translated database templates with versioning, webhook delivery tracking for five providers, pixel open/click tracking, suppression lists, inline CSS, statistics, and retries.
Mail facade, Notification system, and Event system. It integrates seamlessly with Laravel’s existing email infrastructure (e.g., Mailable, Notification channels) without requiring architectural overhauls.laravel-mail.campaigns.enabled, so it can be evaluated independently.add_tags_to_mail_logs_table migration, extending the existing mail_logs table rather than introducing a parallel schema — a low-coupling change that keeps the aggregate reporting model on the same table.MailBounced, MailOpened), enabling custom logic without modifying core package code.tag() / Envelope tags) are persisted on the log record, creating a lightweight attribution key. CampaignReport and php artisan mail:campaign [tag] then aggregate sent, delivered, bounced, complained, distinct opens/clicks, open/click/click-to-open rates, and most-clicked links per campaign. This is a read-model layered on top of existing tracking data — no external analytics dependency required for baseline campaign reporting.text() as a view name — a correctness fix that unblocks plain-text parts for template-driven mail, but one that should be regression-tested if you ship text alternatives.spatie/laravel-translatable (for templates) and tijsverkoyen/css-to-inline-styles (for CSS inlining), both well-maintained packages. No new dependencies appear to be introduced by the campaign reporting feature.mail_logs, mail_templates, mail_template_versions, etc.), plus the 1.4.0 migration adding a tags column to mail_logs. On a large existing mail_logs table, verify the migration is online-safe (column addition + any index build can lock or degrade writes depending on engine/version). Plan a maintenance window or use an online-DDL tool if the table is already tens of millions of rows.mail_logs filtered by tag. Without an index on the tags column (and a sensible tag taxonomy), report queries will table-scan and degrade as volume grows. Treat tag naming as a governed convention, not free-form strings, or reports become unjoinable.mail:campaign runs ad hoc aggregate queries; running it against production during peak send windows may contend with write traffic. Prefer read replicas or scheduled off-peak execution.CampaignReport with custom metrics) may require forking the package.mail_logs, and how long will the 1.4.0 tags migration take to apply in production?text() bug, and do those need re-validation?mail:campaign output, or export into an existing BI/data warehouse?List-Unsubscribe header meet regulatory requirements (e.g., CAN-SPAM, GDPR)?CampaignReport?Mail, Notification, and Event systems. No conflicts with existing email infrastructure. Campaign reporting consumes existing log/tracking data through Eloquent aggregates and an artisan command — no new runtime service required.campaigns.enabled in production.CampaignReport.mail:campaign (or a custom job wrapping CampaignReport) on the scheduler to snapshot metrics over time, since the command computes point-in-time aggregates.Phase 1: Logging and Templates (Low Risk)
LARAVEL_MAIL_LOGGING_ENABLED=true) to audit existing emails without disrupting workflows.PreviewTemplateAction to validate rendering before sending. Validate plain-text parts explicitly, given the 1.4.0 text() behavior change.TemplateMailable classes.Phase 1b: Upgrade to 1.4.0 (Low Risk, Additive)
add_tags_to_mail_logs_table migration in a staging clone sized like production to measure runtime.laravel-mail.campaigns.enabled off until tag conventions are agreed; the feature is inert when disabled.Phase 2: Tracking (Medium Risk)
MailBounced/MailComplained to auto-suppress addresses and trigger alerts.Phase 3: Campaign Reporting (Medium Risk, 1.4.0)
tag() calls to outbound mail at the point of send (job/notification layer), not scattered across templates.laravel-mail.campaigns.enabled, run php artisan mail:campaign [tag] on a known campaign, and reconcile against provider dashboards before publishing any number.Phase 4: Advanced Features (High Customization)
List-Unsubscribe headers globally.php artisan mail:prune) for log cleanup — noting pruning truncates the historical window available to campaign reports.Mailable classes. Only TemplateMailable classes benefit from full template features.composer update, php artisan migrate, republish config to pick up the campaigns block.tag() or Envelope tags are now read by the package. If you already set tags for provider-side purposes (e.g., SendGrid categories), those will now appear in mail_logs — verify naming and volume are acceptable and that no sensitive values leak into tag strings.spatie/laravel-activitylog, laravel-notification-channels). Test with your stack.| Step | Priority | Effort | Dependencies |
|---|---|---|---|
| Install and configure package | High | Low | Laravel 10+ |
| Enable logging and validate data | High | Medium | Database |
| Run 1.4.0 tags migration (staging then prod) | High | Low | DBA review for large tables |
| Regression-test template text bodies | High | Low | Templates with text parts |
| Define campaign tag taxonomy | Medium | Medium | Marketing/product alignment |
| Migrate critical emails to templates | Medium | High | Template design |
Instrument tag() calls at send sites |
Medium | Medium | Tag taxonomy |
Enable campaigns.enabled and validate mail:campaign |
Medium | Low | Migration + tagged traffic |
| Set up webhook tracking for primary provider | Medium | High | Provider API access |
| Implement pixel tracking fallback | Low | Medium | URL routing |
| Configure suppression lists and retries | Low | Medium | Business rules |
| Set up pruning and analytics | Low | Medium | Monitoring tools |
php artisan mail:prune) to manage log growth. Configure retention policies per status (e.g., keep bounces for 1 year, opens for 6 months). Pruning directly shrinks the campaign reporting window — align retention with how far back marketing needs to report, or snapshot campaign aggregates before pruning runs.mail:campaign plans use it.LARAVEL_MAIL_SENDGRID_SIGNING_SECRET) in a secrets manager (e.g., Laravel Forge, Vault).laravel-mail.campaigns.enabled per environment.PreviewTemplateAction to validate before deployment. Include plain-text output in the preview checklist now that 1.4.0 fixes text-part handling.mail:stats to query sent/delivered emails. Check mail_logs table for failed sends.mail_suppressions table for false positives (e.g., typo’d emails).campaigns.enabled on at send time; (3) do webhook events exist for the window; (4) has pruning removed the relevant rows. Most "broken report" tickets are tag typos or disabled config at send time, not aggregation bugs.PreviewTemplateAction to debug rendering.text() behavior and adjust the template rather than the package.X-SMTPAPI) for advanced features; its category field is a natural mirror of package tags, so keep the two namespaces consistent to avoid divergent reporting.mail:campaign cost scales with rows-per-tag. Schedule reports off-peak, run against read replicas, or materialize daily snapshots into a summary table as stakeholder demand grows.campaigns.enabled is off in one environment or a send path bypasses tag(), those sends simply never appear in reports. There is no error — only missing rows. Mitigate with a periodic check that outbound tagged volume matches provider send volume.mail_logs table can cause write stalls. Failure mode is elevated send latency or timeouts during deploy, not data loss.How can I help you explore Laravel packages today?