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

Twig ❤️

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.

Let's make our show() controller render some real HTML by using a template. As soon as you want to render a template, you need to make your controller extend AbstractController. Don't forget to let PhpStorm auto-complete this so it adds the use statement.

Now, obviously, a controller doesn't need to extend this base class - Symfony doesn't really care about that. But, you usually will extend AbstractController for one simple reason: it gives us shortcut methods!

... lines 1 - 4
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
... lines 6 - 8
class QuestionController extends AbstractController
{
... lines 11 - 27
}

Rendering a Template

The first useful shortcut method is render. We can say: return this->render() and pass two arguments. The first is the filename of the template: we can put anything here, but usually - because we value our sanity - we name this after our controller: question/show.html.twig.

The second argument is an array of any variables that we want to pass into the template. Eventually, we're going to query the database for a specific question and pass that data into the template. Right now, let's fake it. I'll copy my ucwords() line and delete the old code. Let's pass a variable into the template called - how about, question - set to this string.

... lines 1 - 8
class QuestionController extends AbstractController
{
... lines 11 - 21
public function show($slug)
{
return $this->render('question/show.html.twig', [
'question' => ucwords(str_replace('-', ' ', $slug))
]);
}
}

Pop quiz time! What do you think that render() method returns? A string? Something else? The answer is: a Response object... with HTML inside. Because remember: the one rule of a controller is that it must always return a Response.

Tip

A controller can actually return something other than a Response, but don't worry about that right now... or maybe ever.

Creating the Template

Anyways, let's go create that template! Inside templates/, create a question sub-directory, then a new file called show.html.twig. Let's start simple: an <h1> and then {{ question }} to render the question variable. And... I'll put some extra markup below this.

<h1>{{ question }}</h1>
<div>
Eventually, we'll print the full question here!
</div>

The 3 Syntaxes of Twig!

We just wrote our first Twig code! Twig is super friendly: it's a plain HTML file until your write one of its two syntaxes.

The first is the "say something" syntax. Anytime you want to print something, use {{, the thing you want to print, then }}. Inside the curly braces, you're writing Twig code... which is a lot like JavaScript. This prints the question variable. If we put quotes around it, it would print the string question. And yea, you can do more complex stuff - like the ternary operator. Again, it's very much like JavaScript.

The second syntax I call the "do something" syntax. It's {% followed by whatever you need to do, like if or for to do a loop. We'll talk more about this in a second.

