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

KnpMarkdownBundle & its Services

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.

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:

  1. Everything in Symfony is done by a service
  2. 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.

Leave a comment!

48
Login or Register to join the conversation
Florian Avatar
Florian Avatar Florian | posted 4 years ago

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

1 Reply

Hey Sven,

Thank you for this report! Actually, we don't have this problem in our screencast because every new line of text does not have leading spaces, you can see it in this code blocks: https://symfonycasts.com/sc... . Basically, if you will stick to the exact code in this tutorial - you won't have this problem. Well, in your case when you intentionally add some spaces before each lines - it might work incorrect, agree, because indentation is special in Markdown and means blockquotes. But the simplest solution instead of using preg_replace() is: just remove all the spaces in your source text. In PhpStorm it's pretty easy to do, just select all the text and press Shift + Tab a few times to move everything left and that's it.

So, I don't think we should fix it in the tutorial, because it's just an odd case when you write the text incorrectly (well, with incorrect indentation).

Anyway, thank you for sharing your solution with others, I think it might be helpful.

Cheers!

Reply
Jesus Avatar

The MarkdownParserInterface don't have transform method, have been updated to transformMarkdown($text)

Reply

Hey Jesús G. !

You're absolutely right :). But this is a weird situation where this service implements two interfaces:

1) In the video (in this chapter), we use Michelf\MarkdownInterface, which has a transform() method.
2) But you can also type-hint Knp\Bundle\MarkdownBundle\MarkdownParserInterface and THAT has the transformMarkdown() method.

So, both are fine. Honestly, there really isn't a reason for the MarkdownParserInterface to exist at this point (it's redundant, because the underlying library has its own MarkdownInterface), but it's there mostly for legacy reasons.

Cheers!

Reply
Oliver-W Avatar
Oliver-W Avatar Oliver-W | posted 3 years ago

What if I wanted to add a © to the string that has to be passed to twig?

Thx

Reply

Hey Oliver W.

If that character is causing you troubles then I recommend you to encode your string by using php's native function htmlentities(). That should do the work. Let me know if you run into more problems

Cheers!

Reply
Oliver-W Avatar

Thx Diego, but 8-(. That gives me the following: "&copy;"

Reply

Hmm, so nothing is decoding the string back. What charset encoding are you using, is it UTF-8? If that's the case I think you can just copy and paste that character into your code and it should work

Reply
Richard Avatar
Richard Avatar Richard | posted 3 years ago | edited

Following the course script line by line. Installing the knp markdown package using composer.

`Using version ^1.8 for knplabs/knp-markdown-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)

Prefetching 2 packages 🎶 💨

  • Downloading (100%)

Package operations: 2 installs, 0 updates, 0 removals

  • Installing michelf/php-markdown (1.9.0): Loading from cache
  • Installing knplabs/knp-markdown-bundle (1.8.1): Loading from cache
    Package symfony/lts is abandoned, you should avoid using it. Use symfony/flex instead.
    `

This is after "composer install" from code in the "start" directory of the archive and not my own "carry on" code form the previous course so it SHOULD just work. I guess this does "work" but its confusing and worrying. Should I worry about it or just run with it for now? Moving to flex is far from a trivial change according to our friends at Symfony:

<a href="#">https://symfony.com/doc/current/setup/flex.html&lt;/a&gt;

Reply

Hey Richard!

Excellent question :). Actually, you're already using symfony/flex - but the error message isn't clear. The symfony/lts thing was a package that existed for a short time to help your application "fix" all of its Symfony packages on a specific version. That has been abandoned. The short answer is: you can ignore this & don't worry about it. The longer answer is, you could remove symfony/lts and add it this line instead - https://github.com/symfony/skeleton/blob/4.0/composer.json#L60 - that is the "new" solution. symfony/flex reads that line and helps "fix" all of your symfony/* repositories at version 4.0 (or whatever version you put here).

If you want more info or still have questions, let me know :).

Cheers!

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 3 years ago

Can I see all the services that belong to bundle? Is there such a console command?

Reply

Hey Dmitriy

I'm not sure if that command exists but what you can do is to search for the file config definition of those services. Just open up a bundle directory and look inside Resources/config. For example vendor/symfony/debug-bundle/Resources/config

Cheers!

Reply
Michel K. Avatar
Michel K. Avatar Michel K. | posted 4 years ago

Installing KnpMarkdownBundle returns an error and messed up the SF4 setup.

Any solution on this?

Tnx

