Creating a Mail Driver
Because of Masonite's Service Container, It is extremely easy to make drivers that can be used by simply adding your service provider.
Masonite comes shipped with a Service Provider called
MailProvider
which loads a few classes into the container as well as boots the default mail driver using the MailManager
. This manager class will fetch drivers from the container and instantiate them. We can look at the MailProvider
class which will gives us a better explanation as to what's going on:class MailProvider(ServiceProvider):
wsgi = False
def register(self):
self.app.bind('MailConfig', mail)
self.app.bind('MailSmtpDriver', MailSmtpDriver)
self.app.bind('MailMailgunDriver', MailMailgunDriver)
def boot(self):
self.app.bind('Mail', MailManager(self.app))
We can see here that because we are only binding things into the container and we don't need the WSGI server to be running, we set
wsgi = False
. Service Providers that set wsgi
to False
will only run when the server starts and not on every request.We can see here that we are binding a few drivers into the container and then binding the
MailManager
on boot. Remember that our boot method has access to everything that has been registered into the container. The register methods are executed on all providers before the boot methods are executed.The
MailManager
here is important to understand. When the MailManager
is instantiated, it accepts the container as a parameter. When the MailManager
is instantiated, it fires a create_driver
method which will grab the driver from the configuration file and retrieve a MailXDriver
from the container. The create_driver
method is a very simple method:def create_driver(self, driver=None):
if not driver:
driver = self.container.make('MailConfig').DRIVER.capitalize()
else:
driver = driver.capitalize()
try:
self.manage_driver = self.container.make('Mail{0}Driver'.format(driver))
except KeyError:
raise DriverNotFound('Could not find the Mail{0}Driver from the service container. Are you missing a service provider?'.format(driver))
Notice that when the driver is created, it tries to get a
Mail{0}Driver
from the container. Therefore, all we need to do is register a MailXDriver
into the container ('X' being the name of the driver) and Masonite will know to grab that driver.So now we know that we need a
MailXDriver
so let's walk through how we could create a maildrill
email driver.We can simply create a class which can become our driver. We do not need to inherit anything, although Masonite comes with a
BaseMailDriver
to get you started faster and all drivers should inherit from it for consistency reasons. You can make your driver from a normal class object but it will be harder and won't be considered in Pull Requests.Let's create a class anywhere we like and inherit from
BaseMailDriver
:from masonite.driver import BaseMailDriver
class MailMaildrillDriver(BaseMailDriver):
pass
Great! We are almost done. We just have to implement one method on this class and that's the
send
method. All other methods like to
and template
are inherited from the BaseMailDriver
class. You can find out how to send an email using Maildrill and implement it in this send
method.We can look at other drivers for inspiration but let's look at the
MailMailgunDriver
class now:import requests
from masonite.drivers import BaseMailDriver
class MailMailgunDriver(BaseMailDriver):
def send(self, message=None):
if not message:
message = self.message_body
domain = self.config.DRIVERS['mailgun']['domain']
secret = self.config.DRIVERS['mailgun']['secret']
return requests.post(
"https://api.mailgun.net/v3/{0}/messages".format(domain),
auth=("api", secret),
data={"from": "{0} <mailgun@{1}>".format(self.config.FROM['name'], domain),
"to": [self.to_address, "{0}".format(self.config.FROM['address'])],
"subject": self.message_subject,
"html": message})
If you are wondering where the
self.message_body
and self.config
are coming from, check the BaseMailDriver
. All driver constructors are resolved by the service container so you can grab anything you need from the container to make your driver work. Notice here that we don't need a constructor because we inherited it from the BaseMailDriver
Since the
MailManager
class creates the driver on boot, we can simply register the driver into the container via any service providers register method. We could create a new Service Provider and register it there. You can read more about created Service Providers under the Service Providers documentation. For now, we will just register it from within our AppProvider
.Our
AppProvider
class might look something like this:from your.driver.module import MailMandrillDriver
class AppProvider(ServiceProvider):
wsgi = True
def register(self):
self.app.bind('WebRoutes', web.ROUTES)
self.app.bind('ApiRoutes', api.ROUTES)
self.app.bind('Response', None)
self.app.bind('Storage', storage)
# Register new mail driver
self.app.bind('MailMandrillDriver', MailMandrillDriver)
def boot(self):
self.app.bind('Request', Request(self.app.make('Environ')))
self.app.bind('Route', Route(self.app.make('Environ')))
Great! Our new driver is registered into the container. It is now able to be created with Masonite's
MailManager
class. We can retrieve your new driver by doing:def show(self, Mail)
Mail.driver('mandrill') # fetches MailMandrillDriver from container
If we want the
MailManager
to use our new driver by default, change the DRIVER
in our config/mail.py
file. In addition, you may have the users of your driver require a special dictionary entry to the DRIVERS
dictionary:DRIVERS = {
'smtp': {
'host': os.getenv('MAIL_HOST', 'smtp.mailtrap.io'),
'port': os.getenv('MAIL_PORT', '465'),
'username': os.getenv('MAIL_USERNAME', 'username'),
'password': os.getenv('MAIL_PASSWORD', 'password'),
},
'mailgun': {
'secret': os.getenv('MAILGUN_SECRET', 'key-XX'),
'domain': os.getenv('MAILGUN_DOMAIN', 'sandboxXX.mailgun.org')
},
'maildrill': {
'secret': 'xx'
'other_key': 'xx'
}
}
This way, users can easily swap drivers by simply changing the driver in the config file.
That's it! We just extended our Masonite project and created a new driver. Consider making it available on PyPi so others can install it!
Last modified 5yr ago