lloadout/microsoftgraph
Laravel package for integrating the Microsoft Graph API. Connect your app to Microsoft 365 to work with Outlook mail, calendars, OneDrive files, Teams, and Azure AD users through a fluent interface.
orchestra/testbench 9–11). If you are still on PHP 8.1 or Laravel 10, stay pinned to v1.20.composer require lloadout/microsoftgraphMail.Send for the mailer, Files.ReadWrite.All for the disk), click Grant admin consent, and create a client secret.vendor:publish tag/provider) and set the tenant ID, client ID and secret in .env. Re-run php artisan config:clear any time you change them — the client caches tokens per config.config/mail.php that uses the package transport, point MAIL_MAILER at it (or call it explicitly), and keep sending mail the normal way:
Mail::mailer('microsoftgraph')->to($user)->send(new InvoiceReady($invoice));
onedrive disk, so Storage::disk('onedrive')->put('reports/q1.pdf', $pdf) works like any other Flysystem disk.Look at the README first — v1.21 added a requirements table and refreshed the Laravel docs links — then at config/microsoftgraph.php for the credential keys and the filesystems / mail snippets it documents.
Mail routing. Define the Graph mailer once in config/mail.php with the package transport, then either make it the default MAIL_MAILER or select it per message with Mail::mailer('microsoftgraph'). This is the usual escape hatch for Microsoft 365 tenants where SMTP basic auth has been disabled. Queue the Mailable rather than sending inline — Graph throttles, and a queue gives you retries for free.
Filesystem usage. Treat the disk as ordinary Flysystem: put, get, exists, delete, files, allFiles. For anything large, use writeStream() / readStream() so you aren't holding whole files in memory, and pass ['overwrite' => true] (or an explicit visibility) rather than assuming the default conflict behaviour.
Multiple drives or tenants. Register additional disks yourself with Storage::extend('onedrive-archive', ...) in a service provider and reuse the package adapter with a differently configured client. This is the documented extension point for multi-tenant apps where each tenant has its own drive.
Testing. Storage::fake('onedrive') and Mail::fake() cover almost everything without touching Graph — the disk is just Flysystem underneath and the mailer is just a transport. Reserve real Graph calls for a single integration test against a test tenant.
HTTP client. The adapter/transport accept a client instance, so you can wrap Guzzle with RetryMiddleware and your own logging handler instead of writing retry logic around every call.
read() no longer returns false. OnedriveAdapter now matches the Flysystem 3 signatures and throws League\Flysystem\UnableToReadFile. Audit any code that inspects the adapter directly (custom wrappers, $adapter->read() checks, === false branches) and switch to catching the exception. Code going through Storage::get() was already exception-based, so most apps need no change.Storage::extend() closures. v1.21 fixed this internally by capturing the provider explicitly. If you copied the old provider pattern into your own package or app, do the same — don't rely on $this inside the closure, it won't be what you expect on Laravel 13.MicrosoftGraphTransport::send() now declares an explicit nullable Envelope. If you subclass the transport, your override must use ?Envelope $envelope = null or you'll hit a fatal signature incompatibility.Mail.Send, Graph sends as the from address you set; a typo or an unlicensed mailbox returns ErrorInvalidUser/403 rather than a friendly bounce. Validate the from address in config.Retry-After. Handle it at the queue level (backoff(), retryUntil()) instead of catching it ad hoc.sendMail accepts roughly 3 MB total; larger attachments require an upload session. For big files, upload to OneDrive and mail a link instead.Storage::url() won't work on this disk. There is no public URL to synthesise — use a Graph sharing link or temporaryUrl if the disk exposes it, and don't assume asset()-style paths..env is almost always a cached token or a stale config:cache. Clear both before digging into the app registration.larastan/larastan. If you run PHPStan with Larastan, move off the abandoned nunomaduro/larastan — the package did the same in this release, and staying on the old one will drift from the new Flysystem signatures.How can I help you explore Laravel packages today?