Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Mails Laravel Package

vormkracht10/laravel-mails

Laravel Mails is a Laravel package for managing transactional email. It lets you store mailables and templates in the database, preview, log, and resend outgoing messages, and keep email workflows organized with Laravel's native mail system.

View on GitHub
Deep Wiki
Context7

Getting Started

Get it running in four commands:

composer require vormkracht10/laravel-mails
php artisan vendor:publish --tag="laravel-mails-config"   # and/or --tag="laravel-mails-migrations"
php artisan migrate

Then send any mail in your app — through a Mailable, a Notification, or Mail::raw() — and check the mails table. There is no code to add to your Mailables; the package hooks Laravel's mail events and records what was sent.

Where to look first, in this order:

  1. config/mails.php — the knobs that matter are the storage disk for attachments, whether mail bodies are persisted, and any retention/pruning settings. Read this before writing a single line of app code, because changing the disk later does not move already-stored attachments.
  2. The published migration — the column set tells you what you can query (to, cc, bcc, subject, body, headers, timestamps, attachment relations). This is your de facto API surface.
  3. The package's Mail model — check whether it is bound in the config so you can extend it.

First real use case: support tickets of the form "I never received my invoice." Instead of trawling logs or asking the customer to check spam, you open the record for that address, see the exact rendered HTML and headers that left your app, confirm the recipient, and resend from the same screen or a tinker one-liner. That single workflow justifies the install for most teams.

Implementation Patterns

Automatic capture is the default, keep it that way. Resist the urge to wrap sends in a custom service. Every Mail::to(...)->send(...), queued Mailable, and Notification goes through the same pipeline, so a listener-based package gives you coverage you would never maintain by hand. If a specific mail must not be recorded (password resets, magic links), prefer excluding by Mailable class in config over bypassing the mailer.

Build an admin index on top of the model. The package gives you persistence, not a UI. A simple resource — filters on recipient, subject, date range, and a detail view rendering the stored body in an iframe with srcdoc — is a half-day of work and is where the package earns its keep. Add a "resend" action that reconstructs the Mailable from the stored record, or simply re-sends the stored body via a raw message if you only need the identical payload.

Attachments: treat the disk as production storage. Configure a dedicated disk (S3 or a private local disk), not public. Attachments should be streamed through a controller with authorization, never hot-linked. Decide early whether you store attachments at all — they are the single biggest source of growth.

Scope by tenant if you are multi-tenant. The package stores a flat table. Add a tenant_id (or your app's foreign key) via a small migration and a global scope on your extended model, otherwise a shared admin view leaks other customers' mail content. This is the one place where extending the package model is genuinely required rather than nice-to-have.

Pair it with scheduled pruning. Whatever retention you choose — 30, 90, 180 days — wire it into the scheduler on day one. A mail audit table with no retention policy becomes a multi-gigabyte liability within a year in any app that sends transactional mail at volume.

Use it in tests sparingly. Asserting on Mail::fake() is still the right tool for "did we send the right Mailable." Reach for the stored records only when you need to assert on rendered content, headers, or attachment presence — that is where faking falls short.

Gotchas and Tips

  • Body storage is a compliance decision, not a technical one. Stored bodies regularly contain PII, reset tokens, one-time codes, and pricing. Check what the config persists and consider disabling body capture for categories of mail, or encrypting the column. Do this before the first production deploy, not after an audit.
  • "Sent" means handed to the transport, not delivered. A record exists even if the SMTP server later bounced. Do not build delivery-confirmation features on this table; use webhooks from your provider for that. Keep the two concepts separate in your UI or support staff will be misled.
  • Queued mail and DB::transaction. Records are written when the job runs, not when you dispatch. Wrapping a dispatch in a transaction that later rolls back leaves a mail record for a mail that was actually sent — because it was. Test the queued path explicitly rather than assuming synchronous behavior from local testing.
  • Config cache bites on multi-server setups. php artisan config:cache after changing the disk or model binding, and remember that every queue worker needs the same storage credentials. A worker that cannot write to the attachments disk will silently degrade to records without files unless you watch for it.
  • Verify exact config keys and method names against the repo's README before committing to an implementation plan — the package is small and its surface changes between minor versions. The migration and config file are the ground truth.
  • Extension points worth knowing: the configurable model binding (for adding scopes, casts, and relations), the storage disk, and any published views if you want the detail screen to match your admin theme. If you find yourself needing more than these, you are probably building a feature (delivery tracking, open rates) that belongs in your mail provider's dashboard, not in this table.
  • Debugging a missing record: confirm the mail actually went through Laravel's mailer (Mail::fake() in a stray test, or a Notification::route bypassing mail), then check whether the config restricts capture to specific mailers or Mailables. Nine times out of ten it is one of those two.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core