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

Using a Service

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.

Ok! Let's see if we can use this new service. First, some setup: in the template, remove the fun fact text and move it into GenusController by creating a new $funFact variable:

... lines 1 - 10
class GenusController extends Controller
{
/**
* @Route("/genus/{genusName}")
*/
public function showAction($genusName)
{
$funFact = 'Octopuses can change the color of their body in just *three-tenths* of a second!';
... lines 19 - 23
}
... lines 25 - 42
}

Let's make things interesting by adding some asterisks around three-tenths - Markdown should eventually turn that into italics.

Pass funFact into the template:

... lines 1 - 10
class GenusController extends Controller
{
... lines 13 - 15
public function showAction($genusName)
{
$funFact = 'Octopuses can change the color of their body in just *three-tenths* of a second!';
return $this->render('genus/show.html.twig', array(
'name' => $genusName,
'funFact' => $funFact,
));
}
... lines 25 - 42
}

And render it with the normal {{ funFact }}:

... lines 1 - 4
{% block body %}
... lines 6 - 7
<div class="sea-creature-container">
<div class="genus-photo"></div>
<div class="genus-details">
<dl class="genus-details-list">
... lines 12 - 15
<dt>Fun Fact:</dt>
<dd>{{ funFact }}</dd>
</dl>
</div>
</div>
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 23 - 40

When we refresh the browser, we have the exact same text, but with the unparsed asterisks.

Now, how the heck can we use the new markdown.parser service to turn those asterisks into italics?

Fetch the Service and Use it!

Remember: we have access to the container from inside a controller. Start with $funFact = $this->container->get('markdown.parser'). Now we have that parser object and can call a method on it. The one we want is ->transform() - pass that the string to parse:

... lines 1 - 10
class GenusController extends Controller
{
... lines 13 - 15
public function showAction($genusName)
{
$funFact = 'Octopuses can change the color of their body in just *three-tenths* of a second!';
$funFact = $this->container->get('markdown.parser')
->transform($funFact);
... lines 21 - 25
}
... lines 27 - 44
}

So.... how did I know this object has a transform() method? Well, a few ways. First, PhpStorm knows what the markdown.parser object is, so it gives me autocompletion. But you can always read the documentation of the bundle: it'll tell you how to use any services it gives us.

Ok team - time to try this out. Refresh! And hey, it's working! Ok, it's not exactly working. Open the page source: it looks like the parser is working its magic, but the HTML tags are being escaped into HTML entities.

This is Twig at work! One of the best features of Twig is that it automatically escapes any HTML that you render. That gives you free security from XSS attacks. And for those few times when you do want to print HTML, just add the |raw filter:

... lines 1 - 15
<dt>Fun Fact:</dt>
<dd>{{ funFact|raw }}</dd>
... lines 18 - 40

Refresh again: it's rending in some lovely italics.

Fetching Services the Lazy Way

One more thing! We can actually do all of this with less code. In the controller, replace $this->container->get() with just $this->get():

... lines 1 - 10
class GenusController extends Controller
{
... lines 13 - 15
public function showAction($genusName)
{
$funFact = 'Octopuses can change the color of their body in just *three-tenths* of a second!';
$funFact = $this->get('markdown.parser')
->transform($funFact);
... lines 21 - 25
}
... lines 27 - 44
}

This does the same thing as before.

Ok: here's the big, giant important takeaway so far: why do we add bundles to our app? Because bundles put more services in our container. And services are tools.

So my question now is: how can we configure these services so that they do exactly what we need them to?

Leave a comment!

29
Login or Register to join the conversation
Default user avatar
Default user avatar Roy Hochstenbach | posted 5 years ago

For some reason PHPStorm is not autocompleting it properly. When I hover over $this->container, it shows me: "Field 'container' not found in GenusController". I have both the Symfony2 plugin and PHP Annotations plugin installed and enabled. Is there some sort of a 'magic switch' to get it to work? I've marked the 'src' directory as Sources Root.

1 Reply

Hi Roy!

I think I know the problem. Also right-click on the var/cache directory and mark this as excluded. Before you did this, I bet that if you hovered over the word Controller (in extends Controller), you would have seen some message like "Multiple references found". The Controller class is cached in the var/cache directory (for performance), and this confuses PhpStorm and it quits auto-completing some things from Controller.

