Transcripts
1. Introduction: Hello and welcome to this course on PHP. Microphone works using Slim if you are brand new to Petri and Michael frameworks and great , because we'll be starting from the bottom up. So we really start with the basics and build up a really simple applications live and then go on to scale that to more advanced uses. If you're an existing bitch, be developer already familiar with over frameworks, then wonderful. You're gonna love Slim because it's super simple, really easy to get started, but scales incredibly well. So if you want to build bigger applications, I have built bigger applications, including e commerce stores, complicated content management systems, customer management databases, everything slim scales. Really well, so it starts off simple and get beautifully bigger and will help. So I really think you're gonna love it. Once you get Teoh, you slim, you probably won't see the need to use over frameworks because this does almost everything you need. I'm Chris. I've Bean, a software consultant. For over 15 years now. I've worked for multinationals for startups. I love programming, and Slim is my personal favorite framework of choice. So I can't wait to teach this to you. Let's get started
2. What is a microframework?: in this lesson will look at what a microphone work is. It might help if we start by looking what a framework is because we could start building every website from scratch on doing everything ourselves, handling the request on the response and all the routing and the template ing. But, ah, lot of this stuff is shared across all web applications. Basically, every web application is going to need some kind of system of saying, okay, what you are well, is the user requesting and where we're going to send them to what we gonna do when they make that request? And so to savers from having to do that people came up with frameworks. Andi. They do things like routing pre processing, the request returning result, managing all the dependencies, having some kind of template ing system managing session variables. And there are a bunch of popular frameworks at their symphony on DLA. Ravel is end used to be more popular. It's declined recently. Code igniter cake, ph. B. All of these come with a torrent all functionalities to make our lives way easier. But they're often quite big and bulky, and they give us all this functionality out the box and maybe that we want some of it, but we don't want over bits of it. We have our personal preferences for how we want to do template ing or session management or this gits to must have thrown in that, slowing it down a meaning. We have to do a load, bootstrapping and write an initial code and these big controllers that we don't really want to do. And we just want something super simple for the next generation of things that came out on micro frameworks, which stripped away a lot of that. They said OK, the rial core component sees is basically the Reuss ing and a few other things on. As long as we can do that, we want to keep it really simple. We want people to L to write a one file Web application where they can say OK, if a user goes to this route, then return this response very popular with AP eyes, for example, we don't need all that big, complex session management and template ing a lot of time, and so it really got condemns down to this super simple framework. This meant that microphone weeks or much quicker and easier to set up as we discussed you can often do in warm file will probably want to use more to make it look cleaner and just easier to manage. But we could shove it all in one file if we wanted to. They tend to come with last bundled functionality, so as we'll see with Slim, it doesn't come. We have any kind of database engine or template ing engine, but it does make it fairly easy to plug one in, so that if we want to do that, we can. Andi, they often scale very well, so people sometimes think that microphone works only for websites with a couple of pages, but actually especially slim with the power it has is, you can write a full Web application with complex things like authentication on and complex business logic, and it actually can. All fit within. Slim will adapt, and you have to go get those services yourself. But they plug very nicely in so microphone works are in an incredibly powerful tool in that you can start very simple and build up quite complex Web applications, just as you could with a larger framework, but with a lot more customizable and just a lot easier to get started and quicker
3. Our first application: in this module. We're going to write our first Web application in slim, and it's gonna be the classic hello world. So nothing super exciting, but it will show is the foundations. And just how easy is to write a slim app before we move on to more substantial projects?
4. Routing concepts: Let's briefly look at the theory behind rooting. So essentially, we're gonna create ah, Web application based on the microphone work on, we're gonna watch signing a bunch of routes to it. In the case of Slim, it's by just calling a function based on the verb we want and then passing a u r l and a callback. So, for example, if we want to get request, which is the standard get post put dilate in this case, most of our requests are gonna be getting maybe some postal cast and so we'll call the get method on the app will pass it the string we wanted to match. So if you're a flash page two, we wanted to read to this function. If you're the u r L. Slash paid free, then we wanted to route to the silver function on the second parameter that we're gonna pass in is telling slim phone work. What should we do when we match this u r l So this could be a anonymous function? Could just Or it could be a named function, a controller class which will explore later in the course. But basically we're going to define the route by telling us what it wants to map and then what we want the framework to do when it gets that match.
5. Installing dependencies: The first thing we're going to do is install the dependencies using PHP dependency manager, composer. So to do that, we're going to come up here to the terminal or command prompt if you're on Windows. And we're going to say composer and we're going to require, and we're going to need a few libraries here. So we're going to need the main slim library. We're also gonna need slim dashed PSR seven, and slim HTTP. You could technically do it without those. And if you wanted, you could just bring in Slim dash slim, and you could bring in your own HTTP and PSR seven libraries. In practice though, we never want to do that. We always want to bring these libraries in because they work really well with slim. So it was only if you were say, migrating a legacy project, I had to have a say and PSR seven library that you would need to do that in practice. We can just bring them in every time. So we'll just let composers spin away and downloaded everything we need to. Okay, that looks great. Cool. So it's downloaded all of these dependencies. And if we go into our project, and now we can say we've got this composer file generated with the libraries we asked for. Then we've also got this composer dot loc. That's the one that tells composer exactly which libraries we've downloaded, including all of the dependencies. So you would never edit this file. We'd only have read a composer JSON. This one's auto-generated by composer. And then we've also got the vendor file, which contains all of the dependencies themselves. Now right now, if we made everything to get that would include our vendor folder and we want compose it, download this every time we install it on a new computer. So let's go ahead and create gitignore file for this in a minute, which is in the root dot git ignore. If you're not going to be using get, this doesn't matter too much. But if you are, you're probably familiar with gitignore and we're just going to tell it to ignore the vendor directory. So here we've got our dependencies downloaded and we should be ready to start coding.
6. Our first page: We've installed all the dependencies we need. So now let's go ahead and create our first page. So we'll create a new PHP file. And we're going to call this index.html HP. I'm gonna create a new directory called public and save it into that. And then we're going to need to bring in a few things. So let's load in some namespaces. Yep. I'm gonna need the response interface. Load the initial response. And then and we'll do the same the request interface. And I'm also bring in the slim factory. And then we also want to load in all of our dependencies and compose them. Makes it really easy to do that by just using the auto load. And then we'll go ahead and wrap. So we'll call it app. And we'll ask the app factory and call the create method or not. And then we'll define our first rate. So we'll call the get method because this can be a GET request. And we'll just make it slash for now. And then we can define a function to say, okay, if you match this slash route, this homebrew run this function. And we're going to take in the request and the response as parameters. And then inside this, we're going to say response get body. And we're going to write some text. So let's just do a Hello World for now. And then we'll return the response. Once we've defined our roots, we can call the Run command. So let's go through what we do in here. Hey, we're loading in some namespaces and packages. We then making sure we include all of the dependencies that we've installed via composer. We're creating our app here using slims app factory create method. And then we define our roots. And we're saying if anything matches dot slash, so the homepage run this function. And this function is going to write hello world to the response and then send that response to the browser. And then once we finish building everything for the app, we call the run method to tell slim, hey, now this is ready. And you can go ahead and execute the app.
7. Running the project: We've written our first root, so let's go ahead and run it and see if it works. So we'll come back to the terminal or command prompt. And we want to go in to this public directory, which is what that's gonna be the web root of our application. So all our code will be kept outside the Web roots. If anything went wrong, we will expose our code, but this bit will be public. And we can use PHP is internal development server for this. So we can type PHP dash S, and then we'll ask it to run a web server on localhost colon 8 thousand. And great. It's up and running. So now we can go to the browser and just type in localhost 8 thousand. And there we go. We have our HelloWorld that's working in a browser. So we've created our little root hair slash hello world. We've run PHP is local dev server and it's worked. Not terribly useful, but you have now successfully written your first application in slim. So now that we've got this running, let's go ahead and do something more interesting.
8. Routes: Now we've got our first route. Let's go ahead and add a second one. And this time let's do something a little bit more interesting than juts a slash. So let's say hallo slash Jane is the root. And again, we'll define a closure, an anonymous function. And we've got a body again. And this time we'll say, Hello Jane. And then we'll return the response again. This looks just like the top line, except this time we're given a route of slash hello slash Jane. Let's add a semicolon there. And now if we go back to him, we go slash hello, flash Jane. We could anew root, Perfect.
9. Route parameters: So this is kind of useful. We're now able to define various routes. But a lot of the time when working with PHP, we want these roots to be dynamic. We want to be able to pass say, a variable and maybe it's the URL of a blog post. What we can do that using parameters. So again, let's define a get request. And this time we're going to say hello. And I'm gonna put its name in curly braces, which tells slim that it's a parameter. And then we'll define function again. And this time I'm going to ask for a third, which is an array called 4xh. And this will give us the arguments, which in this case it's the parameters sacrificing name here. And we're gonna capitalize it. And then I'll access the arguments array and pull out name. So whatever someone puts in the URL here, say you could do slash hello slash Chris. Then that will then be passed through into the 4xh array. And we will capitalize it here and be able to access it. So let's write that into your response. Let's get the body again. Let's use. So we'll say Hello, pass a name. And then we'll return the response as usual. And I will need to close out. So we can pass now any string into here, any variable using the URL, and it will reach to this function. So let's try this. And we're missing because I've added an extra that we go we've got hello chris, perfect. And I could change this or I could say hello Sam. And cues give up to Jane. But if we go to Jane, still going to be written off this because it will cascade down the roots. But if it doesn't match any of these, we cannot. Dick's wildcard here, where we can just pass anything in. It's taken the name parameter here is passing into the array here, and we're pulling out the array and we can do whatever we want with the function. In this case, we're just writing it to this string.
10. Services: in this module, we're going to look at dependency injection. With any web up, you likely have a bunch of services such as database connections, sexually management emails that you want to connect out to and be able to bring in. And how do we manage all those dependencies? Well, Slim doesn't have a built in dependency injection container, but it makes it really easy to import one. So in this module, we're going to do jumps that and configure some services.
11. Dependency injection: Let's start this module by looking at some of the theory behind dependency injection. So if you've got a Web application and probably needs a bunch of services, there's a database you might want to connect to. You want to render out some templates, maybe hates email. You want to manage some kind of session data, and maybe you want to send transactional emails or put people into some kind of email provider like mail chimp. There's all of these external dependence is that you want to work with. Now. How do we make sure, for example, if we wanna have a database connection? Does every class have their own database connection that's horribly inefficient? And so back in the day, we used to use the singleton pattern. If you're old enough to remember that Andi, every time you wanted to use the database, you'd have to call the get instance Method, and that would mean that we could you could only ever create one database connection. And it was horrible because it was just impossible to unit test was the big problem on DSO . We moved over to the solution where we started injecting dependencies. So when you create a class such as this controller, you would pass in the database instance so that when we're unit testing it, we could pass a mock in there on that would allow us to do all the testing we needed. That's fine, but it means that you need Teoh. Have all of your dependencies injected into the class. So every time you write a new class, such a controller, you have to pass everything you need in. And that could be a lot of stuff. In a big Web application, there were a lot of services, so the newer model is to use what's called a container and in a container, it knows how to access all of the services you need. So, for example, in this code here, we're creating a container on were registering a database service on were saying, If someone wants a database, here's the class you need. Here's how you Winston she hated you create its database class and an inside of wrap. We can speak to the dependency injection container and say, OK, get me the database, and only when we call that service does it contain ago and then create the instance off the database, but we don't need to pre register that into the class. We can just start coding and say, Right, we need to database on our dependency injection container. Our service Lehrer knows how to get up for us.
12. Installing a DIC: Slim no longer contains a dependency injection container used to come bundled with 1-bit. The new evasions of slim doubt. So if we want one day, we're gonna have to go ahead and install one. So that's pretty easy, which is here in our terminal. And let's go back to our project. And we're going to ask for it. So we'll say compose it, require PHP dash, dash d pi. So this is a third-party dependency injection container. It's the most popular in PHP, so it'll do well, will compose a go ahead and install that. Now. I'll just take a couple of seconds. And it'll download all the dependencies. Php DI needs. Great, so that's done. And if we take a peek in our vendor folder, we've got PID P d i in that. Great. So here we are in an index file and we can set up our dependency injection contaminants. So we'll bring in important namespace. And then just below where we've brought in our dependencies, we'll go ahead and create a container. We want to do this before we create our application. So we're gonna create a container and then we will tell slim that we want to use this container. So here we are in a factory. This is where we're creating are slim up. But before we've done that, we've created this dependency injection containing a pair. And we set, told the AP factory that when we create an app, we wanted to use this container. And thus when it creates the up-down here, it's already using this, which is great. Obviously there's nothing in our dependency injection container right now, but it's set up ready to use so that when we've got some dependencies, we want to inject into the app, some services, we can do that. So let's do that in the next lesson.
13. Installing a templating library: So in the original project, we just got the response and we wrote some raw HTML to it. There's no actual HTML tags in habitats where we're doing a Widgets inviting raw HTML tags. And it would be nicer if we have some kind of templating engine or view engine that could do that for us. Slim doesn't come with one, so we need to provide one and we can use whichever we want. But for the purposes of this course, we're going to use one called moustache. So again, we're gonna go back to the terminal or command prompt and we're gonna do composer require, must ask slash mustache. And will give composer a couple of seconds to bring that in. Mustache is a really simple template in language. So it be great for this course intended learning because it's pretty easy to understand, has minimum logic in the template. Great, so that's in, Now let's go ahead and install this in our project.
14. Configuring the DIC: So we've got our templating and we've got our dependency injection or Service Manager installed. So let's, first of all, let's get rid of these and then appear in the container. Let's configure this so that when we want to access a templating service, we can, so we'll call contain a set and will again define an anonymous function. Haha, like to get my semi-colons and early so we don't forget about them. And we'll say new mustache on the SQL engine. And I'll pass them config into here. We'll go through this in a minute. For the moment. Let's just get a code down. Okay, that looks good. So we are setting a service called templating. That means that when in, when we define our roots and we try to access the templating serviced, it will execute this function, and this function will deliver us this mustache engine, which is the mustache templating service. Now you might wonder, Okay, we've imported all the namespaces appear for everything else. Why don't we need to do that with mustache? And that's because mustache is in the root namespace and we're in the root namespace here. So in say, this file, there would be a definition at the top that says namespace deal.II. And then it would say like class container. And that's why we need to import it from this DI, namespace. But the mustache engine doesn't have this namespace declaration and so we don't need to make a statement for au pair. Secondly, What are we doing here? Well, we're telling mustache where to load the template from, and we're going to create this template's directory soon. So we're saying generate me a mustache engine. And when you need to load the template uses the file system loader and load it from this location and don't automatically had any extensions. So you could, if your templates are all going to be called dot HTML, you could add it there. But I think it's easier to define this as we go. So we'll load it from the template file and we'll just load the file name, we give it n. Let's look at how this, we actually use this in the next lesson. And then this will all start to come together and make a lot more sense.
15. Using the templating service: Now that we've configured templating service, let's go ahead and use it. So we'll come down here after we've created the app and we'll create a route and will say hello, name just like we did with the original project. And again, we'll define the anonymous function. So we'll take in our quest and our response and some arguments. Okay, nice. And will say we get the templating service. And we'll call the random method, which is a mustache method that says render some HTML. And we'll give it a template name, hello.html. And then we'll pass in variables as well. So the way you render template is you tell mustache what template you want to render and what variables you want to add an in this case, we're just going to, I'm going to pass him one variable called name. And we're going to pull this up the arguments, which of course is gonna take this head that we pass in. So that's going to generate this HTML string. And then we can say response, get body, right? We'll write our HTML and then we'll return the response. Okay, that looks pretty nice. Let's go ahead and create a template in the next lesson.
16. Creating a template: Let's create a template then. So this is really easy. We're going to use a height one tank. I'm gonna say hello. And mustache uses this double curly braces system for any variables that you want to pass in. And literally that's it. So we can save that, call it hello.html. And in our workspace we are going to create a templates directory and save that. Great. So we look at what's going on here. When we call this route with saying, Get to the template and service, render this hello.html, which we said look in the templates directory, passing this name variable. And then a template. We've got that set up pair. So this should be all ready to go now.
17. Rendering a page: So we're now ready to run our projects or just 0.1 tiny syntax error. This should be adult. That looks good. So we'll go into it and we'll go into our public directory. And again, we'll run PHP is local development server, so we'll do pitch P dash S, localhost, 8 thousand. Okay, now we define the roots hello slash name. So let's try slash Kim. Perfect. So we've got an H1 tag. So we're passing in the name here as could be any name here. That's getting passed in here as an argument. And we take an argument with sending it to mustache to render this hello template. We're dropping in Haha, Great. So a templating system is now working.
18. Controllers: we've already written some pages on. We've brought in a service to help us lend to those pages, but at the moment it's not super scalable. Its most of its in one file. And if we wanted to write bigger application, it would get a bit messy. So how do we do that? Will slim scales really nicely because we can use controllers in the same way that we would use controllers in a bigger framework. But they slot really nicely into slim as well. And so in this module, we're gonna look at how to do that.
19. Creating a controller: Being able to stick all our code in one file is useful if we've only got a small project and keeps things nice and simple, what happens if you've got a bigger project? If we've got 20-30 roots, then suddenly index.js HP is going to be huge. So instead, we can break things down into separate files and we can use controllers, jerks like you could use a controller and a bigger framework like symphony or Lera val. You can use controllers in slim as well. It's your choice whether you want to put everything in one file or split up. So let's go ahead and create controller. And I'm just gonna go ahead and save this at first controller. And I'm gonna put it, I'm gonna Chris source directory. And then inside that I'm going to create controller directory and save us first controller. Okay, great. Nice. So first I'm going to give a namespace and then we're going to want to bring in request response. Let's just grab them. And then I'm also going to bring in this container interface and we use this valve damage as well. And then I'll just define this controller. So when useful thing that happened with Slim is that when it creates this controller forwards, it passes in the dependency injection container, the container interface. So the CPU is full. We can save that to this instance variable here. And then we, when we want to access our services, access our dependency injection container favor down. We've got it on this variable ready. Well met his private because we don't need anyone else to access it right now. And then we'll define a method. So let's define a homepage function and slight the anonymous functions in index.html. We're going to get a request and a response here and will access our container. And again we'll say get templating, say render homepage that HTML, Tony to pass any variables. And at this point, and again we'll say get body and call the write method, pass the HTML man. And it would help if I could spell return correctly. Ok, nice. So instead of an anonymous function, now we've got this controller with this homepage method in, in the next lesson, let's go ahead and create this homepage template.
20. Creating the templates: We've created our controller here. So let's go ahead and create this homepage template. So I'll create a new file here and we're gonna use some includes. So the way we do that in mustache is with the curly braces and then greater than arrow. And then we'll put a template. And then we'll create the main body. Say, welcome to my homepage. Thanks for visiting. And I will use that include syntax again to bring in the footer. And we'll save this as homepage HTML. And we'll put that in the templates directory. Couple more things we need. So we need to actually create these headroom footer. Now you could create those manually, but you can also just copy and paste them from the sample code. So if we go in wind project three here. So let's go ahead and take this header. And we'll just save this as header wherever you're working. And photos should be pretty straightforward as well. Again, we'll draw that in. And one of the thing that we want to watch out for is n-x header includes this style.css. So let's go ahead and grab that from free as well. It's in the public directory. So style.css and I know workspace. We'll save that in the public directory. So that's, I'll make things prettier as well. Great. So we've got our template here, we've got our controller here. We just need to wire everything up now.
21. Autoloading: In the olden days of PHP, once we've got this first controller, we might come into here and is something like require sauce can roll. First controller. But this is a real mass because then the code is tightly coupled and really difficult to test. So instead, we can now use auto loading based on the namespace because we set this namespace or payer. So let's go ahead and configure that. We need to come down to our composer file. And we're going to add in a new section here called auto load. And it's going to be a PSR for and we're gonna say at slash slash. So anything, any namespace that begins with an app, you should auto load from source directory. So that means that when we try and read Reference app controller, first controller, it's gonna say, okay, begins with an app. So look in the source directory, and then it'll say, OK, it's in the controller and it's the first controller. So that means that PHP will we help to magically find it. So the next thing we need to go off to our terminal and just update composer. Let composer. Now that's happened. So let's get back to the route here and let's do composer dump, dash auto load. We can also fully regenerate the composer files. But if you just want to update the auto loading, then composer dump dash Autolite, we'll do what we need. Okay, great. So that's generated some new autopilot files and now we should be ready to wire it up.
22. Routing to a controller: Let's go ahead and wire this controller. So we'll get rid of this old code from the first project. And we didn't define a get root again. And we'll just make it the root. So normally here we pass an anonymous function, say, Okay, if you match this URL, then deliver this. And we're going to do a similar thing here, but instead of making it function, we're going to pass in a class and a method name. So we're gonna say app controller first controller, that's the full namespaced path. And we're gonna say colon homepage. So this colon is slim specific syntax saying this is the class, this is the method on that class because that's what we call here that we wanted to execute. And that is literally all we need to do to wire it up. So let's go ahead and blew up the development server and see it running.
23. Testing the controller: We'll come over to our terminal and we'll cd into the public directory. And then as normal, will tell PHP to site development server. Greatest running on localhost 8,000.5, we get a localhost 8 thousand. Perfect. We've got our homepage. We've got our homepage rendering out here. And it's even more in our header as well in our style sheet. And everything looks good.
24. Abstract controllers: This looks like it's working great. But at the moment, it would mean that when we create this controller, we'd have to do all this code every time. It'd be nice if we could simplify a lot of this. So what we could do is create an abstract controller to do a lot of that forest. So let's go ahead and do that now. Let just take this as a base, call it controller dot PHP, and we can work from there. So I'm gonna make this an abstract class. So when we can't instantiate directly, you have to extend using another class. And we'll call it controller. And we'll change this to protect it because we want our child classes to be able to use it. And then we'll get back to this homepage method here. And we'll create a function called render, which will do some of the heavy lifting in terms of rendering HTML. So we'll take a response, take a template name, and we'll take any data and variables a wants to bashed in, which by default will be an empty array. And then we'll do much the same thing that we do in a normal function and we'll call the random effort. Okay, that looks good. So that's a controller we can now extend from. And let's go ahead and create controller that uses that. So we won't be needing that. And let's call the second controller. Great, we'll save that. And we don't need this code anymore because that's in their parent though extending. So we're going to extend controller. And let's get rid of nest as well. And now we'll create a normal function. So we'll call this hello. And now we can use that special method we create on our base controller. So I'll pass in the response object. Template will be called hello. And for now let's just hard code this. Okay, that looks pretty good. So we've got our second controller that extends our base controller based controller sets up things like taking the dependency injection container and putting in an instance variable. And it gives you this random effort, does a lot of the heavy lifting of using that forest. So that here, all we have to do is say, okay, it's gonna be hello. And we just want to highlight name quests. And then we just need that template file name. I have this hanging around here, but let's bring it up to speed. So we bring in the header and footer, or if you delete it, you can just create it from scratch. Okay, deluxe, perfect. So we've got our basic controller, we've got our new controller, and we've got our template. Let's go ahead and wire all of this up.
25. Testing the abstract controller: Let's go ahead and wire up this second controller. So we'll come here to our roots and we'll define a new route. And just say hello. Again. We'll give it a full path. So it's the second controller. And the method was called hello. Right now if we go over to our development server, we gotta hello. Perfect, so we've got hello chris, that's the variable we passed in and we've got a header and footer and working as well. This is pretty similar to what we did at first. But if you remember in first controller this quite a lot of boilerplate code here that's going on here and in here. Whereas this second controller, now we're hiding a lot of that into this abstract controller. This is much prettier because it's literally one line to render a template. We don't even need to touch the template in service in this case. But we're still going to keep this as protected so we could access over services here if we want, and we will later in the project. But it just makes it super nice and clean when using this abstract controller and ChildController pattern.
26. Request and response: in this module. We're going to look at how we modify the request and response objects. So what happens when we want to pull information out of the query, string or form submission? What happens when we want to send different forms of data back, such as Jason? Or maybe a redirect will look all of that in this module, and things will get a bit more interesting on the product. Front will write something a bit more substantial and that we're going to create searchable records, Got Lord called Rockin Records.
27. Rocking Records: In this project, we're gonna create a searchable Music catalog called Rockin records. There's a couple of things that we need to do before we get started. So if you go into Project for the data directory and there's an Album's dot JSON. We need that. So just save this as albums to JSON in a data directory. And in templates. We've got this updated header. Let's go ahead and copy that across. And finally, in the public directory, there's an updated style sheet as well. So bring those three files in and then we're ready to get started.
28. Creating the homepage: In this lesson, we'll create a homepage for record catalog. So we can go ahead and get rid of this first controller. And oops, area. It's just rename this one. And we'll call this search controller. And let's rename it here to get rid of this method him I ran from scratch. So we'll say public function, let's just call it default. It's going to be the default page, the homepage. And much be requests. And I won't say response. Balanced formula nicely. There we go. And then here, let's get that list of albums. So we use adjacent decode method and the file get contents. And we'll go into the data directory and we call the album dot JSON. So JSon decode, you can have it as an object or an array and we want to as an array. And then we'll go ahead and render a page here. So we'll pass in the response will default HTML. And we're gonna pass in the albums there. Right? So we've defined the root function. We've said get that array of albums and pass it to the template. So let's go ahead and create a template for that as well. Look pays standards as well, so we'll say header. And if we wanted to do a loop in mustache, we use this hash and a slash to end it. And then we'll bring in our footer down here as well. Save this and templates. And then inside here for each album, Let's create div. Let's print out the title and the artist's name as well. Okay. Well, that's good to me. Final thing is, and let's wire this up. So we'll go into our index.html, be given to this old code and we'll say app. And we'll define a get route. It's gonna get the app controller, search controller default. Okay? And if we go back to our development server and perfect, There we go. So by default we've got this homepage, Rakim records, and I've got the artist missing here. So let's go ahead and troubleshoot. And there we go, Mr. T, I am perfect. So we've now got the album name and the artist's name displaying on the same page.
29. Searching with query strings: We said we wanted the catalog to be searchable. So let's go ahead and allow us to search with query strings. So we can just copy and paste this method and we'll call it search. Okay, now i, so we're still going to need to load our albums in. But we're also going to need to get the query string. And that's gonna be called Q. So hey, we'll pull the q query string variable from the request. And then we'll say, Okay, if there's a query, then we're gonna filter this array. So we'll use array values. Filter nouns to be album. We've got fitness him And it once we've got the code down. And then down here, render out, which is the template we're going to create. Okay, so what we're doing here, well, if we get a query, if we don't get a query, we're just going to ignore it and we turn all the albums. But if we do have a query, then we're going to take this albums and we're gonna filter it based on whether the query is in the title or in the all test. So now we can go ahead and create a search template. It's going to the templates folder, got default. Haha, let's just copy and paste this called search.html. And we'll go ahead and quit phone. Okay, not looks nice. So we've got assets from here though, the albums, we've got a method here. So let's go ahead and wire it up.
30. Running a search: Let's go ahead and wire up search from Chin and check it works. So we'll open up index dot PHP and we'll define a new here. There'll be to get Ru slash search and wire it to app controllers, such controller. And we call the method set. Great. So we'll go over to our development, whatever. We got a slash search. This looks good. So we've got a search box here, and we've got all of us albums here doesn't know whether being filtered. And let's go ahead and search for I come for hire. And perfect. It's filtered everything down to just the albums with the Albo name of the artist in this string. So that's working perfectly.
31. Form submissions: So in this first example, we've used a query string. So when we make a set, it appears appear in the URL. But what happens if we want to do a form submission using post? That's quite easy to do. Let's go through how we do it. So we'll start with the search template. We'll make a copy of that and we'll just call it formed our HTML. And we'll change the method to a post method here. And then here we'll copy and paste this search method. And again we'll call it form. And this is gonna work almost exactly the same, but because it's now a post variable rather than a query string instead of get query parameter, which is going to say get Program. And we're going to need to change this to you on new template. Okay, lovely. And then in next step, PHP app control set controller form. So if I go to form now, B, a colon, there we go. Okay, perfect. And these looks okay, but actually if I run a search here, it's not going to work. Because when we define the route, we defined it, the ghetto and now we're trying to post to it. So we could change that to post. But then when we just try and load it normally is gonna say it's not a get rw is the post room. So instead, we can use the verb any. And that will allow both GET and post. So we gotta do it now. But it's fine. And putting a search term, great. So it's posting it now it's not in the URL anymore, but we're still able to access it. It's a lot looks perfect.
32. Slim HTTP docs: You might be looking at some of the methods we're using here like this. Get query parameter on the request object and thinking, I can't find this method in the slim docks, where is it? And that's because when it comes to the request and response objects is a bit more complicated unfortunately. So you remember when we first set things up, we brought in Slim. We will also go in these two request and response libraries, which are PSR seven and HTTP. And this is because you can actually swap these out for different libraries if you would like this. Now we'll need to 99% of the time you can just use these, but it is what we call decoupled so that you can swap something else in. And so that means unfortunately we're trying to find the docs. It might not be in a place you expect. So if you can't find the method in slimmed ox, The first thing is to look at the PSR seven docs. Psr seven is a standard. So it's not a specific concrete library, but standard used by a bunch of different frameworks and it will work across framework. So if there's a method on haha you want to use, then this will be available in slim, but it was also be available in Lera val and symphony and everything that uses the pair saw seven standard. You will be helped to use the same standard methods. And then on top of that, there is slims helper methods because PSR seven by itself doesn't provide much. So there's also the slim HTTP library, which provides a lot of really useful, easy functions like things like return data with JSON. Or is this a redirection or get the params and how the past body. So if you're confused about wet, wet to find a method to do if request and responses not in the slim dogs. It might be in the PRC seven docs, it might be in the slim HTTP docs. Unfortunately, the way we decouple things now and allow you to swap different libraries. And they're now means it could be in a bunch of different documentation and it is a bit confusing. But it makes up projects much more compatible and extendable. And there are good programming reasons to do it, even though it does mess up the documentation some more.
33. JSON responses: So far we've been working with HTML. What happens if we want to work with over formats such as Jason? Well, that's fairly easy to do. So let's look at an example of how we would do that. And we can close mesh these files will open up the search controller. And we'll just copy and paste in color API controller dot PHP in our controller directory. Can we do this for method? And we'll get rid of this default method as well. And I'm forget to rename the class. Okay, lovely. So the only thing we need to do, because slim makes it nice and easy is intended. Instead of returning some HTML, we're just gonna return some Jason. So we're gonna say response with Jason, which is one of those helper methods slim, HTTP provides. And we're gonna pass in the albums array. So it same code as before, but we're just using this with JSON method. And then if we open up Index.html page, let's add a new rule. And the app controller, API controller. And I think we called it, said, we modified the name. Now if we get to API, perfect, we've got all our JSON data there and we can even run the query. And it will filter down as well. Okay, lovely.
34. Response codes: Up till now everything we've returned as being at 200 response code. So it's been a happy response. But what happens if we needed something like Flora follower or 500 or something like that? Well, let's take this Jason example and let's say there has to be a query. So if you don't pass a query in here, rather than returning all the data, it's gonna say ever, you need to pass something in. Let's open up our API controller and we're getting our query here. So let's say, okay, if query is blank, then we're going to return an error. So we're gonna say response with a status of 400. So 400 is an invalid request. Page not found would be 404, but in this case, we want invalid request for a 100. And we're going to still send some JSON back. And we can just chain link these methods together. And we're gonna say invalid request. Okay, great, so that's all we need to do if the query is blank, return not invalid request. There we go. So if we pass the query, we still got a data. But if not, we get this invalid request. And if we look at network headers, hey, we can say we get a 400 error rather than a 200. Ok, this is just some tracking, so that's the normal one. This is the request that we've made and we get an invalid request and it showing up in red because we've successfully returned an Ara.
35. Handling errors: Despite our best intentions, Sometimes things go wrong on when they do. We want to be able to handle that gracefully and make things as good as possible for the user in their experience. So in this module, we're gonna look at error handling, and we're going to write a really simple e commerce store that fells bikes. It's just gonna be a list of bikes and details page, and we're gonna look at what happened when the user gets lost and needs to see an error screen.
36. Bike Shop: In this module, we're going to create a bike shop. So there's a couple of resources will need from project five. So in the data directory, we've got this bikes Jason, and save that into our workspace. And then in the public directory we have some updated styles. And then we can just manipulate this header as well. And we're ready to start coding.
37. Bikes homepage: We're gonna start by creating a homepage for our shop. So let's create a controller that we'll call shop controller. And we'll give it a namespace saying slash controller. And let's grab these namespace impulse here too. And I'm not just crit standard controller. So sharp controller extends Controller. Take a request and a response is normal. And we're going to grab the byte stated juts like we did earlier, just like we did with the records data. Nice. And I'm going to use that to render out page passing in the array of bikes. Okay, looks good. So let's go ahead and create this default template as well. Default dot HTML will loop through the bikes. Is a bit of CSS pre-create Flores and CSS we copied in. And we want this to link to a details page that we'll build later and put the byte name in ty pen as well. And then we went a little bike emoji here. So we can just steal this from the template in project five. And then finally, let's go in and why this rule. So we'll say controller, shop, controller colon default, and that should be ready to go. So let's fire up the dev server in the next lesson and see it working.
38. Testing the homepage: One key change we need to make this as template. I'm gonna change this from div to spam. The mattering looks good. So let's go over to our terminal or command prompt and we'll launch it on localhost 8 thousand. Nice. And then in the browser, we'll go to localhost 8 thousand. And here we've got our homepage, tri bike shop. Allah bikes listed with their names are linked to the details and the type as well. But the moment there isn't a details page, so let's go ahead and build that in the next lesson.
39. Details page: In this lesson, we'll create not Details page. So will come back into our controller here. And we'll create a new method called Details. And this time we're going to need the logs as well. And then again, we'll grab the bikes, Jason. There are two ways we could do that. We can put it in a service, we could do it wouldn't stop the construct. Take him and everything's gonna grab it. But it's a one liner. We'll just live with that flight duplication for now. And I'm all set for this array. So we'll take an ID for the bike ID. And we'll find that bike by ID. And that will give us the key because of the way we're doing this here. And then we can render a page. So let's run the details that HTML. Again, let's pass in chosen by care. So we use a key to find it from the bikes array. Okay, looks good. So let's create that details template as well. So we'll call this details to HTML. And let's start by just cranes, simple breadcrumbs. And then we'll get the bike object. Now let's pull in that little bike emoji again. And now because we're inside this bike object, we can just reference a property. So here we're going to use name and put the type n. And I'll put its white writing and its aerodynamic rating as well. Put a photo on the bottom. Okay, it looks good to me. And then we're going to index and more why this rule. So we'll say controller, shop, control over details. And this looks okay. But I think there's an important change we can make to this routing to make it more accurate. So let's go ahead and look at that in the next lesson.
40. Filtering route parameters: So we built out details page. And if we go to a dev server, we can now say, we can click a bike analog, bring in details, and then give us white and Arrow and type everything we put in a template. That's great. But there is one issue is this is supposed to be a numerical ID and yeah, I could type in a name here. And it's still loads the page is still thinks that is the URL. And that's because we've just said id, but we haven't given any information about what ID should be. Luckily, it's really easy to do that and slim, so if we add a colon and then we'll put a regular expression. In this case, we want digits 0 to nine and there can be multiple of them. Sanat retry. This still works when we put a number in. All that looks good. But now let's try putting a name in. And now we get a not found exception. Doesn't look very pretty, but that is what we want. This URL should be a fora for not found. So that's great because now it means that you can only access this page when it's a genuine URL. When you're using the ID.
41. Adding error handling: So we've got this filtering now where if we put in some nonnumeric characters, we get this not found exception. But it's very ugly and not super-helpful. Now luckily, phlegm comes with some bundled middleware to maintenance easier. So we're gonna talk about costume middleware. That's middle while we create in the next module. But in this module we're just going to use some that comes with the slim framework. And this allows us to make the error handling a lot prettier. So let's go ahead and add this. So we're going to add era middleware. And we're gonna say true, true, true when talk through this in a moment. So now refresh the page. We get this nicely formatted for, for not pound era, which is much prettier than the old one, gives us all the details and the stack trace nicely formatted. And this comes with slim. So all we have to do with up this line, these different options allow. This one says whether you should display the error details not so if we sell on to false, we see all we get is not found and we don't get any of the era. It's not a huge deal on a not found. But if you consider this also handles 500 server error is in production. We don't want to display all those every details. We just want to tell the user there's been an error. Whereas in development, we can set this to true. And this gives us all the details so that we can debug it. So we're probably changing this based on production, these over to control. Where else, in terms of the logging, the error goes and you can pass a custom logo in here as well. But the main one is to make sure this is set to false and production. So you're not displaying your PHP code to users if there's an error. But we want it to be true in development so we can work out what's going on.
42. Custom error pages: What happens if instead of Slims default error handler, we wanted to provide our own page where we can do that using the ever middleware. It's a bit fiddly, but we'll go through it now. So we'll say era middleware, set error handler. And then inhale air. Festival will say what we want to handle. So in this case and some not found exception. And then we'll give it a function to work out what to do when we find this. I'm going to want to use our container as well. And then we're going to say Control-A. Okay, so let's talk through what's going on here. So on the ERA middleware, we are setting the error handler and we're setting it this. So whenever there's a not found exception, which is what's going on here. Then we're going to run this function. The function takes in a request object and we're also bringing in our container so that when we run this function, we can create a controller. In this case, it's an exception controller that we're going to create in a minute. And we're going to win the NOT_FOUND method. So this is just like wiring this up book for not found errors. We're gonna manually create around controller. And we're going to return NOT_FOUND method passing in the request. So let's go ahead and create the exception controller and a template for it in the next lesson.
43. Exception controller: Now that we've said we want to use this exception controller, we need to go create that. So let's do that now. So we'll call it an exception controller. It's any app controller namespace. And shoots with over controllers. We need this server request interface. But we're not going to need the response because we'll talk through this in a minute, but normally we get the response passed in. So if we look at chop controller, response comes in here. But if we see here in the error handler, we don't actually have a response object passed him. We've only got the request. So we're gonna have to create our own response. So we will define class. And we're going to extend the controllers that we can use our normal helper methods. And then here instead of, let's create a function first course called the NOT_FOUND when we defined it here. So we'll call it not found here. And we're going to require a crest. And because we don't get the response passed in, we're going to create our own response here. We can do that because we import the namespace here. So we're just gonna create a blank response. And then we can pass that to the render function as normal. Okay, lovely stuff. The other thing that we need to do is to create that NOT_FOUND template. Templates crit, not Dutch found. I'll just say page not found. And some in light with sorry, we could not find that page. And I'm gonna put a link back to the homepage. And then we'll bring that fluttering as well. Okay, so SEP IRA handling here we've said create an exception controller. And when the NOT_FOUND method, we've created our exception controller here, define the NOT_FOUND method. We've created our own response and told it to render the NOT_FOUND template. And then we've created are not found template here. And there we go. So now when a not found error is thrown, it throwing this template that we've just created with our local text. And I'll return to homepage link.
44. Manually triggering errors: We've now got our custom Not Found error page, but there's a scenario I think we're missing. So we felt the URLs that you have to put a number in here, but there's only eight bikes here. So what happens if I put in, say 800? We'll, that's a valid URL because it is a number. But the bike shouldn't exist. And yeah, I'm getting this, which is the first element in the array. And if I put any of a number in a probably get up Bian Qi as well because the key, they can't find the key name. It's coming back false. And if we look at what our code's doing in Shock controller, this will come back as false. And then I'll try and get falls out this which we'll just default to the first item which is the Bian Qi. Whereas actual fact if you put in an idea of 200, you should get a not found. So in order to do that, we're gonna need to manually trigger a HTTP not found exception. So there's going to shop controller and we're going to need to import that. So let's use slim exception, HTTP, not found exception. And then down here, we can do a little check to say if p is false, and we'll do triple equals to make sure that it is a Boolean and not say the number 0, which could resolve to false. If so, we will throw an exception and we need to pass the request and response into this. Okay, so now we're saying if the key doesn't exist, throw this exception and then it'll get handled by our error handling. So if we refresh the page now, lovely. So we put in an idea of 200 and it's that paid not found. We can still go into any of the existing bikes and get the details. But if we change this to an ID that doesn't exist, key comes back as false. We for our exception and it gets caught by the error handling and shows our nice error page.
45. Middleware: middleware allows us to modify the request, the environment and the response and do a lot of extra processing. So if you want to handle things like session management, data processing, error handling, we can do that all with middleware. So in this module, we're going to write a members area application where you can log in and like, ourselves, called number Zone. And we're gonna do that by bringing in some third party middle where to money to the session on and write our own middleware to handle the authentication.
46. Middleware concepts: Let's look at the theory behind middleware so so far in our kind of basic use of the application, we got this idea. That request comes in from the browser, say, a get request to slash, hello and that gets mapped to a root on the route, looks at the request and says, OK, we're gonna send this response back. That says, Here's some hasty moment says Hello World on the code. Within that route, the framework does the matching for the route, and then the root returns the response that we need, and it's a simple in out system. But what happens if we need something more complex where we want to interact with that request and maybe change the response based on different variables or just ADM or information to it, change it slightly, review some of the information in it before we pass it to the root on? This might be something we want to do in every request or do certain requests. Well, the answer is here is that we use middleware, so the request comes in and it then goes through a series of middleware less, and we can have this many middle wears as we want in this little diagram I've shown to piece of middleware. The request comes in. It gets passed through the middle, where down each led to the application application can then do something and pass it back up on again. It goes for that process and eventually reaches the response. So it's a chance for us to intervene in the life cycle of that request and response without the code having to lie in the route itself.
47. Member Zone: Little bit of prep for this module. So we now into projects six. And if we go into public, some updated styles, we can copy and paste into here. And you can also copy over the head to template. Or you can just go in here and update the numbers. I'm going to call it members. I'm that's all we need to get started with this project.
48. Secure and login pages: Let's go ahead and start cranes some basic pages for member zone. So we'll create a controller called off controller. Given the standard namespace. Bring in the request and response. And I know call it off controller extends Controller as usual. And we'll create a method called login. Call the template login penalty for now. And then let's copy and paste this and create one called Secure controller. So this'll be our actual members area. And we'll call this method default, and will render a template called default as well. Let's go ahead and take care of these templates. So we call this one login dot HTML, bringing the fluttering in the end. Okay, that looks good for the login template. Then let's create not default template, which will be the array you get to when you're logged in. Clot default dot HTML. I want to say something like welcome, welcome to the members area. Okay, and then let's wire these roots up. Slash r2 and more. Why this up to the login. So it's going to use the any verb because you're gonna be able to post to this at some point. What you can post to it. Now the form is not gonna do anything, but you could do. And let's call up slash secure. And it's gonna go to secure controller and we called it default. Okay. Let's get developments ever permanent. And let's see what happens here. Okay. Yeah. So I've got my login form and if I go to secure, I've got my welcome page. So we've got the pages out. Obviously, there's now Protection and their login system at the moment, but we've got some good scaffolding to get started.
49. Session middleware: In order to manage this session, we're going to need some kind of session handling library. In this case, I'm gonna use this one called slim session. So we can go ahead and install that lets council dev server and we'll do a composer require spell it correctly. We will find out very quickly. So we'll just let composer do its thing here to install the middleware, which should just take a couple of seconds. And then we'll go back to our index page and install it. Okay, nice. So yeah, we've got that. And if we look compose a file there it is. Perfect. Cool. So we're gonna install this as a service. And we're gonna need to add it as middleware. Well, so let's go ahead and do that. So in our index file, say contain a set session. And we'll give it a function here as usual. And all we're gonna do here is return new slim session, how PKA. And then we need to add the middleware as well. So we'll do that here. Okay, let's talk what's going on through here. So here we're installing the middleware, which is what we need to make it work. But we also want to make the section available in our dependency injection container, hence will also, once we've done that, we're going to install it as a service here as well. So we can then use it in our controller. So if we just gotta off controller and top of hey, let's just echo something out. So we're going to add dependency injection container. We cannot session service. And we'll get the count. And here we're going to implement an account as well. So again, we've got the session service. So this should have cross actually be sacked because we want to set count to be equal to count plus one. Now if we go to here on our login page and we refresh, we see the session services working because every time it's incrementing this number.
50. Logging in and out: Now that we've got accession service, let's go ahead and use it. So we'll comment out this code we've created here. And then we will handle the user submitting the form. So we'll say request is post helper methods that comes on slim HTTP checks if the request is being posted. And if so, we'll go ahead and we'll get the session service. And we'll set the user TB, the email address. So in the real world, we'd obviously check this with a valid user. But for here, let's just believe them that their email address is legitimate. And we're gonna use a nova helper method here because we want to send them to the members area. So we'll do response redirect and we'll redirect them to secure, we're gonna return that. So now if we use a post something, it will save that email address to the session and this use of variable and it will read them. Even thing we wanna do is add a logout method. And here again, we get the session service. And instead of a Gauss Atlas time, we're gonna delete because we want to delete these variable. And will return response again using the read method just to the slash, which will take them back to the login page. And then let's go into index.js, HP and why this up as well? Slash logout. And once you get off controller on the method is called logout super. So we also probably want to tell them not logged in and give them lot logout link. So let's go ahead and do that in the next lesson.
51. Playing back the username: Users can now log in, but we need to ideally tell him they logged in. So if we open up secure controller here and we're going to pass some data in here. So when you get the username, so we get an session service will get a use of variable from that. And then if we open up default, we can say something like you are logged in as a user. And I'm going to also give them a logout link. Ok. Nice. So let's go ahead and I will log in. And that we can see we've played back Chris at example.com. Okay, lovely. And we've got log out link as well, so I can click that. And that sends me back to the login screen. So that's looking good at the only problem is I could just manually type in slash secure. And i'm not until they logged in, we can see the email addresses blank here, but it hasn't stopped me from getting to this page. So the next thing is we need to actually bank this page private.
52. Custom middleware: We have this semi functional login system where he can login, but you can also just type in the URL directly. Now, there's some stuff we could do with that. For example, we could manually go in here and we could say, we get the session. And we could get the user. And we could say, if no, then return a response. We've redirect to the login page, something like that. And we could do that in every method. But that would be a bit of a pain because we'd have to remember to do it in every method. And if we forgot, we would potentially spouse a private page did outside world. So a better way to do that is with some custom middleware. And we'll go through set up now. So it's going to be in our app. And we're gonna call it authenticate dot PHP. And I'm gonna create a new folder here called middleware. And then we're going to need to bring in some things. And we're going to create a class called authenticate private variable called session. And we're gonna take the session service N as a parameter and just save it to this instance variable. And this is a bit while we will limit awhile. So we're gonna use a magic function. Let's pop hit to be called invoke to run when the class gets invoked. And I'll take a request and the request handler. And then we'll say, OK, we've got our session service. And we'll say use variable exists. And if it does continue with the request. So we can tell the request handler to continue handling this request because we know that the user variable exists and therefore the user is logged in if it doesn't exist. And we are going into the requested little by saying handle the request with a redirect to the login page. So when the request comes in, this middleware will get run. And it will say, is the use a variable that if it is continued, the request is normal. If it's not, then redirect change the request to be a redirect to the login screen. So let's go ahead and wire this class in the next lesson.
53. Route middleware: Let's go ahead and wire up our custom middleware. So we've got to secure here. I'm just going to move this down one. And in here we added some custom middleware to the whole app. But here we only want this middleware to run when they're in the secure URL. So I'm gonna go ahead where we're calling the the definition for the route. I'm going to add, haha, I'm going to call the add method. And we're gonna call the authenticate class. Okay, great. And then of course we need to pass the session into that. So we're gonna say apt-get container get session. So just like we installed it here, we've installed a hair as well. And now if we refresh the page, we get sent back to the login screen. So every time we try and type in the URL, middleware kicks in, it goes into here, it says user doesn't exist, so don't continue. The request is normal, send them to the login screen. Then we can go ahead here and we can type in the email address and login. And we see we've got it there. So again, now it's working because the user does exist and so it doesn't need to interrupt the request. Getting this middleware will only run on this route with the moment because that's when we've added it to. If we log out and try and type it in again, we find ourselves packet. The login screens are great. We now have a fully protected working login system.
54. Route groups: What happens if we wanted more than one page? So the moment we just added that one, route, one secure page here, but we might want to add more. Let's say we wanna status page. So let's go into secure controller and just create lat. So we'll call it status. And we'll take our usual request and response. And we'll just run the really basic page here. And then we'll go ahead and create a status page as well template. And I'll just hardcoded to saying your membership is up to date. And we need to wire this up. And we could install the custom middleware again on that separate room. But that would be super messy. So a much cleaner way is that we can use a root group. Let's just comment this out. And instead here, and we'll say app. We'll create a group slash the QA. Because all our URLs are going to start with slash security. You could actually make this empty if you wanted to have a group with completely different URLs. But in this case they're all going to be here. And it's IB AP in there. And then here we're going to define our routes. So if you're slash secure, then we want to go to the default page. As before. I'm going to create a new page. And then again as before, we want to install the middleware. So we can just take that form here. So what's going on here? Well, we define two roots too. It's like haha. But we've put them inside this slash secured zone. That means if you're slash secure and nothing else, it will go here. And if you slash secure slash status, then it will take us to the status page. And for both of these, we're installing our custom middleware group. So let's try refreshing hair and I've seen it work. Let's get a status. Yeah. And then let's widgets. I open up a window, we're not locked in. We got sent to the login page. Ok, brilliant. So this could, if you need it completely URLs, we could leave that blank and we could do something like that. And that would work fine. But as all the URLs are grouped together, we might as well just put slash trickier here and then extend all of our roots here. So we've got the blank, we've got the slash status. And then we can install custom authentication middleware on all of these roots at once, everything in the group, so that all of these areas are protected by the login system.
55. Final thoughts: Congratulations on making it to end of this class on Slim. You now have an introduction to micro frameworks. You understand how slim works in terms of rooting controllers ever reporting middleware and more on Hopefully you built your 1st 6 projects. Share your projects with the group even by sharing the source code. Or however you wanted them away in the project section. If you have enjoyed this plaster and please leave a review on whatever you're moving on to next, we shoot the best of luck.