Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Autowiring Madness

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Ooh, bonus feature! In services.yml, remove arguments and instead just say autowire: true:

... lines 1 - 5
services:
... lines 7 - 10
app.markdown_extension:
... lines 12 - 14
#arguments: ['@app.markdown_transformer']
autowire: true

Refresh again. It still works! But how? We didn't tell Symfony what arguments to pass to our constructor? What madness is this!? With autowire: true, Symfony reads the type-hints for each constructor argument:

... lines 1 - 6
class MarkdownExtension extends \Twig_Extension
{
... lines 9 - 10
public function __construct(MarkdownTransformer $markdownTransformer)
... lines 12 - 31
}

And tries to automatically find the correct service to pass to you. In this case, it saw the MarkdownTransformer type-hint and knew to use the app.markdown_transformer service: since that is an instance of this class. You can also type-hint interfaces.

Tip

The autowiring logic has changed in Symfony 3.3 and higher. For more info, we have a tutorial! https://knpuniversity.com/screencast/symfony-3.3/autowiring-logic

This doesn't always work, but Symfony will give you a big clear exception if it can't figure out what to do. But when it does work, it's a great time saver.

Auto-Escaping a Twig Filter

The HTML is still being escaped - I don't want to finish before we fix that! We could add the |raw filter... but let's do something cooler. Add a third argument to Twig_SimpleFilter: an options array. Add is_safe set to an array containing html:

... lines 1 - 6
class MarkdownExtension extends \Twig_Extension
{
... lines 9 - 15
public function getFilters()
{
return [
new \Twig_SimpleFilter('markdownify', array($this, 'parseMarkdown'), [
'is_safe' => ['html']
])
];
}
... lines 24 - 33
}

This means it's always safe to output contents of this filter in HTML. Refresh one last time. Beautiful.

Where now!?

Oh my gosh guys! I think you just leveled up: Symfony offense increased by five points. Besides the fact that a lot more things will start making sense in Symfony, you also know everything you need to start organizing your code into service classes - that whole service-oriented architecture thing I was talking about earlier. This will lead you to wonderful applications.

There's really nothing that we can't do now in Symfony. In the next courses, we'll use all this to master new tools like forms and security. Seeya next time!

Leave a comment!

28
Login or Register to join the conversation
Alexandre P. Avatar
Alexandre P. Avatar Alexandre P. | posted 3 years ago

In this chapter, 2 code portions are different. Why ?

markdownTransformer = $markdownTransformer;
}

public function getFilters()
{
return [
new \Twig_SimpleFilter('markdownify', array($this, 'parseMarkdown'))
];
}

public function parseMarkdown($str)
{
return $this->markdownTransformer->parse($str);
}

public function getName()
{
return 'app_markdown';
}
}

then
markdownParser = $markdownParser;
}

public function getFilters()
{
return [
new \Twig_SimpleFilter('markdownify', array($this, 'parseMarkdown'), [
'is_safe' => ['html']
])
];
}

public function parseMarkdown($str)
{
return $this->markdownParser->parse($str);
}

public function getName()
{
return 'app_markdown';
}
}

markdownTransformer ---> markdownParser.
Without explications ? Maybe a relic ?

Reply

Hey Alexandre P.

I don't know what are you talking about... cough, cough, I just fixed it, we were showing a wrong code block... cough, cough

Thanks for informing us. Cheers!

1 Reply

Hey there there

are there any plans to delve deeper into this topic? (SOA in symfony) ? It was a wonderful topic, yet we just scratched the surface and it felt a VERY important topic to master.

Reply

I totally agree on both points. But I'm not sure *what* else might be useful to cover. In its simplest sense, it's "put logic into services!" and this tutorial showed the mechanics for doing that. If we can think of a few concrete items that would be helpful, then yea - let's do a tutorial on it :)

Reply

I'll give it some thought and let you know, as indeed, I think this should be explained further, deeply explored, maybe a tutorial with a more hands on, real life situation would help, who knows. Disquss integration for a blog, or a simple "invoice" manager (not a billing system, but a invoice generator, where you mark them as pending, unpaid, canceled, paid, offer printable version, pdf export, send through email, whatever, just blabling on the-spot ideas)

Reply
Default user avatar
Default user avatar 3amprogrammer | posted 4 years ago

I am exploring https://github.com/orocrm/crm and I am confused about the dir structure. What the heck are DependencyInjection and Model directories in Bundles? Do we have any vid on this topic here?

Reply

Hey,

Ah, DependencyInjection is the most mystery dir in bundle structure at first sight :). First of all you need to understand what is a Symfony bundle. You could also check this course, which explains basic things about this directory. In short, this helps to plug in third-party bundles in your application (imports services, routes, configuration, etc.).

