If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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
:
... 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:
... 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.
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
:
... 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
:
... 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()
:
... 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
:
... 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:
... 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:
... 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.
Starting with 3.2 Symfony deprecated getName() function in Twig_Extension class, now it's enough to just override the getFilters() method. More info here http://symfony.com/doc/curr....
Woohoo! You're right! That's an awesome improvement.
To clarify, starting in Twig 1.26 (released October 2nd, 2016), you do not need the getName
method anymore. So when you go to "Implement Methods", getName
will not show up anymore. And that's totally fine - just skip it (i.e. don't add it).
Thanks Vadim for the comment!
Hey Tin,
No, it should be available out-of-the-box. Do you choose the "Implement methods" option in "Generate" menu? Please, also double check that your `MarkdownExtension` extends `\Twig_Extension` class. Do you use the latest PhpStorm version? Probably you just need to restart PhpStorm - it should help in most cases.
Cheers!
Hi Victor, Thank you for your reply but I still didn't have that getName() method from the "Implement methods" in "Generated" menu.
On the other hand, the override methods is fine.
My PhpStorm version is up to date and I did try to restart it.
I decided to ignore this small bug and move on!
Ah, that's weird. If you add vendor dir to excluded folders - I think it may cause this issue too. But if not, I can suggest you to re-index your project. AFAIK, you need to choose "File" -> "Invalidate Caches". I hope it will help.
Cheers!
Same problem with PHPStorm 10 on Windows. Invalidating Cache did not work. Strange, but not a show stopper.
EDITED
Oh wait, not strange. I probably have a later version of twig which already implements the getName method so it shows up in overrides instead.
Hey Maria,
Ah, it makes sense! Thanks for sharing this with us. Agnes , please, check Maria's answer about your problem. I bet you will find this method in "Override" menu.
Cheers!
// 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
}
}
Hi Ryan,
in my PHPStorm, I can not see the Twig_ExtensionInterface in the implemented methods (Cmd+N menu). Do I need to install any plugin to see it?