در حال بارگذاری ...

Problem: No method routing0:00

Alright, welcome back everybody. In this lesson, we will extend or enhance our Writer class to support different request types. So, for example, if you have a form where you want to submit, say, a delete request to a particular controller, how exactly do we do that? Well, I'll show you in this video. Let's get going. So, we will get started in routes.php, and yeah, I want you to notice how for each URI and corresponding controller path, at no point do we distinguish which request type we want to respond to.

and corresponding controller path, at no point do we distinguish which request type we want to respond to. It handles all of them, which is why in our controllers we end up with some kind of gross code. For example, when we show a note, yeah, we have this check here. Well, was the form submitted? In this case, we are deleting a note. Otherwise, we are showing the note. And again, very, very quickly, this becomes very, very messy. So our job is to clean this up.

And again, very, very quickly, this becomes very, very messy. So our job is to clean this up. But how exactly do we do it? Well, one option would be something like this, where you instead return another array, but each item is its own array. And you could say, well, the URI, if I reproduce what we have up here, the URI is the home page, the controller path, and real quick, let's comment this out to remove the squigglies. Anyways, the controller path would be controllers/index.php, and the request method that we want to specifically and uniquely handle, or exclusively handle, I'm sorry, would be a GET request.

Designing Router API1:27

we want to specifically and uniquely handle, or exclusively handle, I'm sorry, would be a GET request. And yeah, we could do that. And then I could repeat this, and I guess it's fine, but I don't know, it just, it's not overly friendly to interact with. Instead, though, what if we had something like a Router object? Well, if that were the case, we could just call specific methods that match our desired request type, like this. Listen for a GET request to the home page, and then here is the corresponding controller path.

Listen for a GET request to the home page, and then here is the corresponding controller path. You know, this just feels a little better to me. And then later, if we have, which one was the DELETE request? Well, it would be something like this. Handle a DELETE request to /note, in this case. Then we could have a dedicated controller for this action and request type alone. Maybe Controllers, NotesController, and destroy is a common term there. So our Router object would have methods like get, delete, post, put, patch, all of the different request types that a form could potentially submit.

Building Router class2:28

So our router object would have methods like get, delete, post, put, patch, all of the different request types that a form could potentially submit. All right, I think this feels pretty good. The next step is to make it work, and that's the harder part. Okay, but it won't be too bad. I'm going to go into my CoreRouter class, and yeah, up until this point, we've had simple functions, but I'm going to upgrade this to a dedicated class. I will rename it to CapitalRouter. And then for the moment, let's just comment everything out, and we'll start at the beginning. Class Router.

And then for the moment, let's just comment everything out, and we'll start at the beginning. Class Router. And let's see. If I switch back to routes.php, like I said, we'll have methods to match each form request type. So if I go back, right off the bat, I know I'm going to have a get method. I will have a post method. I will have a delete method. I will have a patch method. I will have a put.

I will have a patch method. I will have a put. For now, there are differences between patch and put, but for now, just imagine that it represents updating a resource. There are some differences, but for now, you don't really need to think about it. You can consider them synonymous, and people are getting mad as I say that right now, but at this stage, just use one of them or consider them synonyms for now, and then we'll talk about the differences later. Okay. All right, so this is looking good.

Okay. All right, so this is looking good. The next step. When I call the get action or method, it looks like we are passing a URI and a controller path. All right, so let's accept that. And actually, it looks like this didn't rename properly. Capital Router. There we go. So we accept a URI and then a controller path.

There we go. So we accept a URI and then a controller path. Okay, so what should this method do? Well think about it. In our routes.php file, we're calling methods all over the place. So maybe each time we call a request type method like get or delete or post, maybe it should be cached, so to speak. Like maybe we should store it within an array that the router has access to. So right up here, we'll add a new public property called routes, and we will initialize it to an array.

So right up here, we'll add a new public property called routes, and we will initialize it to an array. But this time, I will introduce a small bit of visibility. So up until this point, we've made everything public, as we talked about. And that just means the method or the property is publicly accessible outside of the class. So if you have access to the object instance, you can call this method. You can call that one. You can call this one. You can access this property. But as you'll find in the future, there will be situations where maybe you don't want to