Let me know if this was the problem! No auto-complete is no fun :)

2 Reply
Default user avatar
Default user avatar Roy Hochstenbach | weaverryan | posted 5 years ago

Thanks for your quick response ;-)

I've tried marking the var/caches folder as Excluded, but the issue remains. I've also chosen the Invalidate Caches option, but that doesn't seem to have any effect.

Also when i append 'extends Controller' next to a class statement, it automatically includes the file "Symfony\Component\HttpKernel\Tests\Controller" instead of "Symfony\Bundle\FrameworkBundle\Controller\Controller"

Reply

Ah, interesting! Usually, when you say "extends Controller", it would offer you *both* choices - the one from HttpKernel\Tests and FrameworkBundle (since these are both called Controller). Obviously, you should use the one from FrameworkBundle. But if the one from FrameworkBundle isn't being offered as a choice, then it seems that PhpStorm doesn't see it at all (which is especially weird). Assuming the app works, then the problem is definitely with PhpStorm and you're doing good things by Invalidation the cache. I would also try to browser directly to the path - in vendor/symfony/symfony/src/Symfony/Bundle to see if the file is there. You can right-click on any directory and select "Synchronize" to (hopefully) make PhpStorm see the files in the directory (though Invalidating the Cache is similar).

Let me know if you figure it out!

Reply
Default user avatar
Default user avatar Roy Hochstenbach | weaverryan | posted 5 years ago

The Synchronize option seems to be improving the auto-complete issues at some moments.

Reply
Default user avatar
Default user avatar Igor Kalamurda | posted 5 years ago

Hello I also have a problem with autocompetion.
1. I mark var/cache like excluded
2. check for be sure class Symfony\Bundle\FrameworkBundle\Controller\Controller exist

$funFact = $this->container->get('markdown.parser')
when i write $this->container->(autocomplete show 'get' and other) then ->get( (here no autocomplete) )

Reply

Hey Igor,

Please, double check you have installed and *enabled* Symfony plugin in PhpStorm preferences for your project: press Command + , on Mac and type "Symfony" in search box. Then choose Languages and frameworks -> PHP -> Symfony and ensure you have checked all necessary boxes, especially the "Enable plugin for this project" one.
After all, try to restart PhpStorm. Actually, it helps in most weird behaviors of PhpStorm :)

P.S. Do you use the latest PhpStorm version?

Cheers!

1 Reply
Default user avatar
Default user avatar Igor Kalamurda | Victor | posted 5 years ago

"Enable plugin for this project" - helps, Thanks!

Reply

Great! Easy fix :)

Reply
Default user avatar
Default user avatar kelvin atawura | posted 5 years ago

Is there an explanation behind why $this->container->get() is the same as $this->get()

Reply

Hey Kelvin,

"$this->get()" in a controller is just a shortcut for "$this->container->get()". Open the Symfony\Bundle\FrameworkBundle\Controller\Controller::get() and you will see it yourself ;)

Cheers!

Reply
Default user avatar
Default user avatar Yan Yong | posted 5 years ago

If you are getting transform method not implement error.
Your composr download a dev version which does not have transform() method implemented.

Do the following:
1) Update composer.json under your project root directory
"knplabs/knp-markdown-bundle": "^1.5",
2) run update in your terminal
composer update knplabs/knp-markdown-bundle

Reply
Default user avatar

Hi, i have this error:
"You have requested a non-existent service "markdown.parser". I check and in my services list markdown is not present.
How can I add it?
Thank you.

Reply
Default user avatar

I solved, i installed from https://github.com/KnpLabs/....
I see now, i jumped the video 1 xd

Reply
Victor Avatar Victor | SFCASTS | Xeyos | posted 5 years ago | edited

Hey Xeyos,

Haha, I really wonder how you missed this installation ;)

Cheers!

Reply

Hey Dainis,

Why are you trying to inject "markdown.helper"? The "templating.helper.markdown" service looks like the correct one ;)

Cheers!

Reply
Default user avatar

markdown.parser seems to be having an issue getting recognized as a service and autocompleting for me. I have the symfony plugin and php annotations enabled for the project and autocomplete is working for other things.

In $this->get( ), when I start typing 'markdown' it suggests 'templating.helper.markdown', but no other services will autocomplete, but it won't find markdown.parser.

