Naar hoofdinhoud gaan
Om vanuit een package een eigen manier van e-mail versturen aan te bieden, registreer je bij Laravel de logica die een Symfony-Transport aanmaakt. Je hoeft Mailables of de Mailer van Laravel niet opnieuw te bouwen. Deze pagina is gecontroleerd aan de hand van de implementatie van Laravel Framework v13.39.0. Zie E-mail versturen voor de basis van het versturen en Packageontwikkeling voor het voorbereiden van de provider.

Scheid de Mailernaam van de Transportnaam

Mail::mailer('receipts') leest mail.mailers.receipts en roept de aanmaakfunctie aan die bij transport hoort. MailManager::build() geeft die Transport door aan de Mailer van Laravel en stelt bij normaal resolven op naam ook onder meer de globale afzender in. Eigen aanmaakfuncties krijgen voorrang op de ingebouwde Transports. Overschrijf smtp of ses dus niet, maar gebruik een packagespecifieke Transportnaam.

Bouw het registratiepad zonder externe verzending

Controleer eerst de configuratie en het resolvepad in plaats van de API van de bezorgdienst. Maak in src/Transport/MemoryTransport.php van het package Acme\Postbox een klasse die de standaard ArrayTransport uitbreidt.
ArrayTransport::send() slaat de SentMessage van Symfony op in een interne Collection. Je kunt die bekijken met messages() en met flush() wis je de vastgelegde berichten van die instantie. Het label dient om te controleren dat de configuratie de aanmaakfunctie heeft bereikt.
Dit voorbeeld bezorgt geen e-mail naar buiten. Het is bedoeld om registratie en testen te leren. De in-memory registratie wordt niet tussen processen gedeeld of opgeslagen en bevestigt ook niet dat een bericht de ontvanger heeft bereikt. Als je alleen in het geheugen wilt vastleggen, volstaat de ingebouwde array-Transport.

Registreer bij de lazy geladen Manager

De officiële documentatie roept Mail::extend() aan in boot(). In een package kun je dit combineren met een hook na het resolven van een service, zodat applicaties die geen e-mail gebruiken de Manager niet onnodig aanmaken. Maak src/PostboxServiceProvider.php aan.
Laad de provider bijvoorbeeld via package auto-discovery.
  • De MailServiceProvider van Laravel is een deferred provider en registreert mail.manager als singleton. De Mail-facade resolvet ook deze key.
  • callAfterResolving() wacht op een latere resolve als de Manager nog niet is geresolved, en past de uitbreiding direct toe op een al geresolvede Manager. Al aangemaakte Mailers worden hiermee niet vervangen.
  • Maak je eigen provider die de uitbreiding registreert niet zomaar een DeferrableProvider. Zorg ervoor dat je eigen Transport niet wordt opgevraagd voordat de hook is geregistreerd.
  • Dit voorbeeld registreert de hook in de eigen boot(). Een opstartvolgorde waarin andere providers de eigen Mailer al eerder gebruiken, wordt niet gegarandeerd. Ontwerp de registratievolgorde en verstuur geen e-mail binnen register().

Verwar de argumenten van extend niet met die van andere Managers

MailManager::createSymfonyTransport() roept een eigen aanmaakfunctie aan met slechts één configuratie-array. Het zijn niet de twee argumenten applicatie en configuratie zoals bij Cache::extend(). De returnwaarde is een Transport die TransportInterface implementeert, geen Laravel-Mailer of verzendresultaat. Bovendien slaat MailManager::extend() de closure alleen op en bindt die niet opnieuw aan de Manager. Het voorbeeld gebruikt een statische closure en expliciete argumenten, zodat het niet afhankelijk is van hoe $this wordt geïnterpreteerd. Heb je een client voor een externe API nodig, pas dan in deze aanmaakfunctie de configuratie toe en geef die door aan de Transport. Verstuur niets op het moment van registreren, en ontwerp het zo dat configuratie en bestemmingen niet door elkaar raken wanneer meerdere Mailers dezelfde Transportnaam gebruiken.

Voeg Mailers toe in de gebruikende applicatie

