Introduction & Recap
hello YouTube I'm Chris from code review videos calm. Before watching this video I'd like to let you know that I have over 500 similar videos available on my site code review videos calm. If you look symphony then you'll feel right at home here. I'll show you how to build both websites and Jason api's and then we're going to talk to those Jason API use 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.
Hi, I'm Chris from code review videos calm and in this video we're continuing on with our introduction to Symphony 4. So towards the end of the previous video, we created a single route which when accessed would render out the contents of our template, which was the hello page template, and return that as the body of our response. So we could see if we refresh now on the slash we see in our output. And again, you can see the output the debug routes of command shows that we have hello page setup. And we saw that whilst we didn't need to create our own controller class, we were still using both the controller class, the template controller, and also a controller method, the template action, in order to make this work.
Now this structure called template action, this was in Symphony 2 and Symphony 3 the typical name that we would name our controller methods. And simply for this, this would more likely just be template. And we'll see this again momentarily. Now I also mentioned that whilst this is quite interesting, it's quite nerdy to know about, in the real world, it's very unlikely that you're going to go down this route too often. Typically you would create your own controller class and your own controller methods and so that's what we're going to get onto in this video.
Generating a Controller with make:controller
So I'm just going to start by clearing that off with a ctrl L and then we'll do a PHP bin console and we'll put in just make. You can see the command make itself is not defined but in the make namespace, there's a bunch of different things that we can use this console command line helper to help make. Of course the one that we're interested in at this point in time is the controller, but you can see there are other things that we can make along the way. So I'm just going to hit the up arrow and I'm gonna make a new controller.
So it's asking me to choose a name for my controller class and it's going to call it the Welcome controller. So it says it's created a new controller class in source controller welcome controller. Open up your controller class and add some pages. So let's have a look on the source controller, we've now got our welcome controller. It's created as this index controller method and it's going to be available using routing annotations on /welcome.
So immediately before we do anything else, let's just control all that, do a bin/console debug:router to see if we can see this new route. And we can, and it's right at the top. So the ones that we create in YAML are at the bottom and the ones that we create using annotations will be at the top. Not entirely sure why that is, I'm guessing it's the order in which they're parsed. Just as a heads up though, you're at the top here. So /welcome should be immediately available to us. As long as your web server is still up and running, you can see you've just generated a new controller. We've not had to create this template, that's been done for us.
Anatomy of a Controller and the Response Object
So let's come back into our code and see why that is. So you can see that the view or the template in this instance actually lives in this maker bundle. And the maker bundle is provided as one of the dependencies when we've installed the website skeleton. So this at symbol before it tells it to look at our bundle, and we can come and click on a Mac, it's going to take us there. I've clicked on the bundle name rather than the demo page, but if we click on the demo page, we can see this is the contents of that page. It's got some style built in and then it's got that 'You just generated a new controller' text. And as you can see, it lives inside the maker bundle, which is in the vendor directory, which means it's code that we've pulled in, it's not code that we directly control.
So I'm just going to get rid of that for a moment and before we go ahead and get rid of this and recreate it using our own logic, I want to just firstly expand out this use block. So you can see it's come with our routing annotation, which we're using here and we're also extends controller, so we're using that one, but it's come with this third one which we're not using. So if we hover over in PHPStorm, it's going to say this import is never used. It's quite unusual it's been generated but we're not using it. So Symphony controller methods always have to return a response, and we'll see this in a sec, and this is the response object that we must return.
Now this render, if we control and click on that or command and click on it, is a convenience method provided to us by the controller trait, and we'll look at that in a moment, which does two things: it takes our twig template and converts it or renders it from twig into usable HTML in our case, and then it sets it as the response body as you can see the response setContent call, and then it returns that response object. So whatever we do from a controller action, we have to return a response. Symphony is all about the journey from request to response. That means from a very high level, your request or your visitor's request comes in for a page, stuff happens, and then that page is created as HTML or whatever and then we send it back, and that's the response. Symphony is really just a structure around that process.
So if we get rid of these two lines and we go back and we try and visit our welcome page, we're going to get a nice error that says controller must return the response, but null was given. Did you forget to add a return statement somewhere in controller? Yes we did, we just deleted it. So what we can do is we can take this unused response and we can actually use it. We could say return new Response, and if we command+p inside there we can see that the first argument must be a string, some content. This can be a bit of HTML or it can just be raw text or whatever. Second argument needs to be some status code. And then we could set some headers as the third argument. So to begin with, I'll just return a raw string which will just be hello and I'll do the sort of the naive thing there and just return an integer as a status code, which 200 which should be HTTP OK or in other words a good status code.
So we refresh and we see our hello text but the web debug toolbar has gone. So let's try and get the web debug toolbar back. So in order to do that, we have to have some basic HTML. So I'm just gonna set up the HTML wrapper, put something in the body and just make sure to close off that HTML tag. And then rather than using a direct integer, one of the nice things about the response object is that it comes with a bunch of defined constants. And we can just click through and have a look at what they are, all the different status codes that you might want to use. So the one that I'm after is HTTP OK. Of course, using an integer is no different really than using the defined constant, but the defined constant does explain what that status code actually is. So let's just try and refresh that now, and we can see that the web debug toolbar comes back for us and that's because that's injected for us behind this, but it needs to rely on that closing body tag.
Using AbstractController and the Controller Trait
So we'll see in in a sec when we get to it that when we render out our Twig templates, if we forget to render out any of the expected HTML, so just the HTML and the body wrapper, then we'll still miss out on the web debug toolbar injection.
Now if I get rid of that and I do a return of this render like we had before, then you may be wondering where render comes from. And I kind of alluded to this when we control-clicked and we saw that it was available to us. And it's available on the controller trait, but where's the controller trait being used? So controller trait is just something provided by Symphony. It's in this controller directory, but where we get access to it is because we extends Controller. So if we click on that and we consider we extends Controller, and Controller is just an abstract class from that same namespace that we've just been looking at where our controller trait lived. And we could see that because we extends Controller and Controller uses the controller trait, that's how we get access to this render.
Now, again, just like when we created this and we got this unwanted use statement, so let's get rid of that for a moment, the way in which our controller classes are created is a little bit unusual too because instead of extends Controller, it's actually preferable inside the best practices to extends AbstractController instead. So we'll just change this, or they both live in the same namespace. So again, we can just click through to that and we can see everything's still inside this controller directory. We're now extends AbstractController instead of before we're just extends Controller. They're largely similar, it's just AbstractController is actually a little bit more restrictive. It stops you from accessing the container directly. It instead expects you to inject any necessary dependence. So this is getting a little bit more advanced, we don't need to know too much about that at the moment. We're gonna cover that a little later in this course, but just know that it's better to extend AbstractController. So if you're gonna generate them, first delete that unwanted or unused use statement and also then change up to be extends AbstractController. And again, that's what gives us access to all these convenience methods, such as being able to redirect and easily add flush messages and stuff. And again, we'll get onto all that a little bit later in this course.
And get rid of that response object as well. And next, I'm going to my own, and I'm going to start by defining a new template. So on the templates, just get rid of that vendor directory that's taking up a lot of space. I was going to create a new directory under here which I'm gonna call welcome simply because this is the Welcome controller and it matches up so it's easy to figure out where stuff is in the future. Go in lower case, it's convention. And I've got the index controller method so I'm gonna name this the index.html.twig template. Again, it just helps tie up your templates to your controller methods, and so on. As your project grows, that is helpful.
Rendering a Custom Twig Template
So I've got a template already. This is using bootstrap 4, it's a pretty straightforward thing. I've just basically stolen directly from bootstrap and made a couple of little tweaks. It should have a little bit of a navbar going on which has our home link in there and the href is not correct at the moment. And also we've got this hello page which is what we created over there before. So we're going to set this to be /hello in line with what's in the template. And I'm going to make my welcome controller just be the site root. So whenever I hit the site root, I should hit the Welcome controller, and specifically if I go to /hello I should see this earlier page that we created.
So let's go ahead and render this out now. So we've got our return this render call inside our index controller method, and this is now inside the Welcome directory on the index. So I'm just going to render out welcome/index. Okay, so we're on /. Let's just bring that back up to make sure that everything is looking okay. So now things have swapped round. We've got welcome on / and hello_page on /hello. So it's gonna go to the site root and we can see a basic page. Be nice maybe to push this text down a bit and to do that we're gonna need a custom style sheet.
Adding a Custom Stylesheet with the asset() Function
Now there's a couple of ways you can do this. I'm going to show you the easiest way but Symphony 4 is pushing webpack encore which is a little bit more advanced than what we're gonna need here. In a real world project when building a real website, you may have heard of web pack and you may also have heard how complicated web pack is to set up. Well, symphonies web pack encore is all about simplifying web pack. It also changes the way that you work with your assets, so for example you CSS and your JavaScript and any other stuff like s CSS or anything like that inside a standard Symphony project. And if we go into that, that's like kind of another rabbit hole that we go down and it also involves you having node installed on your machine, which you may already do, you may not. But to keep things as simple as possible, I'm just going to go with a more basic approach. And if you look inside the write up for this video, you'll find a link to webpack encore and there's a nice little sort of do-it-yourself try adding that in. It is very useful, but it's just a little bit outside the scope of what we can cover in this particular video.
So because we're not using webpack encore, we're going to need to do things a little bit more manually. So in previous versions of symphony, you had the web directory and the web directory was anything that was publicly available. In symphony 4, we have the public directory, which is basically exactly the same thing. So because our style needs to be publicly available, I'm going to create myself a new directory under here called css. In here, I'm just going to call this my_style.css. Okay so if we look inside our index.html, the content that we want to push down is in the main block. It has the class of container and a class of main. So I'm just going to use that class of main. I'm going to define a little style that says margin-top, push down by 20 pixels. OK, style's not my all-time favorite thing honestly, but that should be good enough. Now in order to use this, get rid of that, confusing myself, get rid of that as well. Go back to our index. We need a way to call that style sheet from our HTML. So I'm going to put this after the bootstrap style, and the reason I would suggest doing this is because firstly, all the bootstrap stuff, all the bootstrap CSS should I say, is going to be processed and then we want our CSS to be able to easily override the bootstrap styles if needed. We don't need to in this particular case, but order in does matter with styles. So if it should come later, it just stops you from having !important behind all your styles. If you've ever got into that situation, you'll know what I mean.
And so we'll need to add in a new link tag, we'll call it rel of stylesheet and then the href. You might think is just /css and then our style, I think it's just called my_style, so like css/my_style.css like that. And that would work, but it's also problematic depending on the hierarchy, the hub set up with twig. That may not work depending on where you are in that hierarchy. A much better way is to use the asset function, which is a twig extension function, which is provided by the website skeleton. But if you're not using the website skeleton, you would need to do a composer require asset to get access to this function. So I'll say asset and then we just pass in the path, so it's just css/my_style.css. And this delegates the hard work of figuring out where we are in the hierarchy to the asset function and then that should mitigate any circumstances where our paths break when we're nested. If you don't understand any of that, don't worry about it. I would just suggest always using the asset function anyway. As you can see, our style has been properly applied.
Dynamic Routing with the path() Function
Now there's one other thing that I want to cover before we finish up in this video and this is something I think is really cool is part of Symphony, it's not actually new in symfony 4 but all the same this is a really useful feature that I've not seen in other frameworks that I've used in other languages as well. So if we go back into our template, I said that we have this home path which is not quite set. So maybe it should be /, that makes sense. And we know that our other route /hello is also /hello. Okay, so now if we go back to our page and we refresh, we should be able to hit hello page, which comes with its own set of problems, and we're going to address this in the next video, and if we go back then our home should be working as well because that should just be a /.
Now the thing is, we can change these paths. We've already seen this throughout this series. We could go back in and change this to hello/page or hello-page or some other nested path. And we could even change the route if we were so inclined. So what symphony does is it provides us with another one of these helper functions called path. So instead of just using a hard-coded root, we can use the root name. So again, if we go back to our controller, we can see that the root name for the site root is actually welcome. So if we use the path of welcome, now when we go and change that actual path, anywhere that's referencing path: welcome will automatically update. Likewise, we should do the same here. And I think this was just path of hello_page. Okay so if you've got the symphony plugin for phpstorm, it's going to provide you with the autocomplete on some of this stuff, so highly worth getting, got a video on that side as well. Okay, so we should be able to go back, give that another try and save the page. If we refresh, then we should see that these links still work. That's all good. And to illustrate this point, what I'm going to do now is I'm going to go and change the hello_page path to be something different. So under our root channel, if we go and change this to be hello something different and then just go back and refresh this, we didn't need to change it inside twig, but it's already picked it up as you can see at the very bottom of your screen. We can browse that and the path updated, but we didn't need to change anything in our template. Whilst that's really useful when you've got a template that's, you know, quite simple like what we've got here, it becomes even more useful the more that you have these links dotted around his side, which on a real site is probably going to be in more than one place.
So just as a big heads-up, don't hard-code, use the path or if it's an asset, use the asset. Now as I mentioned, we've got all this nice style set in here, and yet if we go to the hello page, then we've lost all that style. Now the last thing that we want to do is have to copy-paste all of this stuff into multiple different templates. There is a better solution to that and we're going to get onto that in the very next video.