Package symfony/lts is abandoned, you should avoid using it. Use symfony/flex instead.
Writing lock file
Generating autoload files
Symfony operations: 1 recipe (6b9874565790f5e93c2c01b6335f4da2)
- Configuring knplabs/knp-markdown-bundle (>=1.7.1): From auto-generated recipe
Executing script cache:clear [KO]
[KO]
Script cache:clear returned with error code 1
!!
!! In FileLoader.php line 168:
!!
!! There is no extension able to load the configuration for "doctrine" (in C:\xampp\htdocs\sf4\certificaten\config\pac
!! kages/doctrine.yaml). Looked for namespace "doctrine", found "framework", "web_server", "sensio_framework_extra", "
!! twig", "web_profiler", "monolog", "debug", "knp_markdown" in C:\xampp\htdocs\sf4\certificaten\config\packages/doctr
!! ine.yaml (which is loaded in resource "C:\xampp\htdocs\sf4\certificaten\config\packages/doctrine.yaml").
!!
!!
!! In YamlFileLoader.php line 656:
!!
!! There is no extension able to load the configuration for "doctrine" (in C:\xampp\htdocs\sf4\certificaten\config\pac
!! kages/doctrine.yaml). Looked for namespace "doctrine", found "framework", "web_server", "sensio_framework_extra", "
!! twig", "web_profiler", "monolog", "debug", "knp_markdown"
!!
!!
!!
Script @auto-scripts was called via post-update-cmd

Installation failed, reverting ./composer.json to its original content.

Reply

Hey Michel,

For me it looks like you don't have Doctrine bundle installed in your project. Could you explain what exactly steps you do to see this error? Did you download the source code of this course? I what directory are you: start/ or finish/? And what command exactly are you executing?

Cheers!

Reply
Lijana Z. Avatar
Lijana Z. Avatar Lijana Z. | posted 4 years ago

Did not know and its good to know that it is automatically escaped and safe from XSS :)

Reply

Yep! All hail Twig! :)

Reply
Abelardo L. Avatar
Abelardo L. Avatar Abelardo L. | posted 4 years ago

Hi everyone,

I have an issue with the Michelf\MarkdownInterface service.

I have written a dummy text in order to test it: **negrita** [enlace] (...) but it doesn't work.

What's wrong?

I don't why Disqus doesn't allow me paste pictures. ¿?

Best regards,

Abelardo.

Reply
Abelardo L. Avatar
Abelardo L. Avatar Abelardo L. | Abelardo L. | posted 4 years ago | edited

I dumped this information:

array:5 [â–¼
"title" => "Why Asteroids Taste Like Bacon"
"articleContent" => """

    **negrita** [enlace](https://baconipsum.com/)\n

\n
"""
"slug" => "why-asteroids-taste-like-bacon"
"comments" => array:3 [â–¶]
"app" => AppVariable {#201 â–¶}

The 'articleContent''s content is surrounded by "< p r e >" and "< c o d e >" tags.

Reply
Abelardo L. Avatar

I wrote this line:

$articleContent = " **negrita** [enlace](https://baconipsum.com/) ";

and...IT WORKS!

Note that "<<<eof...eof;" doesn't="" exist.="" ;)="">

Reply
Abelardo L. Avatar

I pasted the whole text which appears inside the video and the following are the conclusions:
1) You can't put " and start in the following line; the text should be started in the first line, after the "
2) To create a new line, you should put " < b r > " and not " \ n ". If you don't put them, a new line won't be created.
3) Don't forget to end with ".

Reply

Hey Abelardo,

Thanks for sharing it with others, though I'm a bit lost in your problem :) But there're a few quick ideas:

First of all, we do not allow to paste images in DIsqus comments, that's our Disqus configuration, so if you want to share a screenshot - upload it to any image storage like imgur.com and paste a link to it in a comment.

You also can wrap your code with pre and then code tags to tell Disqus that the text should be highlighted instead of rendered.

And br tag is needed when you want to render a new line in browser, because \ n is used to make a new line in source code only and browsers do not consider it.

Cheers!

1 Reply
Abelardo L. Avatar
Abelardo L. Avatar Abelardo L. | Victor | posted 4 years ago

Thanks for your tips!

Brs.

Reply
Paul L. Avatar
Paul L. Avatar Paul L. | posted 5 years ago

I watched this video twice and looked at my code very carefully but I still get this error. Cannot determine controller argument for "App\Controller\ArticleController::show()": the $markdown argument is type-hinted with the non-existent class or interface: "App\Controller\MarkdownInterface". Did you forget to add a use statement?

Reply

Hey Paul L.

Did you import the service in your controller?


// someController.php

use Michelf\MarkdownInterface;

...

Cheers!

Reply
Paul L. Avatar

