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

Emailsender Laravel Package

rednucleus/emailsender

A Laravel/PHP package for sending application emails through a simple, reusable API. It helps compose messages, manage recipients and content, and integrate email delivery into Laravel projects with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Wire the Bitbucket repo into Composer. The package is not on Packagist, so add a VCS repository before requiring it:

"repositories": [
  { "type": "vcs", "url": "https://bitbucket.org/actandodev/emailsender.git" }
]
composer require rednucleus/emailsender

If there are no tagged releases, pin a commit: rednucleus/emailsender:dev-main#<sha>.

2. Register and publish. Check composer.json for Laravel auto-discovery. If absent, add the provider to config/app.php. Then publish the config and inspect the keys:

php artisan vendor:publish --provider="Rednucleus\EmailSender\EmailSenderServiceProvider"

3. Create the Azure app registration. In Entra ID, register an app, add the Mail.Send application permission, and grant admin consent. Collect tenant ID, client ID, and client secret. The From address must be a real, licensed mailbox (or a shared mailbox the app is allowed to send as).

4. Configure a mailer. Set the package's transport name in config/mail.php (usually alongside smtp), point MAIL_MAILER at it, and fill in the Graph credentials and sender address in .env.

5. First use case. The lowest-risk first target is a single transactional flow — password reset or a queued notification — for an M365-hosted domain. This is the common driver: Microsoft is deprecating SMTP AUTH for Exchange Online, and Graph app-only sending sidesteps per-mailbox credentials entirely. Send one email, then confirm it in Exchange Message Trace or the sender's Sent Items; Graph returns 202 Accepted immediately, so an HTTP 2xx only proves the request was queued.

Where to look first: the service provider (how the transport is registered with Laravel's MailManager), the transport class itself (what interface it implements and how it builds the Graph payload), and the published config file (exact key names, fallback behavior, token cache settings).

Implementation Patterns

Treat it as a transport, not a mail API. Your Mailable and Notification classes stay unchanged; only the mailer changes. Select it per message or per mailable:

Mail::mailer('graph')->to($user)->send(new InvoiceReady($invoice));

// or on the mailable
public function mailer(): string { return 'graph'; }

Queue everything. Graph is a network call with token acquisition in front of it; sending inline in a request will add latency and couple your response time to Microsoft's availability. ShouldQueue mailables plus a retry policy is the default shape.

Per-tenant credentials for multi-tenant SaaS. Keep one default config for your own domain and resolve credentials dynamically for customer tenants — mutate config(['mail.mailers.graph.tenant_id' => ...]) before dispatch, or pass a context object if the package supports one. Be careful with queued jobs: the config must be set inside the job, not at dispatch time, since the worker deserializes later.

Fallback pattern. If the package exposes both Graph and SMTP paths, use Graph as primary and SMTP as a break-glass route for a specific mailbox or environment. Do not implement silent catch-and-resend inside the same request: a duplicate is worse than a failure for most transactional mail. Prefer a dead-letter queue with manual replay.

Testing. Mail::fake() short-circuits before the transport, so it validates your application logic but not the package. To exercise the transport itself, Http::fake() the Graph token and sendMail endpoints and assert on the request body (recipients, subject, body content type):

Http::fake([
    'login.microsoftonline.com/*' => Http::response(['access_token' => 'x', 'expires_in' => 3600]),
    'graph.microsoft.com/*' => Http::response(null, 202),
]);

Notifications and multi-channel flows. Map mail channel notifications through the Graph mailer the same way; nothing changes in the notification class beyond a via() that returns ['mail']. Verify that to, cc, and bcc all survive into the Graph message payload — this is a common place for custom transports to drop fields.

Attachments. For small PDFs the transport will base64-encode into fileAttachment. Anything large pushes against Graph's simple-attachment ceiling; confirm whether the package implements an upload session before promising large-attachment support.

Gotchas and Tips

Graph says "accepted," not "delivered." A 202 means Microsoft queued the message. Bounces, blocked-recipient errors, and mailbox-full conditions arrive later as NDRs. Do not treat a successful send as proof of delivery in your own telemetry; correlate with Message Trace during rollout.

Wrong permission type is the top failure. Mail.Send as a delegated permission will not work with client-credentials flow. You need the application permission plus tenant-wide admin consent, or every send fails with a 403 that looks like a config problem.

Sender mismatch. The From must match the configured sending mailbox, or be one the app is explicitly allowed to send as. Overriding from() inside a Mailable is the usual way this breaks, often only in production where a different MAIL_FROM_ADDRESS is set. Assert your from in a test.

Token caching. If the transport requests a fresh token per email, queued batches will hammer login.microsoftonline.com and hit throttling. Confirm tokens are cached (typically Laravel's cache store) with expiry margin. A cache driver of array in a queue worker means zero reuse across jobs.

Throttling. Graph returns 429 with Retry-After. Your queue's backoff() and tries should accommodate this; a tries = 1 job will drop mail on the first throttle. Log the response headers when debugging.

SMTP AUTH is disabled by default in many M365 tenants. If the fallback path is SMTP, it may fail for reasons unrelated to your code — test it deliberately, and know that a security admin can disable it at any time.

Config caching. After changing credentials, run php artisan config:clear (or config:cache). A stale cached config with an old client secret produces an auth error that looks identical to a permissions error.

Transport name must match exactly. The key in config/mail.php must be the string you set in MAIL_MAILER and the string the provider registers. A typo produces InvalidArgumentException: Mailer [x] is not defined, which is easy to misread as a missing package.

Versioning risk. No stars, no dependents, Bitbucket-only. There is no Packagist metadata, no release history, and no community signal to lean on. Pin to a commit SHA, read the transport source end to end (it is likely a few hundred lines), and be prepared to vendor or fork it if Microsoft changes the Graph endpoint or the maintainer goes quiet.

Debugging without sending. Use the log mailer to verify your application assembles messages correctly, and isolate transport problems separately by testing Graph credentials with a raw curl against the token endpoint. That splits "my mailable is wrong" from "my Azure app is wrong."

Extension points to check for. Whether the transport merges custom headers (Reply-To, X-*), whether it preserves both text and html bodies, and whether it honors Laravel's global to/cc/bcc config from mail.php. These are the fields real applications rely on and the ones custom transports most often lose.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle