Buy

Adding a Twig Extension + DI Tags

Because the $funFact needs to be parsed through markdown, we have to pass it as an independent variable into the template. You know what would be way cooler? If we could just say genus.funFact|markdown. Ok, actually we can do this already: the KnpMarkdownBundle comes with a filter called markdown:

42 lines app/Resources/views/genus/show.html.twig
... lines 1 - 4
{% block body %}
<h2 class="genus-name">{{ genus.name }}</h2>
<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>{{ genus.funFact|markdown }}</dd>
... lines 18 - 19
</dl>
</div>
</div>
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 25 - 42

Remove the app.php from the URL and refresh. VoilĂ ! This parses the string to markdown. Well, it doesn't look like much - but if you view the HTML, there's a p tag from the process.

Let's make this a little more obvious while we're working. Open up the Genus entity and find getFunFact(). Temporarily hack in some bold markdown code:

115 lines src/AppBundle/Entity/Genus.php
... lines 1 - 11
class Genus
{
... lines 14 - 86
public function getFunFact()
{
return '**TEST** '.$this->funFact;
}
... lines 91 - 113
}

Ok, try it again.

Nice! The bold TEST tells us that the markdown filter from KnpMarkdownBundle is working.

Ready for me to complicate things? I always do. The markdown filter uses the markdown.parser service from KnpMarkdownBundle - it does not use our app.markdown_transformer. And this means that it's not using our caching system. Instead, let's create our own Twig filter.

Creating a Twig Extension

To do that, you need a Twig "extension" - that's basically Twig's plugin system. Create a new directory called Twig - and nope, that name is not important. Inside, create a new php class - MarkdownExtension:

24 lines src/AppBundle/Twig/MarkdownExtension.php
... line 1
namespace AppBundle\Twig;
class MarkdownExtension extends \Twig_Extension
{
... lines 7 - 22
}

Remember: Twig is its own, independent library. If you read Twig's documentation about creating a Twig extension, it will tell you to create a class, make it extend \Twig_Extension and then fill in some methods.

Use the "Code"->"Generate" menu - or cmd+n - and select "Implement Methods". The one method you must have is called getName(). It's also the most boring: just make it return any unique string - like app_markdown:

24 lines src/AppBundle/Twig/MarkdownExtension.php
... lines 1 - 4
class MarkdownExtension extends \Twig_Extension
{
... lines 7 - 18
public function getName()
{
return 'app_markdown';
}
}

To add a new filter, go back to the "Code"->"Generate" menu and select "Override Methods". Choose getFilters():

24 lines src/AppBundle/Twig/MarkdownExtension.php
... lines 1 - 4
class MarkdownExtension extends \Twig_Extension
{
public function getFilters()
{
... lines 9 - 11
}
... lines 13 - 22
}

Here, you'll return an array of new filters: each is described by a new \Twig_SimpleFilter object. The first argument will be the filter name - how about markdownify - that sounds fun. Then, point to a function in this class that should be called when that filter is used: parseMarkdown:

24 lines src/AppBundle/Twig/MarkdownExtension.php
... lines 1 - 4
class MarkdownExtension extends \Twig_Extension
{
public function getFilters()
{
return [
new \Twig_SimpleFilter('markdownify', array($this, 'parseMarkdown'))
];
}
... lines 13 - 22
}

Create the new public function parseMarkdown() with a $str argument. For now, just strtoupper() that guy to start:

24 lines src/AppBundle/Twig/MarkdownExtension.php
... lines 1 - 4
class MarkdownExtension extends \Twig_Extension
{
... lines 7 - 13
public function parseMarkdown($str)
{
return strtoupper($str);
}
... lines 18 - 22
}

Cool? Update the Twig template to use this:

42 lines app/Resources/views/genus/show.html.twig
... lines 1 - 4
{% block body %}
<h2 class="genus-name">{{ genus.name }}</h2>
<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>{{ genus.funFact|markdownify }}</dd>
... lines 18 - 19
</dl>
</div>
</div>
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 25 - 42

Our Twig extension is perfect... but it will not work yet. Refresh. Huge error:

Unknown markdownify filter.

Twig doesn't automatically find and load our extension. Somehow, we need to say:

Hey Symfony? How's it going? Oh, I'm good. Listen, when you load the twig service, can you add my MarkdownExtension to it?

How are we going to do this? With tags.

Leave a comment!