KnpMarkdownBundle & its Services
Scroll down to the "Why do Asteroids Taste like Bacon" article and click to see it. Here's the goal: I want the article body to be processed through markdown. Of course, Symfony doesn't come with a markdown-processing service... but there's probably a bundle that does! Google for KnpMarkdownBundle and find its GitHub page.
Installing a Bundle
Let's get this installed: copy the composer require
line. Then, move over to your terminal, paste and... go!
composer require "knplabs/knp-markdown-bundle:^1.9"
Notice that this is a bundle: you can see it right in the name. That means it likely contains two things: First, of course, some PHP classes. And second, some configuration that will add one or more new services to our app!
And.... installed! It executed one recipe, which made just one change:
git status
Yep! It updated bundles.php
, which activates the bundle:
// ... lines 1 - 2 | |
return [ | |
// ... lines 4 - 10 | |
Knp\Bundle\MarkdownBundle\KnpMarkdownBundle::class => ['all' => true], | |
]; |
Finding the new Service
So... what's different now? Run:
./bin/console debug:autowiring
and scroll to the top. Surprise! We have a new tool! Actually, there are two interfaces you can use to get the same markdown service. How do I know these will give us the same object? And which should we use? We'll talk about those two questions in the next chapter.
But since it doesn't matter, let's use MarkdownInterface
. Open ArticleController
. In show()
, create a new variable - $articleContent
- and set it to the multiline HEREDOC syntax. I'm going to paste in some fake content. This is the same beefy content that's in the template. In the controller, let's markdownify some stuff! Add some emphasis to jalapeno bacon
ands let's turn beef ribs
into a link to https://baconipsum.com/
:
// ... lines 1 - 11 | |
class ArticleController extends AbstractController | |
{ | |
// ... lines 14 - 24 | |
public function show($slug) | |
{ | |
$comments = [ | |
// ... lines 28 - 30 | |
]; | |
$articleContent = <<<EOF | |
Spicy **jalapeno bacon** ipsum dolor amet veniam shank in dolore. Ham hock nisi landjaeger cow, | |
lorem proident [beef ribs](https://baconipsum.com/) aute enim veniam ut cillum pork chuck picanha. Dolore reprehenderit | |
labore minim pork belly spare ribs cupim short loin in. Elit exercitation eiusmod dolore cow | |
turkey shank eu pork belly meatball non cupim. | |
Laboris beef ribs fatback fugiat eiusmod jowl kielbasa alcatra dolore velit ea ball tip. Pariatur | |
laboris sunt venison, et laborum dolore minim non meatball. Shankle eu flank aliqua shoulder, | |
capicola biltong frankfurter boudin cupim officia. Exercitation fugiat consectetur ham. Adipisicing | |
picanha shank et filet mignon pork belly ut ullamco. Irure velit turducken ground round doner incididunt | |
occaecat lorem meatball prosciutto quis strip steak. | |
Meatball adipisicing ribeye bacon strip steak eu. Consectetur ham hock pork hamburger enim strip steak | |
mollit quis officia meatloaf tri-tip swine. Cow ut reprehenderit, buffalo incididunt in filet mignon | |
strip steak pork belly aliquip capicola officia. Labore deserunt esse chicken lorem shoulder tail consectetur | |
cow est ribeye adipisicing. Pig hamburger pork belly enim. Do porchetta minim capicola irure pancetta chuck | |
fugiat. | |
EOF; | |
// ... lines 51 - 57 | |
} | |
// ... lines 59 - 70 | |
} |
Pass this into the template as a new articleContent
variable:
// ... lines 1 - 11 | |
class ArticleController extends AbstractController | |
{ | |
// ... lines 14 - 24 | |
public function show($slug) | |
{ | |
// ... lines 27 - 32 | |
$articleContent = <<<EOF | |
Spicy **jalapeno bacon** ipsum dolor amet veniam shank in dolore. Ham hock nisi landjaeger cow, | |
lorem proident [beef ribs](https://baconipsum.com/) aute enim veniam ut cillum pork chuck picanha. Dolore reprehenderit | |
labore minim pork belly spare ribs cupim short loin in. Elit exercitation eiusmod dolore cow | |
turkey shank eu pork belly meatball non cupim. | |
Laboris beef ribs fatback fugiat eiusmod jowl kielbasa alcatra dolore velit ea ball tip. Pariatur | |
laboris sunt venison, et laborum dolore minim non meatball. Shankle eu flank aliqua shoulder, | |
capicola biltong frankfurter boudin cupim officia. Exercitation fugiat consectetur ham. Adipisicing | |
picanha shank et filet mignon pork belly ut ullamco. Irure velit turducken ground round doner incididunt | |
occaecat lorem meatball prosciutto quis strip steak. | |
Meatball adipisicing ribeye bacon strip steak eu. Consectetur ham hock pork hamburger enim strip steak | |
mollit quis officia meatloaf tri-tip swine. Cow ut reprehenderit, buffalo incididunt in filet mignon | |
strip steak pork belly aliquip capicola officia. Labore deserunt esse chicken lorem shoulder tail consectetur | |
cow est ribeye adipisicing. Pig hamburger pork belly enim. Do porchetta minim capicola irure pancetta chuck | |
fugiat. | |
EOF; | |
return $this->render('article/show.html.twig', [ | |
// ... lines 53 - 55 | |
'articleContent' => $articleContent, | |
]); | |
} | |
// ... lines 59 - 70 | |
} |
And now, in the template, remove all the old stuff and just print {{ articleContent }}
:
// ... lines 1 - 4 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-sm-12"> | |
<div class="show-article-container p-3 mt-4"> | |
// ... lines 11 - 25 | |
<div class="row"> | |
<div class="col-sm-12"> | |
<div class="article-text"> | |
{{ articleContent }} | |
</div> | |
</div> | |
</div> | |
// ... lines 33 - 71 | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} | |
// ... lines 78 - 83 |
Let's try it! Go back to our site and refresh! No surprise: it's the raw content. Now it's time to process this through Markdown!
Using the Markdown Service
In ArticleController
, tell Symfony to pass us the markdown service by adding a type-hinted argument. Let's use MarkdownInterface
: MarkdownInterface $markdown
:
// ... lines 1 - 4 | |
use Michelf\MarkdownInterface; | |
// ... lines 6 - 12 | |
class ArticleController extends AbstractController | |
{ | |
// ... lines 15 - 25 | |
public function show($slug, MarkdownInterface $markdown) | |
{ | |
// ... lines 28 - 60 | |
} | |
// ... lines 62 - 73 | |
} |
Now, below, $articleContent = $markdown->
- we never looked at the documentation to see how to use the markdown service... but thanks to PhpStorm, it's pretty self-explanatory - $markdown->transform($articleContent)
:
// ... lines 1 - 4 | |
use Michelf\MarkdownInterface; | |
// ... lines 6 - 12 | |
class ArticleController extends AbstractController | |
{ | |
// ... lines 15 - 25 | |
public function show($slug, MarkdownInterface $markdown) | |
{ | |
// ... lines 28 - 33 | |
$articleContent = <<<EOF | |
// ... lines 35 - 50 | |
EOF; | |
$articleContent = $markdown->transform($articleContent); | |
// ... lines 54 - 60 | |
} | |
// ... lines 62 - 73 | |
} |
Un-escaping Raw HTML
And that's it! Refresh! It works! Um... kind of. It is transforming our markdown into HTML... but if you look at the HTML source, it's all being escaped! Bah!
Actually, this is awesome! One of Twig's super-powers - in addition to having very stylish hair - is to automatically escape any variable you render. That means you're protected from XSS attacks without doing anything.
If you do know that it's safe to print raw HTML, just add |raw
:
// ... lines 1 - 4 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-sm-12"> | |
<div class="show-article-container p-3 mt-4"> | |
// ... lines 11 - 25 | |
<div class="row"> | |
<div class="col-sm-12"> | |
<div class="article-text"> | |
{{ articleContent|raw }} | |
</div> | |
</div> | |
</div> | |
// ... lines 33 - 71 | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} | |
// ... lines 78 - 83 |
Try it again! Beautiful!
So here is our first big lesson:
- Everything in Symfony is done by a service
- Bundles give us these services... and installing new bundles gives us more services.
And 3, Twig clearly gets its hair done by a professional.
Next, let's use a service that's already being added to our app by an existing bundle: the cache service.
I have found a service bug. whitespaces at the beginning of the heredoc block is causing a display error.
The markdown isn't working partly
Error screenshot:
https://imgur.com/IIMTSRG
An easy quick fix would be replacing more than one whitespace into 1 whitespace:
$articleContent = $markdown->transform(preg_replace('/[[:blank:]]+/',' ', $articleContent));;
Screenshot working version:
https://imgur.com/kBS3NDr