Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Configuring Bundles

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

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

... 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:

... 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:

... 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.

Leave a comment!

20
Login or Register to join the conversation
Favian P. Avatar
Favian P. Avatar Favian P. | posted 2 years ago

I followed every single step and when I was die dumping the markdown object the parser was still set to max, I figured out it was a cache problem, so if you find yourself in the same spot with me run:
./bin/console cache:clear
and it should work

8 Reply

Thank you very much for sharing with us! I had exactly this problem.

Reply

Hey Pill,

Thank you for confirming this solution helped you!

Cheers!

1 Reply
Default user avatar
Default user avatar Oblong of Orange | Favian P. | posted 1 year ago

Took a little while for this tip to work for me, I cleared the cache, and after it completed refreshed the page a number of times, but it still returned markdown.parser.max each time. When I calmed down and did nothing for moment (around 30 seconds or so) and then refreshed, it was using markdown.parser.light as expected. Thank you!

Reply
Jewel F. Avatar

Thanks for tip

Reply

Hey Poputa,

Thanks for this tip! First of all, it depends in which Symfony env you're... in prod you have to clear the cache first to see any changes, but in dev it should not be necessary. Yeah, sometimes you have to do it in dev, especially when you use a virtual machine or Docker, I just noticed this a few times. So, in any weird situation it's always a good idea to try to clear the cache first. If it does not help - then look into it deeper.

Cheers!

Reply
Niclas H. Avatar
Niclas H. Avatar Niclas H. | posted 1 year ago

Just incase anybody is as retarded as I am:
Running ./bin/console config:dump KnpMarkdownBundle only dumps the default configuration for that service.
In the video, they validated that the config was updated to use markdown.parser.light by adding dd($markdownParser) and checking the browser to see which class is being used.

Edit: As Diego points out below, the two commands are:

symfony console debug:config KnpMarkdownBundle (your current config)
symfony console config:dump KnpMarkdownBundle (the default config)

1 Reply

Hey Niclas H.

Lol - thank's for sharing it, you can also run bin/console dump:config to check your current configuration

Cheers!

Reply
Ruslan I. Avatar
Ruslan I. Avatar Ruslan I. | posted 7 months ago

Why was this autowiring system invented?
We could just write use statement on top of the file and then inside of a method just instantiate manually an object we want and configure it how we want via arguments instead of config files?
I'm just wondering, because at first I didn't like this system, but I get used to it

Reply

Hey Phy!

Well, first of all, in Symfony services are created by Dependency Injection Container (DIC) that knows how to create them. And it create them only on request. I.e. if during the request the service was never been called from the container - it also was never created. Yes, you can instantiate services manually in your code, it would mean you need to pass all the required arguments there, including injection other services or passing some parameters... but why do it manually when DIC may do it for you? All you need to do - teach DIC how to create the service, and then in the code just call that service, that's it. Also, DIC controls that only one instance of the service is created during the request. It means that if you call the same service in a few separate spots of your code - you will get the exact same instance of the service in all those spots. Manually, it would be difficult for you to do this if you instantiate it manually, because sometimes it's difficult to pass the created instance to all the spots correctly without violation code quality.

I hope it clarify things for you now!

Cheers!

1 Reply
Covi A. Avatar
Covi A. Avatar Covi A. | posted 1 year ago

I am trying to create new bundle in symfony 5.3*. but i can't did it. because when i try to create a new bundle first of all i need generate:bundle command. but i could not find it inside bin/console.
bin/console generate:bundle command gave me this error
There are no commands defined in the "generate" namespace.

You may be looking for a command provided by the "SensioGeneratorBundle" which is currently not installed. Try running "composer require sensio/generator-bundle

".
then i try to install composer require sensio/generator-bundle. after this command it's gave me this error

mono@mono:~/Projects/apptest$ composer require sensio/generator-bundle
Using version ^3.1 for sensio/generator-bundle
./composer.json has been updated
Running composer update sensio/generator-bundle
Loading composer repositories with package information
Restricting packages listed in "symfony/symfony" to "5.3.*"
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

Problem 1
- sensio/generator-bundle[3.1.0, ..., v3.1.7] require symfony/framework-bundle ~2.7|~3.0 -> found symfony/framework-bundle[v2.7.0, ..., v2.8.52, v3.0.0, ..., v3.4.47] but it conflicts with your root composer.json require (5.3.*).
- Root composer.json requires sensio/generator-bundle ^3.1 -> satisfiable by sensio/generator-bundle[3.1.0, ..., v3.1.7].

Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