And... that's it! You're either printing something with {{ or doing something, like an if statement, with {%.

Ok, small lie, there is a third syntax... but it's just comments: {#, a comment... then #}.

<h1>{{ question }}</h1>
{# oh, I'm just a comment hiding here #}
<div>
Eventually, we'll print the full question here!
</div>

Let's see if this works! Move over refresh and... got it! If you view the HTML source, notice that there is no HTML layout yet. It's literally the markup from our template and nothing else. We'll add a layout in a few minutes.

Looping with the {% for Tag

Ok: we have a fake question. I think it deserves some fake answers! Back in the controller, up in the show() action, I'm going to paste in three fake answers.

... lines 1 - 8
class QuestionController extends AbstractController
{
... lines 11 - 21
public function show($slug)
{
$answers = [
'Make sure your cat is sitting purrrfectly still ?',
'Honestly, I like furry shoes better than MY cat',
'Maybe... try saying the spell backwards?',
];
... lines 29 - 33
}
}

Again, once we talked about databases, we will query the database for these. But this will work beautifully to start. Pass these into the template as a second variable called answers.

... lines 1 - 8
class QuestionController extends AbstractController
{
... lines 11 - 21
public function show($slug)
{
$answers = [
'Make sure your cat is sitting purrrfectly still ?',
'Honestly, I like furry shoes better than MY cat',
'Maybe... try saying the spell backwards?',
];
return $this->render('question/show.html.twig', [
'question' => ucwords(str_replace('-', ' ', $slug)),
'answers' => $answers,
]);
}
}

Back in the template, how can we print those? We can't just say {{ answers }}... because it's an array. What we really want to do is loop over that array and print each individual answer. To do that, we get to use our first "do something" tag! It looks like this: {% for answer in answers %}. And most "do something" tags also have an end tag: {% endfor %}.

Let's surround this with a ul and, inside the loop, say <li> and {{ answer }}.

... lines 1 - 8
<h2>Answers</h2>
<ul>
{% for answer in answers %}
<li>{{ answer }}</li>
{% endfor %}
</ul>
... lines 16 - 17

I love that! Ok browser, reload! It works! I mean, it's so, so, ugly... but we'll fix that soon.

The Twig Reference: Tags, Filters, Functions

Head to https://twig.symfony.com. Twig is its own library with its own documentation. There's a lot of good stuff here... but what I really love is down here: the Twig Reference.

See these "Tags" on the left? These are all of the "do something" tags that exist. Yep, it will always be {% and then one of these words - like for, if or {% set. If you try {% pizza, I'll think it's funny, but Twig will yell at you.

Twig also has functions... like every language... and a cool feature called "tests", which is a bit unique. These allow you to say things like: if foo is defined or if number is even.

But the biggest and coolest section is for "filters". Filters are basically functions... but more hipster. Check out the length filter. Filters work like "pipes" on the command line: we "pipe" the users variable into the length filter, which counts it. The value goes from left to right. Filters are really functions... with a friendlier syntax.

Let's use this filter to print out the number of answers. I'll add some parenthesis, then {{ answers|length }}. When we try that... super nice!

... lines 1 - 7
<h2>Answers {{ answers|length }}</h2>
... lines 11 - 17

Twig Template Inheritance: extends

At this point, you're well on your way to being a Twig pro. There's just one last big feature we need to talk about, and it's a good one: template inheritance.

Most of our pages will share an HTML layout. Right now, we don't have any HTML structure. To give it some, at the top of the template, add {% extends 'base.html.twig' %}.

{% extends 'base.html.twig' %}
<h1>{{ question }}</h1>
... lines 4 - 19

This tells Twig that we want to use this base.html.twig template as our layout. This file is super basic right now, but it's ours to customize - and we will soon.

But if you refresh the page... hide! Huge error!

A template that extends another one cannot include content outside Twig blocks.

When you add extends to a template, you're saying that you want the content from this template to go inside of base.html.twig. But... where? Should Twig put it all the way on top? On the bottom? Somewhere in the middle? Twig doesn't know!

I'm sure you already noticed these block things, like stylesheets, title and body. Blocks are "holes" that a child template can put content into. We can't just extend base.html.twig: we need to tell it which block the content should go into. The body block is a perfect spot.

How do we do this? By overriding the block. Above the content add {% block body %}, and after, {% endblock %}.

{% extends 'base.html.twig' %}
{% block body %}
... lines 4 - 18
{% endblock %}
... lines 20 - 21

Try it now. It works! It doesn't look like much yet... because our base layout is so simple, but if you check out the page source, we do have the basic HTML structure.

Adding, Removing, Changing Blocks?

By the way, these blocks in base.html.twig aren't special: you can rename them, move them around, add more or remove some. The more blocks you add, the more flexibility your "child" templates have to put content into different spots.

Most of the existing blocks are empty... but a block can define default content... like the title block. See this Welcome? No surprise, that's the current title of the page.

Because this is surrounded by a block, we can override that in any template. Check it out: anywhere in show.html.twig, add {% block title %}, Question, print the question, then {% endblock %}.

{% extends 'base.html.twig' %}
{% block title %}Question: {{ question }}{% endblock %}
{% block body %}
... lines 6 - 20
{% endblock %}
... lines 22 - 23

This time when we reload... we have a new title!

Ok, with Twig behind us, let's look at one of the killer features of Symfony... and your new best friend for debugging: the Symfony profiler.

Leave a comment!

40
Login or Register to join the conversation
Gog sp. z o.o. Avatar
Gog sp. z o.o. Avatar Gog sp. z o.o. | posted 2 years ago

I really have to say one thing - I do love how you run those lessons. It's a true pleasure to watch them!

1 Reply
Steven-pascal K. Avatar
Steven-pascal K. Avatar Steven-pascal K. | posted 7 months ago

I just do the same as the code here but it does'nt work for my code
littlebit confusing if someone knows where the Problem is, Please text me.

Reply

Hey @Rexorlive

Could you let us know the error message you got and what were you exactly doing?

Cheers!

Reply
Azar A. Avatar

Hello everyone!
For some reason, on any question, it returns the '>" sign at the beginning
screen http://joxi.ru/Vm6w9lKC3QokD2
code http://joxi.ru/52aKvEXulNjn0r http://joxi.ru/Y2LxKBlixbZgg2
this is normal?

Reply
Azar A. Avatar

It turns out that my translator was adding ...
Sorry to bother you, henceforth I will be more attentive and do not disturb on trifles

Reply
Azar A. Avatar
Azar A. Avatar Azar A. | Azar A. | posted 11 months ago | edited

Azar A. help me please :)

Reply
Azar A. Avatar

I figured out what was going on, I copied the code from the documentation
after opening the tag, for some reason, extra> added

from the text that is attached under the video
<h1>>{{ question }}</h1>
<div>
Eventually, we'll print the full question here!
</div>

that's right, I suffered a little without understanding what was going on :)

<h1>{{ question }}</h1>
<div>
Eventually, we'll print the full question here!
</div>

Reply

Hey Azar A.

Is it possible that that sign comes from your template? Or perhaps you accidentally added it to your questions in the DB

Cheers!

Reply
Azar A. Avatar

It turns out that my translator was adding ...
Sorry to bother you, henceforth I will be more attentive and do not disturb on trifles

Reply

No worries, you're welcome :)

Reply
James Y. Avatar
James Y. Avatar James Y. | posted 1 year ago

Is this using twig 3? I tried to follow along and ended up with an error: InvalidArgumentException: LoaderLoadException: There is no extension able to load the configuration for "twig" (in ...\config/packages/twig.yaml"). Looked for namespace "twig", found ""framework", "sensio_framework_extra"" in ...config/packages/twig.yaml (which is being imported from ...\src\Kernel.php").

Once I uninstalled, then 'composer require twig/twig:3.0' it worked fine.

Reply

Hey James,

A little-known fact: you can know what versions exactly are used in the course code - just find the Versions tab near the Script and Conversation tags. That Versions tab will show you the exact versions that are used in the course. Though, because we requested a Twig pack in the composer that wasn't unpacked - it's difficult to say what Twig version. I just downloaded the course code and check it for you - we're using v3.3.0 of twig/twig package.

Hope this helps! Good catch on removing and installing again :)

Cheers!

Reply
Israel L. Avatar
Israel L. Avatar Israel L. | posted 1 year ago

I'm from a Javascript background and know absolutely nothing in php but my job requires me to learn Symfony and everything seems so smooth.
Big Thanks man

Reply

Hey Israel!

Thank you for your feedback! We're really happy to hear that our learning curve is smooth for you! You made your day ;)

Good luck with learning Symfony! If you ever have any questions about following our tutorials - just let us know in the comments below the video and we will help you :)

Cheers!

Reply
Farshad Avatar
Farshad Avatar Farshad | posted 1 year ago

I typed in: return $this->render('form/measurements'...
I got an error saying that measurments doesn't exist.
I added .html.twig after and it worked: return $this->render('form/measurements.html.twig'
How does it work without .html.twig in this video?

Reply

Hey Farry7,

I do see the file extension in the video and in the code blocks https://symfonycasts.com/sc...
Anyways, you have to specify the file extension, otherwise Twig won't be able to find it

Cheers!

Reply
Nahom B. Avatar
Nahom B. Avatar Nahom B. | posted 1 year ago

The best of best tutorial of all time. You teach with fun!!! I was looking for this kind of tutorials all my life 😉😁😜

Reply
triemli Avatar
triemli Avatar triemli | posted 1 year ago

Guys, can you help me?
I try to override 404 page for something more fancy on production mode.
I found this https://symfony.com/doc/cur...

I have isntalled composer require symfony/twig-pack

But i don't have any templates/bundles/TwigBundle/Exception/ folder or example templates.
I just created error404.html.twig file. How the twig will know about it? Config maybe somewhere for read templates?
Right now i see template error from /vendor/symfony/error-handler/Resources/views/error.html.php
Thanks!

Reply

Hey triemli

you need to create that folder structure so the Twig bundle can find it and detect that you're overriding the errors templates. You can see an example in the documentation link that you sent but basically it's just another, boring, Twig template

Cheers!

Reply
triemli Avatar

Thanks a lot, it works!
Actually I found also this solution:

framework:
error_controller: App\Controller\ErrorController::show

Reply

as you have seen there are many ways to override that behavior

Reply
Johannes Avatar
Johannes Avatar Johannes | posted 2 years ago

You guys do a great job with SymfonyCasts! I'm so glad to found it! Thank you :-)

Reply

Thanks for the kind words!

Reply
Michel Avatar

Hi all!

I've tried to install the code from this course.
Upon running the symfony server all looks good from the terminal:

bash-3.2$ ls
README.md finish start
bash-3.2$ cd start
bash-3.2$ symfony server:start
Jul 31 09:40:17 |DEBUG| PHP Reloading PHP versions
Jul 31 09:40:17 |DEBUG| PHP Using PHP version 7.4.4 (from default version in $PATH)
Jul 31 09:40:17 |INFO | PHP listening path="/usr/local/Cellar/php/7.4.4/sbin/php-fpm" php="7.4.4" port=53835
Jul 31 09:40:17 |DEBUG| PHP started
Jul 31 09:40:17 |INFO | PHP 'user' directive is ignored when FPM is not running as root
Jul 31 09:40:17 |INFO | PHP 'group' directive is ignored when FPM is not running as root
Jul 31 09:40:17 |INFO | PHP fpm is running, pid 19990
Jul 31 09:40:17 |INFO | PHP ready to handle connections

[OK] Web server listening
The Web server is using PHP FPM 7.4.4
https://127.0.0.1:8000

However when I load the page in my browser I get

Warning: require(/Users/michel/Sites/code-symfony_31-07-2020/start/vendor/autoload.php): failed to open stream: No such file or directory in /Users/michel/Sites/code-symfony_31-07-2020/start/config/bootstrap.php on line 5

Fatal error: require(): Failed opening required '/Users/michel/Sites/code-symfony_31-07-2020/start/vendor/autoload.php' (include_path='.:/usr/local/Cellar/php/7.4.4/share/php/pear') in /Users/michel/Sites/code-symfony_31-07-2020/start/config/bootstrap.php on line 5

Any clue as to why this is happening?
All help is very much appreciated!

Reply

Hey Michel

I'm afraid you have to grab the nearest hammer and smash your computer - J/K
I believe you forgot to install composer. Just run composer install and try again

Cheers!

Reply
Michel Avatar

Thanks Diego! That fixed it for me! Luckily I didn't put a hammer to my macbook ;-)

Reply

haha I'm glad to hear that!

Reply
Default user avatar

Twig\Error\LoaderError:
Unable to find template "question/show.html.twig"

i'm sure there is a file named show.html.twig and directory named question

Help me please :(

Reply

Hey Shayma,

What exact path to the template that's relative to the project root dir? Is it exactly "templates/question/show.html.twig" for you? And what exactly value do you use in your render() function in the controller?

Please, double check spelling. Sometimes it's easy to miss a letter in the file path. Also, you can try to clear the cache first with, it may help too.

Cheers!

Reply
Default user avatar

Hey Victor,

Yes it's exactly "templates/question/show.html.twig". In RenderI Used:


public function show($slug)
{
// ...
return $this->render('question/show.html.twig', [
'question' => ucwords(str_replace('-', ' ', $slug))
]);
}

And for avoiding spelling mistakes I just copyed the code from the script bellow

Can i ask you which cash you mean?

This is the whole error i have:

Twig\Error\LoaderError
in C:\Users\shaym\cauldron_overflow\vendor\twig\twig\src\Loader\FilesystemLoader.php (line 227)
if (!$throw) { return null; } throw new LoaderError($this->errorCache[$name]); } private function normalizeName(string $name): string { return preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name));
Reply

Hey Shayma,

What version of Symfony do you use? Did you download the course code and started from start/ directory? Or did you download a completely new Symfony version and started from scratch?

Hm, the error looks weird to me. Do you see that error on every page or only on the question show page? Could you make a screenshot of the error page? You can upload the screenshot to Imgur.com for example and send the link to it in a comment

Cheers!

Reply
Default user avatar

Hey Victor,

I did download a copletely new Symfony version (5) from the symfony website, because i'm using windows 10, not mac as in the course.
I have this error only on the question page. the other page is just working
Here is a screenshot. https://imgur.com/Y3jVzGO

Thanks!

Reply

Hey Shayma,

This doesn't matter what OS you use, you still can download the course code and start from the start/ directory. The course code contains only Symfony application that is cross-platform PHP application. We do recommend our users to follow our courses with downloaded code.

Thank you for sharing the screenshot! I see cache word on the error line, so first of all, I'd recommend you to clear the cache. Basically, you can remove everything in the var/cache/ directory. Then, try again and let me know if it does not help and you still see the same error. From the stack trace it looks like you did it correct, so not sure what else it may be if not a cache problem.

Cheers!

Reply
Default user avatar

Hello,

i have some problems since the beginning...
- the server is not working so i'm using MAMP
- if i add in the URL questions/is there any witch here , i have an error (not found)
-when i write into the twig file, i see on the navigator the delimitors. It's like it doesnt translate from twig to html
i checked my code with the php bon/console debug:server, it says everything is ok..

Do you have an idea of what it is happening?

Kind Regards

Reply

Hey @Ethel!

Sorry about the issues - that's no fun! A few things:

1) What problems were you having with the Symfony server? You can absolutely use MAMP, it just requires a bit more setup work, which means that there are also more opportunities to run into issues, which may be your problem!

2) My guess is that this is related to the above: it sounds like Apache isn't doing URL rewriting. If you're using MAMP, try running composer require symfony/apache-pack. That's a fancy way to add a public/.htaccess file for Apache which should (hopefully) fix the URL rewriting issue. The correct URL that you will need to go to will "vary", however, based on how your MAMP is setup. It may be, for example, http://localhost/cauldron_overflow/public assuming you have the project setup in a cauldron_overflow inside the MAMP document root. When you get the URL correct, it will (ideally) execute the public/index.php file and boot Symfony.

3) I also think this is related to the stuff above :). No matter what web server you are using, ultimately, no matter *what* URL you are going to, you want your web server to execute the public/index.php file. That boots Symfony and runs your app. I think in your case, your web server is pointing at the root of your project - and so the public/index.php file isn't being executed. When you navigate to your Twig templates, you're correct that they're not being rendered: your browser is just returning them as text. Basically, if we can get your web server setup correctly, life will be much better.

Sorry for the troubles :). This is super annoying setup stuff that (unfortunately) is common to any web framework. I remember learning it, and it's a pain. If you can get beyond it, the other stuff is more fun.

Cheers!

Reply
Richard Avatar
Richard Avatar Richard | posted 2 years ago

I can appreciate the video not being done but its inexcusable not to include the code in the script. Using the code in the downloaded "finish":-

An exception has been thrown during the rendering of a template ("Could not find the entrypoints file from Webpack: the file "/home/maxi/Dropbox/homefiles/development/Symfony/cauldron_overflow/public/build/entrypoints.json" does not exist.").

Reply

Hey Richard!

Sorry about that :). When a video isn't published yet, it often means that it isn't quite finished yet. The code blocks are added to our queue to add as soon as the video becomes available - we have a dashboard for that. I just finished this video yesterday afternoon - so the team hasn't yet added the code block for this.