Usually, model is a simple data class which you used in code. It looks like an entity, but doesn't store in DB (doesn't handle with Doctrine). Frequently, in Symfony projects, models uses in form type as data classes.

Cheers!

1 Reply
Default user avatar
Default user avatar Eduardo Guglielmotti | posted 4 years ago

Hello
In the course the course Symfony: Level up with Services and the Container observed that it does not match the Code of the course downloaded.
For example it speaks of the entity Genus and shows and edits its content but nda to do with the development that I followed in the 7 chapters.
Is there something wrong I've done or should I download other content?
regards

Reply

Hey Eduardo Guglielmotti

When you download the code, it contains 2 folders (start and finish).
The start folder is for the very beginning of this course, it doesn't contain the code of further chapters
And the finish folder contains every line wrote in this course.

I hope this clarifies the thing a bit :)

Have a nice day.

Reply
Default user avatar
Default user avatar Eduardo Guglielmotti | MolloKhan | posted 4 years ago

OK thanks

Reply
Default user avatar
Default user avatar Eduardo Guglielmotti | posted 4 years ago

I'm clearing that I'm testing on symfony 3.3

Reply
Default user avatar
Default user avatar Jonah Mandli | posted 4 years ago

I'm having some trouble using the new Symfony 3.3 autowiring feature with my entity repositories. Currently, I am trying to pass in instances of Doctrine\ORM\EntityManagerInterface, Doctrine\ORM\Mapping\ClassMetadata, and a custom boolean parameter I am manually configuring. However, when I attempt to use this repository I receive the following error:
Too few arguments to function AppBundle\Entity\UserRepository::__construct(), 2 passed in /vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php on line 68 and exactly 3 expected
When I remove this custom parameter, the repository works again. Any hints?

Reply

Hey Jonah Mandli!

Yea..... this isn't very easy to do right now :). Entity repositories cannot be autowired easily. It's actually something I'm working on *right* now in DoctrineBundle - https://github.com/symfony/... - and hopefully it will be merged!

Currently, the problem is that the entity manager (via $em->getRepository()) is *actually* the "thing" that is responsible for creating your repository. You *cannot* configure it like a normal service (even if you *could* get the __construct arguments correct) because then you might have *two* instances of your repository "floating" around - (1) an instance if you fetch it from the container and (2) another instance of you (or someone) calls $em->getRepository().

So, if you want to register your repositories as services, you must use this factory method - see the answer here: https://stackoverflow.com/q...

The downside is that this is a very manual process :/. That's what I'm hoping we can fix in the next version of DoctrineBundle. For now, in my projects, I often inject the EntityManager into my services and use $this->em->getRepository() to get my repository instead of trying to inject *just* my repository directly. And how do I pass extra constructor arguments to my repositories? I don't! Usually (but not always) if my repository needs more configuration, then I might be putting too much in my repository. I instead create a separate service (e.g. BlogPostManager instead of BlogPostRepository), pass my custom config and the EM into that service, and do my work :).

So, that's "bad" news for you now... but hopefully we will have good news in the future!

Cheers!

Reply
Default user avatar

Thanks for the quick response! I'll keep my ear to the ground on any new developments for this feature.

Reply

i have an autowiring error i couldnt fix it someone please help me?

(1/1)
AutowiringFailedException

Cannot
autowire service "AppBundle\Service\MarkdownTransformer": argument
"$markdownParser" of method "__construct()" references interface
"Knp\Bundle\MarkdownBundle\MarkdownParserInterface" but no such service
exists. You should maybe alias this interface to one of these existing
services: "markdown.parser.min", "markdown.parser.light",
"markdown.parser.medium", "markdown.parser.max",
"markdown.parser.flavored".

i have no idea how to change my services.yml it looks like this


app.markdown_transformer:
class: AppBundle\Service\MarkdownTransformer
public: true
arguments: ['@markdown.parser', '@doctrine_cache.providers.my_markdown_cache']

app.markdown_extension:
class: AppBundle\Twig\MarkdownExtension
public: true
tags:
- { name: twig.extension }
arguments: ['@app.markdown_transformer']
Reply

i solved my problem if anybody have same error add the lines below add this

app.markdown_transformer:
class: AppBundle\Service\MarkdownTransformer
public: true
arguments: ['@markdown.parser', '@doctrine_cache.providers.my_markdown_cache']
# add this
AppBundle\Service\MarkdownTransformer: '@app.markdown_transformer'

app.markdown_extension:
class: AppBundle\Twig\MarkdownExtension
public: true
tags:
- { name: twig.extension }
arguments: ['@app.markdown_transformer']