No but it seems quite important lol. For being a literal make or break line if code he sure does gloss over it!

-1 Reply

Probably you missed it because PHPStorm adds imports automatically :)

Reply
Default user avatar
Default user avatar Tess Hsu | posted 5 years ago

hi knp team,

just courious, how could I find out those bundles? all through github?
https://github.com/search?u...

so it is means every bundles, service has theiry own definition class method which means need to read every doc?
Or once installed, the phpStorm will auto-writing hit method for us?

thank you

Reply
Default user avatar

Packagist is for all the php-packages. You also could search bundles special for Symfony at http://knpbundles.com/

1 Reply

Hey Tess Hsu!

Great questions :). First, to find bundles, yea... it seems simple, but I mostly use Google or search GitHub as you mentioned :). It's easy to find bundle (e.g. "Symfony markdown bundle"). The more important part is to judge whether the bundle you've found is "decent". The most important things to look at are the number of stars on GitHub & also the date of the last commit (to make sure it's not abandoned). You can also search on packagist and see how many downloads a package has. It's not an exact science. Oh, and you can also filter packagist to show you only bundles - https://packagist.org/?q=&p... (the "symfony-bundle" type on the right is selected).

> so it is means every bundles, service has theiry own definition class method which means need to read every doc?
Or once installed, the phpStorm will auto-writing hit method for us?

I don't think I understand this question - can you ask again? What do you mean by "definition class method"? Are you wondering how you know which method (like transform) that you can call on each service? Or are you wondering how you know which type-hint (like MarkdownInterface) you should use? If you're wondering about the method (like transform), then yep! PhpStorm auto-completes that. As soon as you type-hint the argument (like MarkdownInterface), PhpStorm will very confidently auto-complete all methods available.

Cheers!

Reply
Default user avatar

Hi weaverryan,

greate yes your anwsers are all my questions, cheers

Reply
Petru L. Avatar
Petru L. Avatar Petru L. | posted 5 years ago

Hey Ryan,

Got a problem with transform() method from Michelf's class MarkdownInterface ... it returns the plain text.I did a bit of debugging to check if it reaches the method(which it does) but further debugging will take a while and since this service isn't so important to me maybe you have seen this problem before and can suggest a quick resolve? Thank you

Reply

Hey Petru L.

That's weird, are you using some sort of cache? Or probably your content does not contain any MD tags?

Reply
Petru L. Avatar

Uhm, the content is exactly the one from the video , containing *** and the link markdown.Now i do have a cache, the symfony default cache service, because im further wit hthe videos,but at that time i didnt.Anyway, i did cache:clear just in case and still no result.

1 Reply

Hmm, even weirder :p
Can you dump the markdown object just to be sure that you are getting passed the right parser?

Reply
Petru L. Avatar

Max {#242 â–¼
#features: array:19 [â–¼
"header" => true
"list" => true
"horizontal_rule" => true
"table" => true
"foot_note" => true
"fenced_code_block" => true
"abbreviation" => true
"definition_list" => true
"inline_link" => true
"reference_link" => true
"shortcut_link" => true
"images" => true
"block_quote" => true
"code_block" => true
"html_block" => true
"auto_link" => true
"auto_mailto" => true
"entities" => true
"no_html" => false
]
+fn_id_prefix: ""
+fn_link_title: ""
+fn_backlink_title: ""
+fn_link_class: "footnote-ref"
+fn_backlink_class: "footnote-backref"
+fn_backlink_html: "↩︎"
+table_align_class_tmpl: ""
+code_class_prefix: ""
+code_attr_on_pre: false
+predef_abbr: []
+hashtag_protection: false
#footnotes: []
#footnotes_ordered: []
#footnotes_ref_count: []
#footnotes_numbers: []
#abbr_desciptions: []
#abbr_word_re: ""
#footnote_counter: 1
#id_class_attr_catch_re: "\{((?>[ ]*[#.a-z][-_:a-zA-Z0-9=]+){1,})[ ]*\}"
#id_class_attr_nocatch_re: "\{(?>[ ]*[#.a-z][-_:a-zA-Z0-9=]+){1,}[ ]*\}"
#block_tags_re: "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|figure"
#context_block_tags_re: "script|noscript|style|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video"
#contain_span_tags_re: "p|h[1-6]|li|dd|dt|td|th|legend|address"
#clean_tags_re: "script|style|math|svg"
#auto_close_tags_re: "hr|img|param|source|track"
#em_relist: array:3 [â–¶]
#strong_relist: array:3 [â–¶]
#em_strong_relist: array:3 [â–¶]
+empty_element_suffix: " />"
+tab_width: 4
+no_markup: false
+no_entities: false
+hard_wrap: false
+predef_urls: []
+predef_titles: []
+url_filter_func: null
+header_id_func: null
+code_block_content_func: null
+code_span_content_func: null
+enhanced_ordered_list: true
#nested_brackets_depth: 6
#nested_brackets_re: "(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[(?>[^\[\]]+|\[\])*\])*\])*\])*\])*\])*"
#nested_url_parenthesis_depth: 4
#nested_url_parenthesis_re: "(?>[^()\s]+|\((?>[^()\s]+|\((?>[^()\s]+|\((?>[^()\s]+|\((?>\)))*(?>\)))*(?>\)))*(?>\)))*"
#escape_chars: "\`*_{}[]()>#+-.!:|"
#escape_chars_re: "[\\`\*_\{\}\[\]\(\)\>#\+\-\.\!\:\|]"
#urls: []
#titles: []
#html_hashes: []
#in_anchor: false
#in_emphasis_processing: false
#document_gamut: array:6 [â–¶]
#block_gamut: array:8 [â–¶]
#span_gamut: array:9 [â–¶]
#list_level: 0
#em_strong_prepared_relist: array:9 [â–¶]
#utf8_strlen: "mb_strlen"
}

