Symfony controller classes do not need to extend a base class. As long as your controller function returns a Response object, Symfony doesn't care what your controller looks like. But usually, you will extend a class called AbstractController.
Why? Because it gives us shortcut methods.
Rendering a Template
And the first shortcut is render(): the method for rendering a template. So return $this->render() and pass it two things. The first is the name of the template. How about vinyl/homepage.html.twig.
It's not required, but it's common to have a directory with the same name as your controller class and filename that's the same as your method, but you can do whatever. The second argument is an array of any variables that you want to pass into the template. Let's pass in a variable called title and set it to our mix tape title: "PB and Jams".
| // ... lines 1 - 4 | |
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | |
| // ... lines 6 - 8 | |
| class VinylController extends AbstractController | |
| { | |
| ('/') | |
| public function homepage(): Response | |
| { | |
| return $this->render('vinyl/homepage.html.twig', [ | |
| 'title' => 'PB & Jams', | |
| ]); | |
| } | |
| // ... lines 19 - 34 |
Done in here. Oh, but pop quiz! What do you think the render() method returns? Yea, it's the thing I keep repeating: a controller must always return a Response object. render() is just a shortcut to render a template, get that string and put it into a Response object. render() returns a Response.
Creating the Template
We know from earlier that when you render a template, Twig looks in the templates/ directory. So create a new vinyl/ sub-directory... and inside of that, a file called homepage.html.twig. To start, add an h1 and then print the title variable with a special Twig syntax: {{ title }}. And... I'll add some hardcoded TODO text.
| <h1>{{ title }}</h1> | |
| {# TODO: add an image of the record #} | |
| <div> | |
| Our schweet track list: TODO | |
| </div> |
Let's... go see if this works! We were working on our homepage, so go there and... hello Twig!
Twigs 3 Syntax
Twig is one of the nicest parts of Symfony, and also one of the easiest. We're going to go through everything you need to know... in basically the next ten minutes.
Twig has exactly three different syntaxes. If you need to print something, use {{. I call this the "say something" syntax. If I say {{ saySomething }} that would print a variable called saySomething. Once you're inside Twig, it looks a lot like JavaScript. For example, if I surround this in quotes, now I'm printing the string saySomething. Twig has functions... so that would call the function and print the result.
So syntax #1 - the "say something" syntax - is {{
The second syntax... doesn't really count. It's {# to create a comment... and that's it.
| <h1>{{ title }}</h1> | |
| {# TODO: add an image of the record #} | |
| <div> | |
| Our schweet track list: TODO | |
| </div> |
The third and final syntax I call the "do something" syntax. This is when you're not printing, your doing something in the language. Examples of "doing something" would be if statements, for loops or setting variables.
The for Loop
Let's try a for loop. Go back to the controller. I'm going to paste in a tracks list... and then pass a tracks variable into the template set to that array.
| // ... lines 2 - 8 | |
| class VinylController extends AbstractController | |
| { | |
| ('/') | |
| public function homepage(): Response | |
| { | |
| $tracks = [ | |
| 'Gangsta\'s Paradise - Coolio', | |
| 'Waterfalls - TLC', | |
| 'Creep - Radiohead', | |
| 'Kiss from a Rose - Seal', | |
| 'On Bended Knee - Boyz II Men', | |
| 'Fantasy - Mariah Carey', | |
| ]; | |
| return $this->render('vinyl/homepage.html.twig', [ | |
| 'title' => 'PB & Jams', | |
| 'tracks' => $tracks, | |
| ]); | |
| } | |
| // ... lines 29 - 42 | |
| } |
Now, unlike title, tracks is an array... so we can't just print it. But, we can try! Ha! That gives us an array to string conversion. Nope, we need to loop over tracks.
Add a header and a ul. To loop, we'll use the "do something" syntax, which is {% and then the thing that you want to do, like for, if or set. I'll show you the full list of do something tags in a minute. A for loop looks like this: for track in tracks, where tracks is the variable we're looping over and track will be the variable inside the loop.
After this, add {% endfor %}: most "do something" tags have an end tag. Inside the loop, add an li and then use the say something syntax to print track.
| <h1>{{ title }}</h1> | |
| {# TODO: add an image of the record #} | |
| <div> | |
| Tracks: | |
| <ul> | |
| {% for track in tracks %} | |
| <li> | |
| {{ track }} | |
| </li> | |
| {% endfor %} | |
| </ul> | |
| </div> |
Using Sub.keys
When we try it... nice! Oh, but let's get trickier. Back in the controller, instead of using a simple array, I'll restructure this to make each track an associative array with song and artist keys. I'll paste in that same change for the rest.
| // ... lines 2 - 8 | |
| class VinylController extends AbstractController | |
| { | |
| ('/') | |
| public function homepage(): Response | |
| { | |
| $tracks = [ | |
| ['song' => 'Gangsta\'s Paradise', 'artist' => 'Coolio'], | |
| ['song' => 'Waterfalls', 'artist' => 'TLC'], | |
| ['song' => 'Creep', 'artist' => 'Radiohead'], | |
| ['song' => 'Kiss from a Rose', 'artist' => 'Seal'], | |
| ['song' => 'On Bended Knee', 'artist' => 'Boyz II Men'], | |
| ['song' => 'Fantasy', 'artist' => 'Mariah Carey'], | |
| ]; | |
| // ... lines 23 - 27 | |
| } | |
| // ... lines 29 - 42 | |
| } |
What happens if we try it? Ah, we're back to the "array to string" conversion. When we loop, each track itself is now an array. How can we read the song and artist keys?
Remember when I said that Twig looks a lot like JavaScript? Well then, it shouldn't be a surprise that the answer is track.song and track.artist.
| // ... lines 1 - 7 | |
| <ul> | |
| {% for track in tracks %} | |
| <li> | |
| {{ track.song }} - {{ track.artist }} | |
| </li> | |
| {% endfor %} | |
| </ul> | |
| // ... lines 15 - 16 |
And... that gets our list working.
Now that we have the basics of Twig down, next, let's look at the full list of "do something" tags, learn about Twig "filters" and tackle the all-important template inheritance system.
20 Comments
After following steps my homepage wont load
Hey Frank,
Did you try to follow the advice in the error message, i.e. running
composer require symfony/twig-bundle? It might be a good idea to try to remove that bundle and install again, probably it was not installed well. Keep the eye on any prompts in the console - you may need to allow some Flex recipe execution to let Symfony set up everything for you :)Cheers!
I have a problem with twig, seemingly, specifically with device viewport. I have installed webpack Encore.
I am testing some symfony code on mobile devices, and I add some code that returns the viewport width.
When I compute the raw code for my web page into the variable
$htmland my controller returnsnew Response($html), I check that the viewport of my iPhone is 393px, which is normal. On my tablet, the viewport is 744px, which is also normal.When I use twig, the twig body simply returns the window width, computed the same way as previously. I do not even input extra code. Then I get a viewport of 980px both on my iPhone and on my tablet.
I have tested this on my local ntework and also from a remote server. I use actual devices for all my tests.
This has very serious consequences when it comes to responsive design. actually, I have done all of these tests because I could not set my font sizes correctly on my phone and tablet when testing my symfony app.
All of this has been done on a fresh install of Symfony+Encore.
Can you help me?
Tia,
François
I got the answer.
The default base.html.twig file does not have the viewport meta tag in the head.
Ah, thanks for following up that you found that!
Hey, could I ask, how did you implement the changes for each string in the list when adding a song and an artist attribute?
Hey Ivangogh,
Ryan cheated a little bit by copy-pasting the new array, but he converted a simple array with many string items inside into an array of arrays where each item is an array containing two keys, song, and artist.
Cheers!
Hi
We are upgrading from SF5.4 to SF6.0 and we get this error when visiting any page:
You cannot use the "renderView" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".
We already have that bundle installed, looking to the renderView method, it seems like "twig" service is not being injected to the Controller.
Injecting Environment $twig to constructor of the controller works ( new Response($this->twig->render(...))) but this limitates us, because can not use the shortcuts provided by AbstractController and would have to refactor a lot of code.
Any idea? help on this would be very appreciated :). Thanks a lot.
Hey Samuel,
That's unexpected :) - Do you have the autoconfigure and autowire features enabled? If that's the case, try injecting the Twig service into any service class. My guess is the Twig service is being removed from the container because nothing is using it
Cheers!
Hi MolloKhan,
First of all thank you very much for your quick response.
Yep, really unexpected :/
The same project downgraded to SF5.4 works fine, but fails when changed the version to SF6.1 in composer at this point.
We have some services with twig injected, so no idea why fails at this point:
Hi,
We finnaly found the issue in services.yaml
We had to change the bind parameter => $container: '@service_container'
To the service alias => Symfony\Component\DependencyInjection\ContainerInterface: '@service_container'
We have an auto authentication functionallity in our Behat Context tests, so we needed the $container to be bind for ease of configuration.
Replacing the bind by the alias fixed the issue.
Thank you for your help.
Regards!
Ohh, so the
containerservice injected into your controllers was been modified due to your binding config, that's interesting!I'm glad to know you solved your problem. Cheers!
Upon extending the AbstractController class, the following exception is occurring:
"Controller\VinylController" has no container set, did you forget to define it as a service subscriber?I tried clearing up the caches with
bin/console cache:clear, even stopped the server and restarting it, but the exception is still showing up. What's the solution to this annoying problem, as I cannot proceed further with the tutorial?Thank you.
Hey @roenfeldt!
Ah, sorry about the trouble - that's super weird! Hmm. So, the reason why this made happen is a bit technical... your controllers should be (via
config/services.yaml) auto-registered as services and autoconfigured (but things we talk about in the next tutorial). It seems that one of those two things isn't happening... but I can't imagine why. Have you modified yourservices.yamlfile at all? What is the namespace insideVinylController? It should benamespace App\Controller- based on the error, are you missing theApp\part at the beginning?Let me know if any of this helps :). This is basically an error that shouldn't be happening - so there is likely some small typo (and a non-friendly error for the typo) or something else weird happened.
Cheers!
Hello Ryan,
Thank you for the very fast reply, as well as for the solution provided. It was, as you correctly suspected, the namespace which was missing the
App\part for some reason. In any case, now everything's working as it should.Amazing level of support, I am definitely going to become a paid subscriber at SymfonyCasts very soon :)
Six stars out of five!
Yay! Cheers and keep up the good work ❤️
Typo suggestion: common to have a directory with the same
knowname as your controller class (can't edit it on github for some reason atm)Hey Mark!
Thank you for reporting this! I fixed it in https://github.com/SymfonyC...
Cheers!
"Houston: no signs of life"
Start the conversation!