Reply

Hey msthzn !

I think I know the cause, and it can be confusing :). In Symfony 3.3, we (meaning Symfony) added some extra, default configuration to services.yml which enabled a bunch of autowiring features, but this tutorial was recorded before Symfony 3.3. So basically, your version of the code has these new, optional autowiring features enabled, which is making things not match what we're doing in the tutorial. We *are* doing some autowiring (obviously, this chapter is about autowiring), but the new config in services.yml in Symfony 3.3 takes it to another level :). We have a blog post about this: https://knpuniversity.com/b.... Basically, if you want to avoid issues and follow along exactly, you can just comment-out those new items in services.yml.

And don't worry too much about the new stuff while going through this tutorial! We talk about it in this tutorial: https://knpuniversity.com/s.... And at the end of the year when Symfony 4 comes out, we will talk A LOT more about all of this cool stuff :).

Cheers!

Reply
Default user avatar
Default user avatar Felipe Gusmao | posted 4 years ago

While interviewing for few jobs I was asked about implementing a service bus. I understand DIP and services. However, I cant see how a service bus could be implemented. Do you guys from Knp, know of an example of service bus and its use case. Possibly, using RabbitMQ?

Reply
Default user avatar
Default user avatar Praxitelis Kourtellos | posted 4 years ago

Any idea about this error:

Fatal error: Uncaught Symfony\Component\DependencyInjection\Exception\AutowiringFailedException: Cannot autowire service "AppBundle\Service\MarkdownTransformer": argument "$markdownParser" of method "__construct()" references interface "Knp\Bundle\MarkdownBundle\MarkdownParserInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "markdown.parser.min", "markdown.parser.light", "markdown.parser.medium", "markdown.parser.max", "markdown.parser.flavored". in C:\Users\pkourtellos\Desktop\Php stuff\aqua_note\vendor\symfony\symfony\src\Symfony\Component\DependencyInjection\Compiler\AutowirePass.php:291
Stack trace:
#0 C:\Users\pkourtellos\Desktop\Php stuff\aqua_note\vendor\symfony\symfony\src\Symfony\Component\DependencyInjection\Compiler\AutowirePass.php(223): Symfony\Component\DependencyInjection\Compiler\AutowirePass->autowireMethod(Object(ReflectionMethod), Array)
#1 C:\Users\pkourtellos\Desktop\Php stuff\aqua_note\vendor\symfony\symfony\src\Symfony\Component\Dependen in C:\Users\pkourtellos\Desktop\Php stuff\aqua_note\vendor\symfony\symfony\src\Symfony\Component\DependencyInjection\Compiler\AutowirePass.php on line 291

I've even taken out autowire=true from services.yml

Reply

Hey Praxitelis Kourtellos

I believe you are trying to autowire the service "app.markdown_transformer", but that's not correct, you should autowire the other service "app.markdown_extension", if I'm wrong, can you show me your services.yml file ?

Cheers!

Reply
Default user avatar

I have the same problem. And I believe it is only for the latest symphony 3.3.2.

Reply

Hey Wei Dai

Symfony 3.3 changed a lot of things about autowiring and the container, we have a dedicated tutorial just in case you want to go deeper https://knpuniversity.com/s...

If you run "bin/console debug:container --types" you can see all the classes that your container knows about, so you can find which ones are for the MarkdownParser, and then, define an alias that you can inject into your service.
You can filter the list by running this: "bin/console debug:container --types | grep Markdown "

Cheers!

Reply
Default user avatar

After run the console command, the container doesn't know any classes from the Knp markdown bundle. I think the reason is because the bundle is built for previous symfony version, and all service makred as non-public?
The container can only know the customer classes we defined.

Reply
Default user avatar

solved. Thanks for the tutorial.

Reply

Hey there, I noticed that sometimes you use `array()` syntax for PHP arrays, and sometimes (most of the time) square brackets notation. Do you have some logic for using one over the other?

Reply

Yo Serge!

Good catch! Short array syntax, i.e. square brackets, is just a new syntax since PHP 5.4 and we're trying to use it in our all new screencasts. So it's just a matter of habit if you see Ryan writes old syntax sometimes. But they are both do the same.

Cheers!

Reply

Thanks victor!

That's what I thought. Personally, I like the square brackets notation syntax much more and the only valid reason to use `array()` for me was if production PHP version is < 5.4. Fortunately, it's a rare situation nowadays. :)

Reply

Yes, absolutely agree! Well, I think old array syntax occurred sometimes due to copy/paste some example code from docs and when you're lazy enough to tweak it :D

Btw, I like "[]" because it's the same syntax which is used in JS, so double win!

Cheers!

1 Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}