You can access this property. But as you'll find in the future, there will be situations where maybe you don't want to expose or reveal certain information or data types. So you can change that by switching public to either private or protected. I often use protected. So what this means is this property now is protected. It is not available to the outside world. Within this class and within this object instance, we can interact with it. But outside of it, you don't need to interact with it. And that's usually the thinking I will reach for.

But outside of it, you don't need to interact with it. And that's usually the thinking I will reach for. Do you need to access this? Well, no. There's no reason why you would need to access this routes array outside of the Router class. So I will make it protected. And yeah, let's put a pin in it for now. And then in the next series, we'll talk quite a bit more about object-oriented programming and encapsulation and visibility. They're all kind of connected.

and encapsulation and visibility. They're all kind of connected. So now we can just push to the array. We're figuring this out on the fly. This routes. Let's push a new item. And yeah, maybe we could reproduce what we had at the beginning of the video, where we say, well, the URI is this URI. The controller is the controller. And then the method should match, well, the method name you called.

The controller is the controller. And then the method should match, well, the method name you called. So we can force and hard code that to get. Okay. So now the first time, think about it, the first time I call this method, well, in our Router class, we will push a new item onto this routes array. All right, let's do some more. And yeah, I can do a little copy paste here and think about it. I could probably remove some duplication in a little bit. So the only thing that really changes here, if you think about it, is the method itself.

I could probably remove some duplication in a little bit. So the only thing that really changes here, if you think about it, is the method itself. So let's just keep working along. This would be method is delete. And then once again, accept the URI and the controller. And then let's just do this. I'm going to try to do this a little bit quicker. All right. This one is patch. And then finally, one more.

This one is patch. And then finally, one more. And this one would be put. All right. A little clean up, reformat. And there we go. Okay. So why don't we try this out? And actually, this is a good demonstration of visibility. If I want to quickly spit out all of the routes, well, have a look here.

And actually, this is a good demonstration of visibility. If I want to quickly spit out all of the routes, well, have a look here. I could dd (die and dump) $router->routes. So notice I'm trying to dd this property. Let's see what happens in the browser. So we load this in Firefox. And what? Warning, require functions.php failed to open stream. Why? What happened here?

Why? What happened here? Oh, I see what's going on here. phpstorm. How dare you? Looks like I accidentally renamed the directory. That's why the router... Okay. Okay. I see what's going on here.

What's the problem here? This isn't a JavaScript issue. Hmm. Well, let's... Oh, yeah, of course. This would be the case because we created a new router. I'm such an idiot. Okay. So let's think about the flow one more time, and I'll get us back on track. So yeah, right down here, we're still trying to require the old router.php file that we've

So let's think about the flow one more time, and I'll get us back on track. So yeah, right down here, we're still trying to require the old router.php file that we've since upgraded to a dedicated class. So instead, let's just initialize it, a new CoreRouter, like so. Now you'll see it's squawking right now because we've upgraded it to a class, but we haven't yet applied the namespace. So we'll do that now. So yeah, if I switch back, we're seeing that white screen of death because we initialized our router, but then we never actually routed anything. So index.php completed, and we didn't load a view.

our router.php, but then we never actually routed anything. So index.php completed, and we didn't load a view. We didn't do anything. So we see a white screen. Okay. We totally understand what's happening here. Let's get back to work. So if I come back to router.php, you'll remember before at the bottom of the page, we had these three lines where we require our routes, and then we parse the current URI, and then we call this routeToController.

Wiring router in index8:58

three lines where we require our routes, and then we parse the current URI, and then we call this route to controller. So let's upgrade it now. We're still requiring our routes, but you'll remember that routes.php expects access to some kind of router object, and at the moment, it's not available. Okay. So let's come back and do this. I'm going to grab all of this, and I'm going to move it to index.php, and we'll add it here. So here's our router, and then I'll paste these lines in.

here. So here's our router, and then I'll paste these lines in. Next, I will require routes.php, and because I've already initialized a router object, that will be available within this file. That's how it works. And now you can see the squiggly here because that property is protected, but we'll get to that in just a minute. Okay. So we require our routes.php file. This will populate the array of routes that we have here for the application.

So we require our routes.php file. This will populate the array of routes that we have here for the application. Then we figure out what the current URI is. We parse that URL, and then we're still calling this old route to controller function. Why don't we swap that out with something like router, and what method do I want to call? How about route? We will use it as a verb in this case. I want you to route the current URI to wherever it needs to go. Okay.