Reply

Hey Petru L.!

Ah, yea, super weird! I'm sure it's something really subtle - like cache as Diego suggested. Ok, let's do this so we can debug: can you post your code: specifically your controller and Twig template. That might help us spot any issues :).

Cheers!

Reply
Petru L. Avatar
Petru L. Avatar Petru L. | weaverryan | posted 5 years ago | edited

namespace App\Controller;


use App\Service\MarkdownHelper;
use Psr\Log\LoggerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class ArticleController extends AbstractController
{


    private $isDebug;

    public function __construct(bool $isDebug)
    {


        $this->isDebug = $isDebug;

    }

    /**
     * @Route("/", name="app_homepage")
     */
    public function homepage()
    {

        return $this->render('article/homepage.html.twig');

    }

    /**
     * @Route("/news/{slug}", name="article_show")
     */
    public function show($slug, MarkdownHelper $markdownHelper)
    {

        $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;

        $comments = [
            'I ate a normal rock once. It did NOT taste like bacon!',
            'Woohoo! I\'m going on an all-asteroid diet!',
            'I like bacon too! Buy some from my site! bakinsomebacon.com',
        ];


        $articleContent = $markdownHelper->parse(
            $articleContent
        );

        return $this->render('article/show.html.twig', [
            'title' => ucwords(str_replace('-', ' ', $slug)),
            'articleContent' => $articleContent,
            'slug' => $slug,
            'comments' => $comments,
        ]);

    }


    /**
     * @Route("/news/{slug}/heart", name="article_toggle_heart", methods={"POST"})
     */
    public function toggleArticleHeart($slug, LoggerInterface $logger)
    {
        // TODO -actually heart/unheart the article!

        $logger->info('Article is being hearted');

        return new JsonResponse(['hearts' => rand(5, 100)]);
    }
}
Reply

Hey Petru L.!

It all looks fine to me. Except... *maybe* for one minor thing. It may have just been caused by how the code pasted into the comment (I looked at the source of the code you posted), but it looks like the $articleContent text is indented. Here's what I'm talking about: https://gist.github.com/wea...

IF this is true, then when the content is parsed through markdown, I think that markdown may think (due to the indentation) that this is some "literal" content, or possibly a blockquote. And so that may be causing the odd parsing. If I'm incorrect and your code is NOT indented, could you post a screenshot of the page with the raw content?

Cheers!

Reply
Burke C. Avatar

Eight months later, and I experienced the exact same problem. Very helpful to have this in the comments. I'm doubtful that I would have figured it out otherwise...instead assuming I had some unknown dependency issues. Glad to be able to move forward in the lesson all up to speed.

2 Reply
Petru L. Avatar
Petru L. Avatar Petru L. | weaverryan | posted 5 years ago | edited

Hey weaverryan ,

It seem like it was exactly what you said.I've deleted the content and simply wrote a few lines by hand, with the same markups and it rendered it just fine.... Thank a lot.

Reply
Petru L. Avatar
Petru L. Avatar Petru L. | weaverryan | posted 5 years ago | edited

{% extends 'base.html.twig' %}

{% block title %}Read{% endblock %}

