Okay, guys.
So in this video I'd like us to have the router class implemented.
We're gonna be implementing it from bottom to the top.
Additionally, I'd like to have a separate routes file where we're gonna be adding routes using this
add method.
And the way we're gonna implement routing would probably be the closest to how it's done in the Laravel
framework.
Now, I generally do think that the router is a complex piece of code.
So you would need to focus.
And also I think you're gonna learn some interesting tricks and practices.
And I think it is also very useful to just try and solve a complex problem, which definitely the routing
is.
So maybe let's start from this call action method.
In this method we would like to create a class.
The controller class that we point to in this controller string and call a method of this class, which
also is passed as a string parameter.
So that's interesting.
We haven't done anything like this before.
Now let's see how can we create a class by just having the class name, the controller class name,
and the action name, which is a method.
So first let's figure out the namespace of the class.
So we know that the app code will be stored inside the app folder.
I'm going to create a controller folder inside because this is where we're going to be keeping the controller
classes.
Let's quickly jump to Composer.json to remind ourselves that inside the app directory, the namespace
prefix is app.
Okay, so we can start with the app prefix.
Now I'm adding two slashes because one slash is an escape character for special characters.
Then we go with controller namespace again slash, and only then we've got the actual controller class
name, and I can directly use this controller variable here.
So this would be our class name.
Now what's interesting is I can create a new class in PHP by using a variable.
So in this place PHP will just figure out what's the value of this variable.
It will try to find the class using this name using autoloader.
And if it succeeds it will just create this controller class.
So I would just need to create a specific controller class first.
Now, I've said previously that action is a method of a controller.
That's why I can also use the action variable in place of the method name.
Again, PHP will resolve the value of this variable, and whatever the name is, it will try to find
such method in this created object and run it.
Okay, but controllers can also have some parameters.
What do we do about that?
Finally, let's use array unpacking using the params, which is an array, because we're going to collect
the passed parameters of every defined route in the order they were defined.
And that's also a requirement of our method that it should have those parameters defined in the same
order as we can find them in the route.
I need to fix this.
This is return and this method is complete.
This is how we're going to be calling the specific controller method.
Once we are able to identify the route and was its matching controller.
So let's go to the match root method next.
Now this match root method has only one job.
Its job is to compare the root URI to request URI.
Now this means that we're going to run this for every single root we have defined, unless we earlier
find the proper root.
So here is a reminder.
So first we need to divide all those URIs into segments.
So as you see those two will match in our case.
So we've got the static part.
We've got a parameter.
And this is matching.
That's why first let's cover dividing those URIs into segments.
And the segment separator is a forward slash.
Okay.
So let's define a variable called root segments.
We're talking about the current root that we are processing.
And another one would be the request segments.
So we can compare them.
Okay, so I've said that the forward slash is a separator.
And in PHP to get the strings separated by something we use explode.
This is our separator.
And I'm going to stream to remove Whitespaces.
So first I'm using the root URI.
And additionally when using trim to remove white spaces I can remove some additional characters like
the forward slash itself.
Now, you might have guessed that we're going to call the same function for the request URI.
Just need to fix the name here.
That's request URI.
Okay, we've got the segments.
So the algorithm let's take a look at it.
We need to first see if we have the same segment count and then match individual segments.
So the count of arrays because those will now be erased.
The root segments and request segments.
We can just compare the result of count of route segments.
If it's different than the count of request segments.
If it's different, we return null and null in.
This function is used to return that we haven't matched the actual route URI to a defined route, because
if we would match it, then something would be returned in the array.
And this something is either an empty array or list of all the found parameters.
Now, if we don't eliminate this right route right away, then let's add the params variable, which
would be an empty array and then return it.
But first we need to go through every single route part and compare it.
Okay, so let's use a for each loop to go through all the route segments.
Immense and let me get the index as it might be handy.
And then we've got the route segment.
Okay, now our first job is to check if this segment is a parameter.
How can we do that?
Some extra functions in PHP.
So one is called str startswith where we can check if the root part which is haystack.
That's the parameter name starts with curly brace opening because this is how we begin a parameter.
And also we check if the same string ends with.
So there is another function called endswith where haystack is also the root part.
And here we are looking for a closing brace.
Now if that would be the case then we just add a parameter.
So two parameters.
We're going to add the parameters using the parameters defined name which we can get from the root part
excluding the curly braces.
So we can just use trim get the root part.
And what we exclude is white spaces and curly braces.
And the value of it is the request.
Segments.
And this is where we needed the index.
So that's the root part I've used the wrong name.
This is the root segment okay.
So again this is a parameter.
It's always starting with a curly brace and ending with a curly brace.
So everything inside it is the parameter name.
That's why here we are trimming white spaces and curly braces by using the root segment, which is from
the root definition.
And that's the key to the parameters.
And then as a value, we use the segment at the same place from the request which will contain this
parameter value.
So that's the first condition that we have for a segment.
Another one is checking if maybe the static segments don't match.
Because if we don't have a parameter then this means we have a static segment that's a static segment
like the text that is saying literally posts.
That's just an example.
So if the root segment is different than the request segments with the given index, remember we can
safely use the Indexes for both arrays because first we have checked if they have the equal.
Length and if that's the case.
So if the static segment has a mismatch then again we can return null.
This means that we didn't have a match and this route just doesn't match.
So maybe this is a little bit complex because it is a kind of an algorithm.
But anyway I think we really need to practice all kinds of problems and solving them anyway.
This should match one specific route.
And now our job is to call this match route function for every defined route that we have inside the
app.
Next up, this find route method will use the match route method to compare all the routes defined to
the current URI and the method.
So let's use foreach again going through all the routes.
And then we are calling this match route and we are getting the parameters.
So here we'll be passing the route URI.
And the URI of the request.
So we are comparing those.
Now the result as you might remember if we get anything else then null it means we have found a matching
route.
But that's not everything.
So first we need to check if params is different than null.
This means we've got something that is almost what we are looking for.
But remember that we've also got a method and this wasn't checked anywhere.
So it needs to match the expected method of the request that's either Get or post or whatever you'd
like to use.
Only then we can return something from Findroot method, which as you see, is also a nullable array.
So we go through every single route and if we can't match anything, then we just return null from this
method.
But we need to tell the dispatch method what is the name of the controller class and the action as well.
That's why from this find route method, we're going to return all the info about the route itself,
about this route we are currently investigating.
And only then we're going to add another parameter called params.
Getting the parameters that were passed through this route.
So at this point, it might be getting complicated why we construct this return type this way, but
we are just returning all the data that will be required in an array.
So maybe it would be more clear once we see that this add method is adding routes that have the method,
the URI, the controller.
So basically here we are returning those three values.
The method that you write the controller name.
Plus we are adding the parameters on top of it.
Maybe it will make sense if I just implement the add method right now.
So we can add two routes by using this square operator.
And every item is an array itself where we've got the method which equals method.
Then we've got the You or I, which also equals the value of your variable and the controller.
You can also optionally replace this with a call to a compact function, which I've probably used before.
So this is how a route looks like.
It has a method you write controller.
So basically in this place where we return the response from find route, we're going to be getting
those three elements of the array plus the parameters that we got from the actual URI.
Okay.
Next up we've got the final step the dispatch function.
So here our job is to just find the route for the URI and the method that we received.
If we've got a null so it wasn't found it might happen.
If someone enters an incorrect URL we can return the result of not found method.
This doesn't exist yet, but we can easily copy this from the previous code.
So let me just quickly add this.
This is public function not found.
For now, let's just echo this text not found.
We're going to be changing that to an actual view later on.
And the final thing here is to call the controller and the action.
So we can use array destructuring and call explode.
Because this directly stems from how we're going to be defining the controller.
So let me show you how we're going to do that.
So when we point to a controller let it be a post controller class.
The action will be marked after an at sign.
So this would should be index action in post controller class or this example.
Here would be a show action in post controller class.
So that's the name of a method inside this class.
That's why with explode I'm going to use the at sign up separator.
And from root controller we are getting the controller class and the method name the action.
So with destructuring I'm getting separately the controller and the action because this will just return
an array with two items.
So that's the first item.
That's the second one.
And finally we return the result of this call action which accepts the controller, the action and the
parameters which we get from the route under the key palms.
Now the logic is complete and it's time that we define some roots and try this out.
Okay, so since this is a pretty long lecture, I think we might take a short break and do it in the
next one.
For now, let's just quickly review what we have done.
So using this add method you can add more roots.
This is a not found function.
We had a previously something very similar.
But the main entry point to this class is the dispatch method to which you pass the URI and the method.
It then tries to find a root and call the specific controller if obviously one is found.
So here we dynamically create a class and try to call its method also dynamically by using a variable.
So maybe this was something we haven't done before.
And I think it's interesting to know that this is possible.