Buy

Tagging Services (and having Fun!)

We need to somehow tell Twig about our fun new Twig extension. To do that, first, register it as a service. The name doesn't matter, so how about app.markdown_extension. Set the class, but skip arguments: we don't have any yet, so this is optional:

15 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 10
app.markdown_extension:
class: AppBundle\Twig\MarkdownExtension
... lines 13 - 15

Now, this service is a bit different: it's not something that we intend to use directly in our controller, like app.markdown_transformer. Instead, we simply want Twig to know about our service. We somehow need to raise our hand and say:

Oh, oh oh! This service is special - this service is a Twig Extension!

We do that by adding a tag. The syntax is weird, so stay with me: Add tags:, then under that a dash and a set of curly-braces. Inside, set name to twig.extension:

15 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 10
app.markdown_extension:
class: AppBundle\Twig\MarkdownExtension
tags:
- { name: twig.extension }

And that's it.

Real quick - make sure it works. Refresh! Boom! We see a pretty awesome-looking upper-case string.

What are these Tags???

Tags are the way to hook your services into different parts of the core system. When Symfony creates the twig service, it looks for all services in the container that are tagged with twig.extension. It then configures these as extensions on twig.

Google for "Symfony dependency injection tags": there's an awesome reference section on Symfony.com called The Dependency Injection Tags. It lists every tag that can be used to hook into core Symfony. And if you're tagging a service, well, you're probably doing something really cool.

For example, if you want to register an event listener and actually hook into Symfony's boot process, you create a service and then tag it with kernel.event_listener. You won't memorize all of these: you just need to understand their purpose. Someday, you'll read some docs or watch a tutorial here that will tell you to tag a service. I want you to understand what that tag is actually doing.

Finish the Extension

Let's finish our extension: we need to parse this through markdown. We find ourselves in a familiar position: we're inside a service and need access to some other service: MarkdownTransformer. Dependency injection!

Add public function __construct() with a MarkdownTransformer argument. I'll hold option+enter and select "Initialize fields" as a shortcut. Again, this just added the property for me and assigned it in __construct():

33 lines src/AppBundle/Twig/MarkdownExtension.php
... lines 1 - 6
class MarkdownExtension extends \Twig_Extension
{
private $markdownTransformer;
public function __construct(MarkdownTransformer $markdownTransformer)
{
$this->markdownTransformer = $markdownTransformer;
}
... lines 15 - 31
}

Go Deeper!

Watch our PhpStorm Course to learn about these great shortcuts.

In parseMarkdown(), return $this->markdownTransformer->parse() and pass it $str:

33 lines src/AppBundle/Twig/MarkdownExtension.php
... lines 1 - 6
class MarkdownExtension extends \Twig_Extension
{
... lines 9 - 22
public function parseMarkdown($str)
{
return $this->markdownTransformer->parse($str);
}
... lines 27 - 31
}

The last step is to update our service in services.yml. Add arguments: ['@app.markdown_transformer']:

16 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 10
app.markdown_extension:
... lines 12 - 14
arguments: ['@app.markdown_transformer']

Refresh! And it's working. Now, let me show you a shortcut.