{% block body %}


    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="show-article-container p-3 mt-4">
                    <div class="row">
                        <div class="col-sm-12">
                            <img class="show-article-img" src="{{ asset('images/asteroid.jpeg') }}">
                            <div class="show-article-title-container d-inline-block pl-3 align-middle">
                                <span class="show-article-title ">{{ title }}</span>
                                <br>
                                <span class="align-left article-details"><img class="article-author-img rounded-circle" src="{{ asset('images/alien-profile.png') }}"> Mike Ferengi </span>
                                <span class="pl-2 article-details"> 4 hours ago</span>
                                <span class="pl-2 article-details">
                                    <span class="js-like-article-count">5</span>
                                    <a href="{{ path('article_toggle_heart', {slug: slug}) }}" class="fa fa-heart-o like-article js-like-article"></a> </span>
                            </div>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-sm-12">
                            <div class="article-text">
                                {{ articleContent|raw }}
                            </div>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-sm-12">
                            <p class="share-icons mb-5"><span class="pr-1">Share:</span> <i class="pr-1 fa fa-facebook-square"></i><i class="pr-1 fa fa-twitter-square"></i><i class="pr-1 fa fa-reddit-square"></i><i class="pr-1 fa fa-share-alt-square"></i></p>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-sm-12">
                            <h3><i class="pr-3 fa fa-comment"></i>{{ comments|length }} Comments</h3>
                            <hr>

                            <div class="row mb-5">
                                <div class="col-sm-12">
                                    <img class="comment-img rounded-circle" src="{{ asset('images/astronaut-profile.png') }}">
                                    <div class="comment-container d-inline-block pl-3 align-top">
                                        <span class="commenter-name">Amy Oort</span>
                                        <div class="form-group">
                                            <textarea class="form-control comment-form" id="articleText" rows="1"></textarea>
                                        </div>
                                        <button type="submit" class="btn btn-info">Comment</button>
                                    </div>
                                </div>
                            </div>

                            {% for comment in comments %}

                            <div class="row">
                                <div class="col-sm-12">
                                    <img class="comment-img rounded-circle" src="{{ asset('images/alien-profile.png') }}">
                                    <div class="comment-container d-inline-block pl-3 align-top">
                                        <span class="commenter-name">Mike Ferengi</span>
                                        <br>
                                        <span class="comment"> {{ comment }}</span>
                                        <p><a href="#">Reply</a></p>
                                    </div>
                                </div>
                            </div>

                            {% endfor %}

                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>




{% endblock %}

{% block javascripts %}
    {{ parent() }}

    <script src="{{ asset('js/article_show.js') }}"></script>
{% endblock %}
Reply
Default user avatar
Default user avatar Jacob Nijgh | posted 5 years ago

Hi Ryan,

Does this mean that we always need bundles to use services, or is it also possible for instance to pull in a package from composer and setup the service in the services.yml file for example to use it as type-hinted classes in methods. If so how can this be done and is this a best practice?

Reply

Hey Jacob Nijgh

If you install a non-symfony library, then you can't just type-hint any of its classes and expect it to automatically works. You have a couple of choices:

- Implement a factory service which will be reponsible of instantiating and returning any of the library's classes.

- Implement your own dependency injection extension, this topic is a bit more advance, but you can find good examples from any common Symfony bundle (just look inside "src/DependencyInjection" folder), or you can watch our tutorial about creating your own Symfony bundle.
Docs: https://symfony.com/doc/cur...
Tutorial: https://knpuniversity.com/s...

Cheers!

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

how do i transform this ?

/**
* @Route("/advert/{slug}", name="show_advert")
*/
public function show($slug, MarkdownInterface $mk)
{
$adv = $this->getDoctrine()
->getRepository(Advert::class)
->findOneBy(array('slug' => $slug));

$MDadv = $mk->transform($adv);

return $this->render('advert/show.html.twig', [
'adv' => $MDadv,
]);
}

obviously i get Catchable Fatal Error: Object of class App\Entity\Advert could not be converted to string ...

Reply

Hey cybernet2u

Well, you can't just pass an object to the transformer, you have to serialize it first, or code a function that create a summary of that object.

Cheers!

1 Reply
Default user avatar

how do i do that :) ? you don't have complex tutorials with doctrine :(

Reply

Hey cybernet2u

Nice to see you again :)
You can use a serializer third party library like "jms/serializer-bundle", you can watch Ryan using it in this chapter
https://knpuniversity.com/s...
and if you want to see more, you can check this other chapter for advanced usage of it
https://knpuniversity.com/s...

If you want to learn more about Doctrine, we have a couple of courses about it (but we use Symfony3):
https://knpuniversity.com/s...
https://knpuniversity.com/s...

1 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.1.4
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.0.2
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}
userVoice