If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeIn .env
, we're using the null
transport. In .env.local
, we're overriding that to send to Mailtrap. This is great for development, but it's time for our app to grow up, get a job, and join the real world. It's time for our app to send real emails through a real email server.
To do that, I recommend using a cloud-based email service... and Symfony Mailer can send to any service that supports the SMTP protocol... which is all of them. We did this for Mailtrap using the {username}:{password}@{server}:{port}
syntax.
But to make life even nicer, Mailer has special support for the most common email services, like SendGrid, Postmark, Mailgun, Amazon SES and a few others. Let's use SendGrid.
Before we even create an account on SendGrid, we can jump in and start configuring it. In .env.local
, comment-out the Mailtrap MAILER_DSN
and replace it with MAILER_DSN=smtp://sendgrid
. In Symfony 4.4, the syntax changed to sendgrid://default
.
#MAILER_DSN=smtp://USERNAME:PASSWORD@smtp.mailtrap.io:2525
MAILER_DSN=smtp://sendgrid
# Symfony 4.4+ syntax
#MAILER_DSN=sendgrid://default
So far, we've seen two transports - two ways of delivering emails: the smtp
transport and the null
transport. Symfony also has a sendgrid
transport, as well as a mailgun
transport amazonses
transport and many others.
In Symfony 4.3, you choose which transport you want by saying smtp://
and then the name of one of those transports, like null
or sendgrid
. In Symfony 4.4 and higher, this is different. The syntax is the transport name, like null
or sendgrid
then ://
and whatever other options that transport needs. The word default
is a dummy placeholder that's used when you don't need to configure a "server", like for the null
transport or for sendgrid
, because that transport already knows internally what the address is to the SendGrid servers.
Anyways, whether you're in Symfony 4.3 with the old syntax or Symfony 4.4 with the new one, this is how you say: "I want to deliver emails via the SendGrid transport".
At this point, some of you might be screaming
Wait! That can't possibly be all the config we need to send emails!
And you're 1000% percent correct. This doesn't contain any SendGrid username, or API key. Heck, we haven't even created a SendGrid account yet! All true, all true. But let's... try it anyways. Because, Symfony is going to guide us through the process. How nice!
Head over to the browser and refresh. Woh! An immediate error:
Unable to send emails via Sendgrid as the bridge is not installed.
This is another example of Symfony making it easy to do something... but without bloating our project with stuff we don't need. Now that we do want to use Sendgrid, it helps us install the required library. Copy the composer require
line, spin over to your terminal and paste:
composer require symfony/sendgrid-mailer
Ooh, this package came with a recipe! Let's see what it did:
git status
In addition to the normal stuff, this also modified our .env
file. Let's see how:
git diff .env
Cool! The recipe added a new section to the bottom! Back in our editor, let's see what's going on in .env
:
... lines 1 - 48 | |
###> symfony/sendgrid-mailer ### | |
# SENDGRID_KEY= | |
# MAILER_DSN=smtp://$SENDGRID_KEY@sendgrid | |
### |
Yea... this makes sense. We know that mailer is configured via a MAILER_DSN
environment variable... and so when we installed the SendGrid mailer package, its recipe added a suggestion of how that variable should look in order to work with SendGrid.
Now, two important notes about this. First, when you install this package in Symfony 4.4, the config added by the recipe will look a bit different: it will add just one line:
MAILER_DSN=sendgrid://KEY@default
Like we just talked about, this is because Symfony 4.4 changed the config format: the "transport type" is now at the beginning. The KEY
is a placeholder: we'll replace with a real API key in a few minutes. And the @default
part just tells the SendGrid transport to send the message to whatever the actual SendGrid hostname is.... we don't need to worry about configuring that.
Now, if you look at the config that Symfony 4.3 uses, you'll notice the second important thing: this defines two environment variables. Gasp! It defines SENDGRID_KEY
and then MAILER_DSN
. This... is just a config trick. See how the MAILER_DSN
value contains $SENDGRID_KEY
? It's using that variable: it's environment variables inside environment variables! With this setup, you could commit this MAILER_DSN
value to .env
and then only need to override SENDGRID_KEY
in .env.local
.
This idea - the idea of using environment variables inside environment variables totally works in Symfony 4.4. But to keep the config a bit simpler, in Symfony 4.4 - you won't see this two-variable system in the recipe. Instead, we'll configure the entire MAILER_DSN
value. After all, it's a pretty short string.
Next... let's actually do that configuration! It's time to create a SendGrid account and start using it.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"aws/aws-sdk-php": "^3.87", // 3.110.11
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-snappy-bundle": "^1.6", // v1.6.0
"knplabs/knp-time-bundle": "^1.8", // v1.9.1
"league/flysystem-aws-s3-v3": "^1.0", // 1.0.23
"league/flysystem-cached-adapter": "^1.0", // 1.0.9
"league/html-to-markdown": "^4.8", // 4.8.2
"liip/imagine-bundle": "^2.1", // 2.1.0
"nexylan/slack-bundle": "^2.1,<2.2.0", // v2.1.0
"oneup/flysystem-bundle": "^3.0", // 3.1.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"sensio/framework-extra-bundle": "^5.1", // v5.4.1
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.3.4
"symfony/console": "^4.0", // v4.3.4
"symfony/flex": "^1.9", // v1.17.6
"symfony/form": "^4.0", // v4.3.4
"symfony/framework-bundle": "^4.0", // v4.3.4
"symfony/mailer": "4.3.*", // v4.3.4
"symfony/messenger": "4.3.*", // v4.3.4
"symfony/orm-pack": "^1.0", // v1.0.6
"symfony/security-bundle": "^4.0", // v4.3.4
"symfony/sendgrid-mailer": "4.3.*", // v4.3.4
"symfony/serializer-pack": "^1.0", // v1.0.2
"symfony/twig-bundle": "^4.0", // v4.3.4
"symfony/twig-pack": "^1.0", // v1.0.0
"symfony/validator": "^4.0", // v4.3.4
"symfony/web-server-bundle": "^4.0", // v4.3.4
"symfony/webpack-encore-bundle": "^1.4", // v1.6.2
"symfony/yaml": "^4.0", // v4.3.4
"twig/cssinliner-extra": "^2.12", // v2.12.0
"twig/extensions": "^1.5", // v1.5.4
"twig/inky-extra": "^2.12" // v2.12.0
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.2.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/browser-kit": "4.3.*", // v4.3.5
"symfony/debug-bundle": "^3.3|^4.0", // v4.3.4
"symfony/dotenv": "^4.0", // v4.3.4
"symfony/maker-bundle": "^1.0", // v1.13.0
"symfony/monolog-bundle": "^3.0", // v3.4.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.3.4
"symfony/profiler-pack": "^1.0", // v1.0.4
"symfony/var-dumper": "^3.3|^4.0" // v4.3.4
}
}