Leave a comment!

  • 2017-08-28 Diego Aguiar

    I believe you are running on Symfony 3.3, right? The dependency injection service has changed a bit in that version.
    What you need to do is create an alias for MarkdownParserInterface, like this:


    // services.yml
    services:
    Knp\Bundle\MarkdownBundle\MarkdownParserInterface: '@markdown.parser'

    And now, Symfony will be able to autowire any service that depends on MarkdownParserInterface

    if you want to learn more about how all this works, you want watch our fabulous tutorial of Symfony 3.3 https://knpuniversity.com/s...

    Cheers!

  • 2017-08-26 Eni Shima

    I have done all that the tutorial have said but when we try to add the twig extension , it gives error . The error is as below : PHP 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 /var/www/html/aqua_note/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php:297
    Stack trace:
    #0 /var/www/html/aqua_note/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php(223): Symfony\Component\DependencyInjection\Compiler\AutowirePass->autowireMethod(Object(ReflectionMethod), Array)
    #1 /var/www/html/aqua_note/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php(146): Symfony\Component\DependencyInj in /var/www/html/aqua_note/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php on line 297

    .
    I think the problem is on the MarkdownExtension.php class , but i haven't guess yet what it is . anyway this is are my 2 classes :https://pastebin.com/7vRnaKnp and https://pastebin.com/pWAhHGnY . Please can you help me

  • 2017-08-26 Diego Aguiar

    This is strange, your services.yml file look's good (or I just can't detect the error), try to not importing it and see if the error goes away, just to be 100% sure this file is the problem, if that's not the case, check that all classes exists, and their namespaces are correct.

  • 2017-08-25 Eni Shima

    I have removed the "parameter" but it haven change . Here is the file services.yaml :https://pastebin.com/QW05QRF5 .

  • 2017-08-25 Diego Aguiar

    Could you upload it to https://pastebin.com/ or any other platform, please? It's hard to see the problem and it lost the identation.

    As a first guess, I think removing the first line "parameters:" might fix it

    Cheers!

  • 2017-08-25 Eni Shima

    Hi Diego Aguiar .Yes here is my services.yml . I added public: true in app.markdown_extension: but the problem still exist

    # Learn more about services, parameters and containers at
    # https://symfony.com/doc/cur...
    parameters:
    #parameter_name: value

    services:
    # default configuration for services in *this* file
    _defaults:
    # automatically injects dependencies in your services
    autowire: true
    # automatically registers your services as commands, event subscribers, etc.
    autoconfigure: true
    # this means you cannot fetch services directly from the container via $container->get()
    # if you need to do this, you can override this setting on individual services
    public: false

    # makes classes in src/AppBundle available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    AppBundle\:
    resource: '../../src/AppBundle/*'
    # you can exclude directories or files
    # but if a service is unused, it's removed anyway
    exclude: '../../src/AppBundle/{Entity,Repository,Tests}'

    # controllers are imported separately to make sure they're public
    # and have a tag that allows actions to type-hint services
    AppBundle\Controller\:
    resource: '../../src/AppBundle/Controller'
    public: true
    tags: ['controller.service_arguments']

    # add more services, or override services that need manual wiring
    # AppBundle\Service\ExampleService:
    # arguments:
    # $someArgument: 'some_value'

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

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

  • 2017-08-25 Diego Aguiar

    Hey Eni Shima

    Look's like your services.yml file has a syntax error, could you show it here? so I can help you debugging

    Cheers!

  • 2017-08-25 Eni Shima

    Hi all . I have a problem when adding twig extension . The project give me some error as below :

    (3/3) FileLoaderLoadException
    The file "/var/www/html/aqua_note/app/config/services.yml" does not contain valid YAML in /var/www/html/aqua_note/app/config/services.yml (which is being imported from "/var/www/html/aqua_note/app/config/config.yml").
    in FileLoader.php (line 179)
    at FileLoader->doImport('/var/www/html/aqua_note/app/config/services.yml', null, false, '/var/www/html/aqua_note/app/config/config.yml')
    in FileLoader.php (line 101)
    at FileLoader->import('services.yml', null, false, '/var/www/html/aqua_note/app/config/config.yml')
    in YamlFileLoader.php (line 184)

    Please does anyone help me

  • 2017-08-09 Victor Bocharsky

    Hahaha, thanks :)

  • 2017-08-09 Mike

    Thanks Victor for your blazing fast answer! You're a tutorial god!

  • 2017-08-09 Victor Bocharsky

    Hey Mike,

    Yes, it's a new feature. Actually, Twig 2.x allow to autoregister Twig extensions, so probably you're on the new Twig 2.x :) If so - you don't need to register it manually now. Btw, we have written a blog post how to avoid such kind of problems when you're on Symfony 3.3: http://knpuniversity.com/bl... - I think it could be interesting for you.

    Cheers!

  • 2017-08-09 Mike

    I have SF 3.3.6 and it even works without registering it inside services.yml. Is autoloading twig extensions a new feature?
    If I try to register it via services.yml it works as well. So isn't it necessary anymore to include the twig extension in services.yml?