Introduction
Hello YouTube, I'm Chris from codereviewvideos.com. Before watching this video, I'd like to let you know that I have over 500 similar videos available on my site, codereviewvideos.com. If you love Symfony, then you'll feel right at home here. I'll show you how to build both websites and JSON APIs, and then we're going to talk to those JSON APIs and React and Redux.
There's courses on Docker and Ansible as well as loads on deployment and test-driven development and behavior-driven development. If you enjoyed this video, please like and subscribe and as ever, thanks for watching.
The Problem: Duplicated Template Code
Hi, I'm Chris from codereviewvideos.com and in this video we're going to continue on our exploration of Symfony 4. So towards the end of the previous video, we had set up our Twig template structure and we saw that whilst this was all settled for our homepage, when we visited the hello page, we lost all our styling. And we can understand why that is if we look at the code.
So we've got our welcome controller which has our index controller method and that has the associated template welcome/index.html.twig. And in there we have our full HTML which includes all the header tags and then our navbar, our main content area, and that all renders out. And then we also have this hello page that we set up in the first video, where this does render but we don't have any of the HTML stuff in there.
And what we don't want to end up doing is taking a full copy of that and then changing just the bit that changes. So like this bit here and we'll say in fact maybe we'll get rid of all of that and we'll just put in one new paragraph that says hello codereviewvideos.com. Then we can go back, refresh, go to our hello page, and yes, this has fixed the problem visually. But this obviously as the site grows, you know, add more pages, becomes a vastly unmaintainable solution. For example, if we wanted to add a new page in, we'd have to go into both templates and update the nav bar and that just becomes a nightmare.
Introducing Twig Blocks and Template Inheritance
What we want to do instead is extract out the common stuff and override with specific templates. And Twig provides us with the feature to do just this, called blocks. So one thing that I've not mentioned is that when we installed the website skeleton, inside the templates directory by default we get a base.html.twig. And you can see these blocks in action in there. This is a really straightforward template; there's not a lot in there at all. But it does illustrate two important things really. Firstly, there's the concept of blocks and secondly, we can have default content inside a block. So what this means is if we wish to render out a page based on the base, if we don't provide a block title that's more specific in one of our sub templates, then we're just going to get this welcome title. However, we can override it by providing our own block title. It's a lot easier to see, so let's just get on with it.
Creating a Master Layout with base.html.twig
In practice, we are going to use this base. And what I'm going to do is I'm going to take this hello page that we've just changed. I'm just going to revert it all back to what it was. And I'm going to go into base, I'm just going to highlight all and delete and paste in our generic content. Okay, so the first thing that I'm going to do is going to add in this concept of a title inside the block title. So I'm going to leave the title tags inside our base template, but then I'm going to create a new block. I'm just going to call it block title. You can call it anything you like, but because it's the title, it makes sense. And I'm going to say end block. Now I could also say end block title, we don't need to. It's just on more complicated templates sometimes it can be useful to match up which blocks are finishing where. I'm not going to bother doing that. And I'm going to set a default title here of, let's explore Symfony for.
Now I'm going to add in some more blocks. I would also do is put in a block here for block stylesheets and again add in the end block. Now it's unlikely that I'm going to have any specific stylesheets in the context of this small tutorial, but what this does allow me to do is on a per page basis add in a custom stylesheet or multiple custom stylesheets, which because of the way that CSS cascades would allow me to override stuff that's defined inside the bootstrap style or my own custom style as needed really.
And then just below the main, so just before the end of the body, I'll put in another one which says block JavaScript and again end block. I really shouldn't need anything with JavaScript at this point, but it's nice to put the JavaScript at the end or just before the closing body tag. And that's because we don't want our browser to pause whilst it downloads all the JavaScript before displaying anything else. So if we put it at the end just before the closing body tag, it's going to be processed after all our HTML is hopefully displayed at least. And then the one thing that does change is this middle section here. So maybe I could get rid of the divs or maybe I could keep them in, it really depends on your layout. In fact, what I'm going to do is I'm going to get rid of the divs and I will put them into my template, my specific template as needed. So just call this block main. You can call it again anything you like, maybe block content or something like that. Maybe we'll just call this one end block main just to show that it doesn't make any difference. So we've extracted all this stuff out.
Refactoring Child Templates to Extend the Base Layout
And now we'll go back to our hello page. Well, it's not just going to know about it and start using it, we need to define a little bit extra here. So I'm gonna take all of that, let's cut it out. Let's start off by saying that our hello page extends our base.html.twig. And then the only thing that I want to override at this point is the main block. So I'm going to say block main and remember to close the block. And then I'm going to paste in my content there. Okay, so if we go back here and refresh, it all still works, even though we effectively now have two templates. All right, a specific hello page HTML.twig, but really that extends the base and uses everything from the base. And we can take another look and see that our title has been set to the default. So let's explore Symfony for, and maybe we could go back in here and override that now with something a little bit more specific by saying block title, and again close it off. You, this is just to show that your blocks can be all on one line as well and they don't need to go across multiple lines like that. Say custom title, doesn't really matter what it is. There you go, got a custom title.
So let's repeat the same process for our index.html.twig. Well, all that we need is this section here. We also want the div I suppose. So same thing, cut that out. Select everything with command A, delete it. Extends the base, and then define our custom block main, and... making sure it's a type end block properly, paste that in. And our hello page hasn't changed. And if we go back to our home, everything still looks the same.
The Power of Inheritance: A Practical Example
Okay, so what extra benefit has that brought in for us? So if we go to our base and we'll just add in another link. This isn't going to go anywhere of course, but we'll just add it in for the moment just to show what's going to happen. Let's put hash because there's not really anything there. We'll just say a link. And now we don't need to put it on both pages, but it's there on both pages. So it's there on our home and it's there on our hello page, but we only had to do it once.
One other thing if we go to say a hello page where we've got these different blocks defined, they don't need to be in a specific order. There's no requirement there. This should all still work. Typically, the convention is to put the extends at the top and then my own personal convention is to put the blocks in the order they appear in, or as close as to what you get in your base template. So because our title comes before our main, that's why in the child template I would do it like that.
Advanced Twig: Accessing Global Variables
So as a heads-up, you can skip this entire next section and jump on to the next video if you're not interested in opening your geek skills any further. What we're about to learn here is interesting to know, but it's not essential. So what we're going to do is we're going to make our hello page a little bit more dynamic. So what we've got at the moment is hello code review videos and it's obviously hard-coded. What I want to do is instead be able to change this value at runtime.
And to do that, the first thing I'm going to do is just change the path back to /hello. This actually isn't essential but because our URL will be the most important part of this process, it's nice to have at least a short URL so that we can see what's happening. Okay, so with that in mind, I should just be able to go to /hello and everything should continue. And to begin with, what we're going to do is we're going to pass in a name, so it might be like name=Chris. And that's our query parameter. And the interesting thing here is we're not using a custom controller to render this template. So how can we get access to those query parameters? Seems like it might be a tough question to answer.
However, Twig implicitly receives an app variable. And this app variable gives us access to a bunch of interesting things such as the current user, which would be app.user, or the environment such as prod or dev, and that's via app.environment. Or you can even get access to the current request, which would be app.request. So that's what we're going to use in a moment. There are a couple of others, so if you want to know about them, please check the show notes.
Dynamically Displaying Request Data with app.request
Now this app variable isn't immediately obvious, but we can see it for ourselves by using another handy Twig function, which is the dump function. So we'll go into our template, hello page, and just above here, we'll dump out. Generally just calling dump. Okay, so let's go back and refresh our page. And now we get our dumped output. And even though we've not passed anything into our template at the time of rendering, we do still get this app implicitly passed in. And as mentioned, we get access to a bunch of things, such as app.environment or in our case app.request. We can see the available sub-request. Now we've got our query parameters, one of which, or the only one, is the name of Chris.
So let's get access to this from our template. Just going to comment that out for a moment because I don't want that anymore. So on a Mac that's just command and slash and that gets rid of that. I think it's control and slash on Windows or Linux. Anyway, we're going to put a new variable call in. I'm going to say app.request and on the request we want to get the query parameters. From the query parameters we want to get the name parameter. Okay, so let's give that a refresh and there we go, hello Chris.
Handling Missing Data with the default Filter
Now there's an edge case to consider here. What if the name query parameter is not set? Well, doesn't look good. At least it still renders. What we can do is we can provide a default. So we'll say put in the pipe and default and then we'll just put in code review videos here. So if nothing is passed in then at least we get a nice default. But we can override it with name=Bob, and then we get the value set.
So this is quite nice but it's not actually something I'd do in the real world. I'm showing you this because knowing that you can do it can be useful. But most of the time, a vast majority of the time honestly, you're much more likely to want and perhaps need to pass in variables from your own controller methods. But let's get on to how we might do this from one of our custom controller methods in the very next video.