This course is still being released! Check back later for more chapters.

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com
Login to bookmark this video
13.

Generating URLs in the CLI Environment

|

Share this awesome video!

|

When we switched to asynchronous email sending, we broke our email links! It's using localhost as our domain, weird and wrong.

Back in our app, we can get a hint as to what's going on by looking at the profiler for the request that sent the email. Remember, our email is now marked as "queued". Go to the "Messages" tab and find the message: SendEmailMessage. Inside is the TemplatedEmail object. Open this up. Interesting! htmlTemplate is our Twig template but html is null! Shouldn't that be set to the rendered HTML from that template? This little detail is important: the email template is not rendered when our controller sends the message to the queue. Nope! the template isn't rendered until later, when we run messenger:consume.

Why does this matter? Well messenger:consume is a CLI command, and when generating absolute URLs in the CLI, Symfony doesn't know what the domain should be (or if it should be http or https). So why does it when in a controller? In a controller, Symfony uses the current request to figure this out. In a CLI command, there is no request so it gives up and uses http://localhost.

Configure the Default URL

Let's just tell it what the domain should be.

Back in our IDE, open up config/packages/routing.yaml. Under framework, routing, these comments explain this exact issue. Uncomment default_uri and set it to https://universal-travel.com - our lawyers are close to a deal!

19 lines | config/packages/routing.yaml
framework:
router:
// ... lines 3 - 4
default_uri: https://universal-travel.com
// ... lines 6 - 19

In development though, we need to use our local dev server's URL. For me, this is 127.0.0.1:8000 but this might be different for other team members. I know that Bob uses bob.is.awesome:8000 and he kinda is.

Development Environment Default URL

To make this configurable, there's a trick: the Symfony CLI server sets a special environment variable with the domain called SYMFONY_PROJECT_DEFAULT_ROUTE_URL.

Back in our routing config, add a new section: when@dev:, framework:, router:, default_uri: and set it to %env(SYMFONY_PROJECT_DEFAULT_ROUTE_URL)%:

19 lines | config/packages/routing.yaml
// ... lines 1 - 6
when@dev:
// ... lines 8 - 10
framework:
router:
default_uri: '%env(SYMFONY_PROJECT_DEFAULT_ROUTE_URL)%'
// ... lines 14 - 19

This environment variable will only be available if the Symfony CLI server is running and you're running commands via symfony console (not bin/console). To avoid an error if the variable is missing, set a default. Still under when@dev, add parameters: with env(SYMFONY_PROJECT_DEFAULT_ROUTE_URL): set to http://localhost.

19 lines | config/packages/routing.yaml
// ... lines 1 - 6
when@dev:
parameters:
env(SYMFONY_PROJECT_DEFAULT_ROUTE_URL): 'http://localhost'
// ... lines 10 - 19

This is Symfony's standard way to set a default value for an environment variable.

Restart messenger:consume

Testing time! But first, jump back to your terminal. Because we made some changes to our config, we need to restart the messenger:consume command to, sort of, reload our app:

symfony console messenger:consume async -vv

Cool! The command is running again and using our sweet new Symfony config. Head back to our app... and book a trip! Quickly go back to the terminal... and we can see the message was processed.

Pop over to Mailtrap and... here it is! Moment of truth: click a link... Sweet, it's working again! Bob will be so happy!

Running messenger:consume in the Background

If you're like me, you probably find having to keep this messenger:consume command running in a terminal during development a drag. Plus, having to restart it every time you make a code or config change is annoying. I'm annoyed! Time to add the fun back to our functions with another Symfony CLI trick!

In your IDE, open this .symfony.local.yaml file. This is the Symfony CLI server config for our app. See this workers key? It lets us define processes to run in the background when we start the server. We already have the tailwind command set.

Add another worker. Call it messenger - though that could be anything - and set cmd to ['symfony', 'console', 'messenger:consume', 'async']:

9 lines | .symfony.local.yaml
workers:
// ... lines 2 - 5
messenger:
cmd: ['symfony', 'console', 'messenger:consume', 'async']
// ... lines 8 - 9

This solves the issue of needing to keep this running in a separate terminal window. But what about restarting the command when we make changes? No problemo! Add a watch key and set it to config, src, templates and vendor:

9 lines | .symfony.local.yaml
workers:
// ... lines 2 - 5
messenger:
// ... line 7
watch: ['config', 'src', 'templates', 'vendor']

If any files in these directories change, the worker will restart itself. Smart!

Back in your terminal, restart the server with symfony server:stop and symfony serve -d messenger:consume should be running in the background! To prove it, run:

symfony server:status

3 workers running! The actual PHP webserver, the existing tailwind:build worker, and our new messenger:consume. So cool!

Next, let's explore how to make assertions about emails in our functional tests!