so i can't find any right way to generate a new bundle. could you please give me any suggestion what can i do for create a new bundle in symfony 5.3* version. and also please tell me what wrong i am doing.
thank you

Reply

Hey Monoranjan,

Yes, unfortunately, there's no a command that would generate a bundle for you, today you're trying to create the bundle yourself. Just literally open any bundle you're using and steal some boilerplate code. The bundle's structure you can find here: https://symfony.com/doc/cur...

That sensio/generator-bundle is abandoned and does not maintain anymore, which means you can't use it for Symfony 4 / 5. Since Symfony 4 we get rid of splitting your project code into bundles, so only third-party bundles are left that are supposed to be reusable in more than one project and should be installed via Composer in vendor/ dir. So, in theory you need to create a new bundle very rare, that's why there's no generate command so far, and yes, it's kinda requires some experience to work with the bundles if you want to create a new one.

So, following our tutorial about creating reusable bundles: https://symfonycasts.com/sc... and the docs I linked above you should be able to create a bundle I think. If you get stuck somewhere on that tutorial - let us know in the comments below the video and we will try to help you.

I hope this helps!

Cheers!

Reply

What is the difference between ./bin/console config:dump [name] and ./bin/console config:dump-reference [name]? In the documents I see this alternative command popping up.

Edit: I just tested this command in my project, and it seems it is just an alias for the same thing.

Reply

Hey Robin,

Those are the exact same command :) Symfony console has a really cool feature when you can make sorter command names, so the original command name is "config:dump-reference", but you can make it shorter and type only "config:dump-ref", or even shorter like "config:dump", or go crazy and type: "conf:dump", or even "c:d" :)

So, literally, to call that "config:dump-reference" - you can just type "bin/console c:d" and if Symfony won't find more than one command that matches your short name - it will call it. But sometimes Symfony may find more than one command and then it will fail asking you to be more specific.

To be sure about the full command name that is called when you call a short version of it - you can add "--help" option, e.g. "bin/console config:dump --help" will show you that behind the scene it calls "config:dump-reference" command.

I hope this helps ;)

Cheers!

2 Reply
Jewel F. Avatar
Jewel F. Avatar Jewel F. | posted 2 years ago

I have added the knp_markdown.yaml file within the config->packages directory and added the "service: markdown.parser.light" by following you. But It do nothing, even I do mistakes on "services" instead of "service".

Reply

Hey Md,

I see that clearing the cache helped you. I just curious if you use any virtualization tool like Docker, etc. Probably you loaded the website in prod mode instead? Are you sure you were in dev mode? Only in dev mode Symfony rebuilds the cache on every page reload, for prod you need to clear the cache to see changes.

Cheers!

Reply
Jewel F. Avatar
Jewel F. Avatar Jewel F. | victor | posted 2 years ago | edited

Thanks victor for repying me.
I was on prod mode, and I also use Docker.

Reply

Hey Md,

Yeah, then you have to clear the cache manually in prod to see changes. If you use Docker - even in dev mode it's always a good idea to clear the cache first to see if this helps, as filesystem is shared between your host machine and containers.

Cheers!

Reply
Jewel F. Avatar
Jewel F. Avatar Jewel F. | Jewel F. | posted 2 years ago | edited

Problem solved by following Jewel F.

Reply

Hey Md,

Thanks for confirming this helped you! https://symfonycasts.com/sc...

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.3.0 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "symfony/asset": "5.0.*", // v5.0.11
        "symfony/console": "5.0.*", // v5.0.11
        "symfony/debug-bundle": "5.0.*", // v5.0.11
        "symfony/dotenv": "5.0.*", // v5.0.11
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.0.*", // v5.0.11
        "symfony/monolog-bundle": "^3.0", // v3.6.0
        "symfony/profiler-pack": "*", // v1.0.5
        "symfony/routing": "5.1.*", // v5.1.11
        "symfony/twig-pack": "^1.0", // v1.0.1
        "symfony/var-dumper": "5.0.*", // v5.0.11
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.0.*" // v5.0.11
    },
    "require-dev": {
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/profiler-pack": "^1.0" // v1.0.5
    }
}