But, hopefully I can help with the error :). At the end of the tutorial, we will introduce Webpack Encore as an optional feature. So, to get the final code to work, you will need to run


yarn install
yarn encore dev

However, I'm glad you messaged us... because I don't want Encore to be a *requirement* to run the final code :). So, I'm going to commit the built assets at the end of the tutorial so that they are part of the code download. Then you *can* use Encore if you want, but the files will be there either way.

Cheers!

1 Reply
Richard Avatar

Thanks for the ever polite reply. Sorry if I sounds a bit short. It's a tough day ;)

1 Reply

No worries - you helped us spot that entrypoints issue :). Hope your day gets better!

2 Reply
Cat in space

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

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.3.0 || ^8.0.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "easycorp/easy-log-handler": "^1.0.7", // v1.0.9
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "symfony/asset": "5.0.*", // v5.0.11
        "symfony/console": "5.0.*", // v5.0.11
        "symfony/debug-bundle": "5.0.*", // v5.0.11
        "symfony/dotenv": "5.0.*", // v5.0.11
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.0.*", // v5.0.11
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/profiler-pack": "*", // v1.0.5
        "symfony/routing": "5.1.*", // v5.1.11
        "symfony/twig-pack": "^1.0", // v1.0.1
        "symfony/var-dumper": "5.0.*", // v5.0.11
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.0.*" // v5.0.11
    },
    "require-dev": {
        "symfony/profiler-pack": "^1.0" // v1.0.5
    }
}