I want you to route the current URI to wherever it needs to go. Okay. Let's give that a shot. So I will go into router, and we're going to add a new method here called route that accepts the current URI. Okay. So now if I scroll down, here's the old implementation that we had. Let's have a look. So we had access to a specific URI and then a list of routes, and then we checked, well, if the list of routes has a matching key for that URI, then that's the one we want to load,

So we had access to a specific URI and then a list of routes, and then we checked, well, if the list of routes has a matching key for that URI, then that's the one we want to load, otherwise abort. Okay. So we're mostly going to reproduce that. We will loop over all of our routes, so for each route as route, and then we might say, well, look in that route, and remember, each route is this array here. So we might say, well, if the route's URI matches the URI that we passed in here, which is the current URI for the request, well, then that's the one. So we could return require base_path('route/controller.php'), right?

is the current URI for the request, well, then that's the one. So we could return require base_path route controller, right? We're going to require the corresponding controller. Okay. And that will handle 50% of the job. We'll come back to it in a minute. Otherwise we want to abort because if we hit this line, we haven't found a matching URI. So if we scroll down, you'll remember we had this old abort function. Let's grab it and bring it up like this, uncomment it. We could make this protected if you want, reformat, and yeah, that's mostly the same.

Let's grab it and bring it up like this, uncomment it. We could make this protected if you want, reformat, and yeah, that's mostly the same. So I could call abort. All right, we're making progress. But now if I scroll up, we're still not inspecting the current request type, and that's something we need to do. Hmm. How could we do that? So it sounds like for this to work properly, maybe we need to accept a request method, and this would be get, or post, or delete, or put, or patch.

So it sounds like for this to work properly, maybe we need to accept a request method, and this would be get, or post, or delete, or put, or patch. Okay. So let's come back to index.php, and yeah, now it's squawking because I need to give it some kind of request type. So that's the next step. How can we figure out what the current request type is from the form? You'll remember if we inspect the $_SERVER superglobal, and then the REQUEST_METHOD, that will tell us if we have a get or a post request. So yeah, that would be an option, and that does handle the case where we have post requests.

Supporting DELETE via _method12:30

us if we have a GET or a POST request. So yeah, that would be an option, and that does handle the case where we have POST requests. But now, do you remember a couple episodes ago when I said forms don't natively support submitting DELETE requests or PUT requests? All they can really do is GET or POST. So that means we need some way to suggest to our application that, well, I had to submit a POST request because that's all forms support, but what I really want to do is submit a DELETE request. Okay. So we can signal that through our view when we build up the form.

Okay. So we can signal that through our view when we build up the form. So for example, we have within note/show, yeah, in the last episode, you'll remember we added this form that has a button that says delete, and when you press it, it should delete the note. So now, yeah, you can see the method is set to post because, again, it's my only choice here. But yeah, I want to add some suggestion to the back ends that I really want a delete request in this case. And I will do that by creating a hidden input, and I'm going to give it a name with something

request in this case. And I will do that by creating a hidden input, and I'm going to give it a name with something unique that ideally will not interfere with any of my other attributes. So very common would be _method, but again, you can do anything you want. If you want to do, you know, __request_method, just something unique and the underscores at the beginning are what make it unique because it's not very common. Again, something unique that will ensure that it doesn't interfere with your other form attributes. But again, _method is pretty common.

attributes. But again, _method is pretty common. Okay. And I'm going to set a value of delete. Okay. So now I submit the form and there will be this hidden input called _method that is set to delete. So now think about it. Our router only has to check. All right.

Our router only has to check. All right. Well, do we have something in the POST request called _method? And if we do, let's favor that request type over whatever the default request type was. That's how we're going to handle it. Okay. So let's come back down. Where are we? index.php. So let's see here for method.

Index.php. So let's see here for method. We could do this in a couple steps. The old syntax would be to do something like this. Check if _method exists within the $_POST super global. And if it does, I'm using the ternary operator here, then that's the one we want to use because the User signaled that to us. Otherwise, I will stick with the default request method. But yeah, notice how I have a squiggly line here and that's because phpStorm is correctly indicating that there's a simpler way we can do this.