If I run ./bin/console debug:container markdown I see all the services which makes it even more confusing.

./bin/console debug:container markdown

Select one of the following services to display its information:
[0] markdown.parser.min
[1] markdown.parser.light
[2] markdown.parser.medium
[3] markdown.parser.max
[4] markdown.parser.flavored
[5] markdown.parser.parser_manager
[6] templating.helper.markdown
[7] doctrine_cache.providers.my_markdown_cache
[8] markdown.parser
>

Reply

Hey todd!

When you do `$this->get('markdown.parsers');` does it throw an exception or something?
If that's not the case and you are getting the correct object, it must be something related to PhpStorm settings, which version are you using? have you tried clearing the index? (inside settings->plugins->symfony)

Reply
Default user avatar

Yeah, no exception so Symfony is finding it when things get executed, but PHP storm still says 'Missing service'. I've cleared the Symfony plugin index, invalidated the PHP storm caches.

Using php storm 2017.2.1. It looks like others have had a similar issue, so maybe it will just start working, who knows..

Reply

Yeah, some times it gets glitched, try excluding from your project the folder `var/cache`

Reply
Default user avatar

Yeah, did that too. I've tried pretty much everything, but PHP storm can't seem to find any custom services I define.

Reply
Default user avatar

Soooo I had var/cache excluded from the beginning, per the recommendation in this course. However, when excluding var/cache PHP storm won't find any of my services. IF I cancel exclusion of that var/cache, now PHP Storm can find all my services and autocomplete works great...

Are you guys sure about excluding that directory? I'm fairly certain appDevDebugProjectContainer.xml is important for mapping services.

Or if you exclude var/cache maybe you need to manually map it?

In this SO thread it seems to be an important file, but I don't have a cache directory in my app folder, only in var/cache, should I? Or is this something symfony changed recently?
https://stackoverflow.com/q...

Reply

Oh, it depends on the Symfony version you are using, I don't know when exactly they did that change (moving cache directory from app to var directory) but if you are using Symfony 3.* you need to adjust your symfony's plugin settings

Reply

Oh, really? I can't find any mention about the "markdown.helper" in script code: https://github.com/knpunive... and even in the video of this chapter. Do you mean "markdown.parser"? Could you point me to the script/video where we use the "markdown.helper", please?

Cheers!

Reply
Default user avatar
Default user avatar Dainis Brjuhoveckis | Victor | posted 5 years ago

Sorry, my bad, I meant "markdown.parser", and it's working. The PHP Symphony auto suggest initially offered me only the "templating.helper.markdown" option for some reason so it confused me. Now it's working.

Problem solved, thread can be deleted.

Thank you :-)

Reply

You're welcome! I'm glad we clarify it. And don't worry about this thread, it could help someone ;)

Cheers!

Reply
Default user avatar
Default user avatar Blueblazer172 | posted 5 years ago

when i want to add {{ funFact }} to the dd section in show.html.twig symfony throws this error: "Variable "funFact" does not exist in genus\show.html.twig at line 17"

the fetNotesAction function looks like this:

$funFact = 'Octopuses can change the color of their body in just *three-tenths* of a second!';

$notes = [
['id' => 1, 'username' => 'AquaPelham', 'avatarUri' => '/images/leanna.jpeg', 'note' => 'Octopus asked me a riddle, outsmarted me', 'date' => 'Dec. 10, 2015'],
['id' => 2, 'username' => 'AquaWeaver', 'avatarUri' => '/images/ryan.jpeg', 'note' => 'I counted 8 legs... as they wrapped around me', 'date' => 'Dec. 1, 2015'],
['id' => 3, 'username' => 'AquaPelham', 'avatarUri' => '/images/leanna.jpeg', 'note' => 'Inked!', 'date' => 'Aug. 20, 2015'],
];
$data = [
'notes' => $notes,
'funFact' => $funFact,
];

return new JsonResponse($data);

Reply

Hey Hannes,

I suppose you forget to pass funFact variable in GenusController::showAction(), which actually renders the `genus/show.html.twig` template ;)

Cheers!

Reply
Default user avatar
Default user avatar Blueblazer172 | Victor | posted 5 years ago

yep :P that was the problem thanks :)

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
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0" // v3.1.3
    }
}
userVoice