Voeg de volgende twee toe aan de bestaande mailers-array in config/mail.php. Overschrijf vanuit het package niet de standaardwaarde of de volledige mailers van de gebruiker.
Wil je het als standaard gebruiken, dan geef je in MAIL_MAILER niet de Transportnaam op, maar de Mailernaam receipts. Met een expliciete keuze per aanroep kun je het gebruiken zonder de bestaande standaard te wijzigen.
Deze raw() is een voorbeeld om het synchrone verzendpad te controleren zonder een view voor te bereiden. Ook bij normaal versturen met een Mailable genereert de Mailer eerst de inhoud en geeft die daarna door aan dezelfde Transport.

Grenzen bij het bouwen van een echte bezorg-Transport

Voor een implementatie die naar een externe dienst bezorgt, kun je volgens de officiële documentatie Symfony\Component\Mailer\Transport\AbstractTransport uitbreiden en doSend(SentMessage $message): void en __toString(): string implementeren. De registratiemethode hierboven kun je ongewijzigd gebruiken. Mailer::sendSymfonyMessage() van Laravel roept send() van de Transport aan. Als een MessageSending-listener het verzenden afbreekt of de Transport null retourneert, retourneert ook send() van Laravel geen verzendresultaat. Beschouw het uitblijven van een exception niet als enig bewijs van geslaagde bezorging. Ook als er een SentMessage terugkomt, is dat geen garantie dat het bericht de mailbox van de ontvanger heeft bereikt.

Onderscheid configuratiewijzigingen van het opnieuw resolven van Mailers

MailManager houdt Mailers en hun Transport per Mailernaam vast. Alleen de configuratie wijzigen of extend() opnieuw registreren werkt al aangemaakte objecten niet bij.
$same is dezelfde Mailer als $before. Bij $after, die na purge('receipts') wordt opgehaald, wordt de Transport aangemaakt op basis van de nieuwe configuratie. De gecontroleerde implementatie van purge() verwijdert alleen de referentie uit de interne array van de Manager. De oude $before en services waarin die Mailer al is geïnjecteerd, worden niet vervangen. Het sluit ook niet expliciet externe verbindingen af.

Bovenliggende Mailers zoals failover worden apart vastgehouden

failover en roundrobin lezen de configuratie van de onderliggende Mailernamen, maken de Transports rechtstreeks aan en nemen ze op in de Transport van de bovenliggende Mailer. Ze delen geen instanties van Mail::mailer() van de onderliggende Mailers. Wijzig je daarom de configuratie van een onderliggende Mailer en roep je purge('receipts') aan, dan wordt de interne Transport van een al geresolvede bovenliggende Mailer niet bijgewerkt. Ook de betreffende bovenliggende Mailer moet opnieuw worden geresolved. Zelfs forgetMailers() kan referenties naar bovenliggende Mailers die de gebruikende code al vasthoudt niet vervangen. Neem in productie het vernieuwen van de configuratiecache en het herstarten van langlevende workers op in je normale deployproces. Zie opnieuw resolven niet als een gelijktijdige update van alle processen, en lees ook Levensduur van services.

Test niet alleen met Mail::fake, maar ook het echte aanmaakpad

Mail::fake() dient om het versturen en in de queue plaatsen van Mailables vast te leggen. MailFake::mailer() resolvet geen echte Mailer, maar legt de Mailernaam vast en retourneert de fake zelf. Ook raw() verstuurt niets. Assertions voor verzonden Mailables kunnen dus slagen, zelfs als je eigen Transport niet is geregistreerd. Laat in de testomgeving van het package zoals hieronder de echte Manager en Transport doorlopen, zonder fake. De Transport in dit voorbeeld verstuurt niets naar buiten, zodat je het registratiepad zonder risico op echte bezorging kunt controleren. Er wordt eerst een hulpprovider geladen, zodat ook het geval van een al geresolvede Manager wordt geverifieerd.
Deze test controleert de grenzen van registratie, configuratie, synchroon versturen en vastgehouden objecten. Verifieer bij een echte bezorg-Transport daarnaast de payload en foutresponses met een mock of fake van de API-client. Als je versturen via de queue aanbiedt, scheid dan ook de tests voor Queues en controleer dat de provider ook aan de kant van de worker wordt geladen.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 30 oktober 2026