But yeah, notice how I have a squiggly line here and that's because phpStorm is correctly indicating that there's a simpler way we can do this. I can swap it out with two question marks. This is equivalent to what we had before. It's just shorthand. So we're saying, all right, I want to use this if it's set and if it's not null, otherwise I want to use this. So yeah, a small bit of new syntax for you there. It's pretty clean. But yeah, if you're a little overloaded with new syntax, then you can stick with this and

It's pretty clean. But yeah, if you're a little overloaded with new syntax, then you can stick with this and that would be fine. But yeah, it's so much cleaner. Just learn it. Okay. So now I know what the current URI is. Let's clean this up. I know what the desired request method is. So I will route the request.

I know what the desired request method is. So I will route the request. Okay. So we come back into our router. We now have a URI and a method. So the next step is to say, well, let's check if the URI matches the current URI and the routes method matches the current method. And then let's wrap this in a call to strtoupper and that will capitalize all of the characters. Otherwise, if we have a capital GET, but the form's method was set to _GET, well,

When we route the request, we loop over all of the routes that were created right here. So in real life, you'd have a bunch of these here and we can get rid of that now. Then within our router, we check for each item there. Well, does the URI match the current URI? And then also does the request method match the current request method? If so, let's require the corresponding controller and we're good to go. We can return. Otherwise, we couldn't find a match. So we should abort, which we do so here.

So we should abort, which we do so here. Okay. So now I can get rid of all of this junk and this ends up being a lot cleaner and we can even take it a little further. But for now, I just want to make sure things work. So let's come back to Firefox. Here's our white screen of death. Let's give it a refresh. And there we go. It looks like our updated router is working.

And there we go. It looks like our updated router is working. Okay. So now I'm going to come back to routes.php and I'm not going to make you watch this, but I'm going to reproduce all of these items here using our new syntax. Okay. I'll get started. All right. And through the magic of screencasting, I'm back in record timing and I have converted these six items here to the new syntax so I can get rid of that.

And through the magic of screencasting, I'm back in record timing and I have converted these six items here to the new syntax so I can get rid of that. So here's our static pages and then here's our routes related to notes. So if I come back to Firefox, give it a refresh, there's about, there's contact, there's notes. Let's create a note. That's good. Let's come back. Let's show a note. Oh! We have one issue.

Oh! We have one issue. Call to undefined function abort. Yeah. That's probably because we have authorization and we deleted that abort function. Okay. So let's return it. Let's go into functions.php and yeah, right there. We no longer have an abort function. It should live here anyways.

We no longer have an abort function. It should live here anyways. abort. We want a status, which will default to 404. And in fact, let's just go to router.php and grab all of this. We have a little duplication here, but that's fine. Like so. Okay. Come back. Let's give it a refresh and there we go.

Refactoring with add()18:49

of cleanup on the router itself. So again, notice how every one of these methods is basically doing the exact same thing other than setting the request method. So with that in mind, why don't we add another helper method like add. And this will accept the method, the URI, and the controller. And then this method alone will be responsible for appending to the routes array like that. And we'll swap it out. Okay. So now think about it. This method can simply call this add, and then we send through get URI and controller.

So now think about it. This method can simply call this add, and then we send through get URI and controller. So it just ends up being a little bit cleaner. And then I will update this one, and this will be post. And then this one will be delete. This will be patch. I know this is really stimulating, but you got to do it. And this will be put. Yeah. It just simplifies it a little bit, doesn't it?

Yeah. It just simplifies it a little bit, doesn't it? All right. And actually, if you want to have a little fun, you could even swap this out with a call to compact like this. This is sort of like the opposite of extract. It creates a new array with these keys, but then the values for each of these keys is the variable associated with the key, which is kind of cool. But I don't know. It can be a little confusing, and I don't want to pile too much on you.

But I don't know. It can be a little confusing, and I don't want to pile too much on you. So why don't we stick with this? And I think this is pretty good and even better. Now our routes.php file. Yeah, this works for me. It's pretty clear. And then in the future, if I need to extend it to add support for additional things like authorization or middleware, I now have a way to do that. So I know we covered a lot.

authorization or middleware, I now have a way to do that. So I know we covered a lot. If you have any questions at all, please do ask them in the comments below. Otherwise, we'll keep going in the next episode.

Request MethodsRequest Type SpoofingRouting

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