Buy Access to Course
04.

Configuring Bundles

Share this awesome video!

|

In the show controller, we're using two services: MarkdownParserInterface - from a bundle we installed - and CacheInterface from Symfony itself:

52 lines | src/Controller/QuestionController.php
// ... lines 1 - 4
use Knp\Bundle\MarkdownBundle\MarkdownParserInterface;
// ... lines 6 - 8
use Symfony\Contracts\Cache\CacheInterface;
// ... lines 10 - 11
class QuestionController extends AbstractController
{
// ... lines 14 - 31
public function show($slug, MarkdownParserInterface $markdownParser, CacheInterface $cache)
{
// ... lines 34 - 49
}
}

And... this was pretty easy: add an argument with the right type-hint then... use the object!

But I'm starting to wonder how can I control the behavior of these services? Like, what if I want Symfony's cache service to store in Redis instead of on the filesystem? Or maybe there are some options that I can pass to the markdown parser service to control its features.

Let's dd($markdownParser) - that's short for dump() and die() - to see what this object looks like:

54 lines | src/Controller/QuestionController.php
// ... lines 1 - 11
class QuestionController extends AbstractController
{
// ... lines 14 - 31
public function show($slug, MarkdownParserInterface $markdownParser, CacheInterface $cache)
{
// ... lines 34 - 40
$parsedQuestionText = $cache->get('markdown_'.md5($questionText), function() use ($questionText, $markdownParser) {
return $markdownParser->transformMarkdown($questionText);
});
dd($markdownParser);
// ... lines 46 - 51
}
}

Move over and refresh. This is apparently an instance of some class called Max. And... inside, there's a $features property: it kind of looks like you can turn certain features on or off. Interesting.

If we did want to control one of these feature flags, we can't really do that right now. Why? Because we aren't responsible for creating this object: the bundle just gives it to us.

Bundle Configuration

This is a super common problem: a bundle wants to give you a service... but you want some control over what options are passed to that object when it's instantiated. To handle this, each bundle allows you to pass configuration to it where you describe the different behavior that you want its services to have.

Let me show you exactly what I'm talking about. Open config/bundles.php and copy the KnpMarkdownBundle class name:

14 lines | config/bundles.php
// ... lines 1 - 2
return [
// ... lines 4 - 11
Knp\Bundle\MarkdownBundle\KnpMarkdownBundle::class => ['all' => true],
];

Now, close this file and find your terminal. We're going to run a special command that will tell us exactly what config we can pass to this bundle. Run:

php bin/console config:dump KnpMarkdownBundle

Boom! This dumps a bunch of example YAML that describes all the config that can be used to control the services in this bundle. Apparently, there's an option called parser which has an example value of markdown.parser.max... whatever that means.

Go back to the bundle's documentation and search for parser - better, search for parser:. Here we go... it says that we can apparently set this parser key to any of these 5 strings, which turn on or off different features. Copy the markdown.parser.light value: let's see if we can change the config to this value.

Look back at the YAML example at the terminal. The way you configure a bundle is via YAML. Well, you can also use PHP or XML - but usually it's done via YAML. We just need a knp_markdown key, a parser key below that and service below that. But what file should this live in?

Open up the config/packages/ directory. This is already full of files that are configuring other bundles. Create a file called knp_markdown.yaml. Inside, say knp_markdown:, enter, go in 4 spaces, then we need parser:, go in 4 spaces again and set service to markdown.parser.light:

knp_markdown:
parser:
service: markdown.parser.light

If you're wondering why I named this file knp_markdown.yaml, I did that to match this first key. But actually... the filename doesn't matter! It's the knp_markdown YAML key that tells Symfony to pass this config to that bundle. If we renamed this to i_love_harry_potter.yaml, it would work exactly the same.

So... what did this change? Well, find your browser and refresh!

Ah! An error! That was a genuine Ryan typo... but I love it!

Unrecognized option services under knp_markdown.parser. Did you mean service?

Why yes I did! Let me change that:

knp_markdown:
parser:
service: markdown.parser.light

This is one of my favorite things about the bundle config system: it's validated. If you make a typo, it will tell you. That's awesome.

Refresh now. Woh! By changing that little config value, it changed the entire class of the service object!

The point is: bundles gives you services and every bundle gives you different configuration to help you control the behavior of those services. For example, find your terminal and run:

php bin/console config:dump FrameworkBundle

FrameworkBundle is the main, core, Symfony bundle and it gives us the most foundational services, like the cache service. This dumps a huge list of config options: there's a lot here because this bundle provides many services.

Go Deeper!

You can also pass sub-level key, e.g. cache as the second argument to reduce the output:

php bin/console config:dump FrameworkBundle cache

Try it!

Of course, if you really needed to configure something, you'll probably Google and find the config you need. But how cool is it that you can run this command to see the full list of possible config? Let's try another one for TwigBundle, which we installed in the first course:

php bin/console config:dump TwigBundle

Hello Twig config! Apparently you can create a global Twig variable by adding a globals key. Oh, and this command also works if you use the root key, like twig, as the argument:

php bin/console config:dump twig

That's the exact same list.

Ok! We now know that every bundle gives us configuration that allows us to control its services.

But I'm curious about this service config we used for knp_markdown. The docs told us we could use this markdown.parser.light value. But what is that string? Is it just some random string that the bundle decided to use for a "light" parser? Actually, that string has a bit more meaning. Let's talk about the massively important "service container" next.