This episode is for members only

Sign up to access "Nested Categories and Breadcrumbs with Laravel" right now.

Get started
Already a member? Sign in to continue
Playing
09. Multiple wildcard routing

Transcript

00:00
Let's look at something much more advanced now. And that is within the URL, making sure that we always show the category slugs up until the category that we're in. Let me show you an example of this, and then we'll go ahead and look
00:12
at step-by-step how to implement it. So let's take the sale under boots and shoes as an example. At the moment, we just have one slug in here. What we ideally want is something like shoes, boots, and then this kind of URL.
00:28
Now, at the moment, this is not going to work of course, because we're taking the first item here and matching that up. And this doesn't match up with the route that we've defined. Let's first of all, have a look at how we do that within our routes.
00:41
So if we open up our web routes at the moment, we're using route model binding for this single category. What we want to do is change this over to any value. Let's just call this any for now, just so we can see what this looks like.
00:54
Now, if we head back over and give that a refresh, it still doesn't work. What we need to do is we need to apply a where clause to this route, and we need to say where any, and then we need a regular expression in here to match this up.
01:06
If we head over to the category show controller, this is now not going to work because it's not going to be a model. This is going to be a string instead. So let's just call this path and let's just die dump on this
01:16
so we can see what this looks like. So let's die dump that path out, give that a refresh. And there we go. So our job now is to essentially break this up, grab the last thing that
01:27
we're looking for, and then go ahead and fetch that out instead. And that means that any URL that we provide in or any path that we provide in is always going to end up with the last category that we want to see. What we also need to be careful about is does this category belong within
01:43
boots and does boots belong within shoes? So we're going to have to traverse back with the ancestors to check this as well. This is completely optional, but we want to make sure that our URL structure can't be modified and changed around to show a category that doesn't
01:58
exist within another category. Okay. Let's go over and we're just going to play around with this in the controller. Then we're going to move this over to its own root model binding.
02:06
We've also got a couple of other jobs to do because we need to figure out how to build up the paths for this within our templates. So let's start by just doing a simple explode on this. I'm going to call this trail and we're going to explode out the
02:19
path that we get with a forward slash. That's going to give us just a broken up array in PHP with the slash as the character that we want to break up by. Okay.
02:30
So the next job is to grab the last one. Let's just create out a variable in here and use the last helper in Laravel to grab the last item from that trail. If we go ahead and dump that last item out, we will end up with
02:42
the category that we should be in. So let's get this working and then we'll look at some of the other issues that we're going to come across. So let's redefine a variable called category and we'll use the category
02:53
model to pluck this out where the slug equals that last thing. So we can either do where slug, or we could say where slug, and we can pass in that last item. Now, really importantly, we want to do first or fail, because if this
03:07
can't be found, we want to abort with a 404 error. Okay. So now that we've done that, this is getting passed to our view and it should just work.
03:15
So this still works nicely, but we've got a couple of other issues. If we hover over any of these items here, of course you can see just at the very bottom of the screen. This just points towards the ID of the item because we're passing this
03:29
in with the root model binding, which we now no longer have. So let's go ahead and focus on building up the URLs for this. And then we'll look at tidying all of this up and also checking the ancestors as well.
03:41
So we're going to come over to the category model and we're going to need to create our helper in here, which is going to build this URL up for us. Let's call this get path and let's just return anything in here just for now. And we'll use this in our templates.
03:55
So we're going to need to use this in a few places. Let's go over to our index here and we'll go over to the category item that we're iterating through, and we're going to need to update this. So we're going to need to say category and get path.
04:09
We're also going to need to do this over in the show page as well. So let's open up the category show where we're showing these and let's do the same thing for child, get path, and also in our breadcrumbs as well. So if we head over to our breadcrumb roots, we're going to need to
04:24
update this here for the ancestor. And we'll need to say, get path. So now if we head over, this still works. And now we just get ABC at the end of each of the URLs.
04:35
Okay. So let's go back over to our category model and let's figure out how we can actually build this up. So what we want to do, let's just die dump here just for now.
04:44
We want to grab all of our ancestors and self to build this URL up. So we know that this, we can use ancestors and self, and we are currently working within the model. So we can just pass in this ID that gives us all of the ancestors
05:01
and we can just go back through them. So let's go ahead and return this, but let's map through this. So we're going to use map here to return the slug for each of the ancestors. So we'll get each of the ancestors in here and let's return the ancestor
05:19
slug and then let's cast that to an array. I'm not going to return this just yet. Let's just make sure this looks good before we go ahead and return this. Okay.
05:29
Yeah. So we've got shoes. That's fine because this isn't at this point. We've die dumped at this point here, but if we were to go through,
05:36
this should pull back our ancestors. Let's go ahead and return this and just see what it looks like. So at the moment, this is going to be an array. So it's not quite going to work.
05:45
We can just give this a refresh and hover over here. You can see at the bottom, we've just got boots and shoes. And then for any of the others, we just have formal added to the end. What we need to do with this is join these together as a string.
05:59
So let's take what we have here and let's use Laravel's array helper from illuminate support. And let's join these together as a string. So we're going to pass.
06:07
This is the first argument. We want to join these by a forward slash to build up the URL. Okay. Let's go over and have a look and let's go into a deep category.
06:16
So let's go into sale under boots and shoes and hover over and we've got boots, shoes, and that looks okay. So at the very bottom, in the left hand corner, you can see shoes slash boots. And if we go over to categories and hover over sale, you can see that builds
06:31
up the full URL at the very bottom. So now I can click on this and go through to the right one. You can see everything is working nicely. And of course we get the full URL at the top as well as we go through.
06:43
So this is working really nicely. There is one problem at the moment, and that is that our queries are going to be pretty high. So let's just pull this open a little bit.
06:52
And you can see that because we're iterating through each of the categories and we're using this method here, ancestors and self that's going ahead and performing a query for every single one. So the more categories you end up with, the more you're going to see queries.
07:07
Now, what we could do with this is either leave it as it is. If you don't mind the additional queries for this, it's not too much trouble because the page load speed is still going to be okay, but you could cash it. So this is totally optional.
07:20
Let's go ahead and cash this value. And then you can bust the cash whenever you add or change categories. So let's go ahead and use Laravel's cash helper. And let's say, remember forever, or that you could pass a value
07:31
in there if you wanted to. Let's choose a key. So let's say category path, and we'll go ahead and add on the ID for each of these. Now in the closure, we can go ahead and return this.
07:42
So let's take everything that we've done here and just move that into here. Now, at the moment, when we set up a Laravel project, the cash store should be database. I've switched this over to file.
07:54
You'll probably be using something like Redis in production, but make sure this is file, otherwise you're just going to end up with even more database queries. Okay. Give that a refresh a couple of times.
08:03
And there we go. Go back to how we were before. And now we don't run into an N plus one problem, despite going deeper into categories.
08:12
On first load, of course, this will be higher, but then as you refresh, we'll be getting back the cash value. Okay. So one more problem that we've got is that into, let's go into a deep category
08:23
here, sale, this is under boots and boots is under shoes. Now, if I change this over to anything, it's still going to work because remember, we're only plucking out the last item and serving that. Now, if you don't mind this, then feel free to skip.
08:39
But I want to make sure that this exists within this category and this exists within this category. How do we do that? Well over here, where we're playing around with this, we can basically
08:50
iterate through each of the categories, ancestors, and then abort with a 404 if this doesn't exist so we can say category ancestors, and let's go ahead and say each, we don't need to say self because we've already got the last one and let's create our a closure in here, we'll go ahead
09:09
and bring into scope the trail. So we can look inside of there. And effectively we want to, what we want to say is if this ancestor that we're currently pulling through here doesn't exist within this trail.
09:22
Then we want to abort. That means that all of the ancestors have to exist within the trail that we've passed in. So we can just use a simple in array here to check this.
09:32
Let's go ahead and grab the ancestor slug and make sure it exists within the trail that the user or we have built up. Now, if it doesn't exist, we're just going to go ahead and abort with a 404. Let's go back over to the browser and let's change this back over to
09:47
boots and you can see that works. But if I change this over to something else like jackets, you can see that we get a 404 because this has ancestors, but jackets doesn't exist within the ancestors of this last one.
10:00
So now all of the URLs that we've built up, we know that when we hit this, we need to check that the categories are within the right order. So the last thing that we're going to do is pull all of this over to use root model binding.
10:12
You don't really want all of this code directly hanging around in your controller. To do this, let's start with our web routes and make any a little bit more readable. So we're going to say category from path.
10:23
We're not going to use category as the root model binding, because that's going to go ahead and try and fetch that out of the database because we've done that, we need to change over the regular expression that we've added for any to the name that we're defining in the URL.
10:37
Okay. If we head over to the category show controller, the goal is to be able to grab the last category out. So using root model binding, because we've called this category from path,
10:47
we're going to need to name this variable category from path, naming it category like this is not going to work. So let's make sure that's category from path. And I'm just going to die dump on category from path, just so we know
10:58
that once we've moved this over using root model binding, it's going to work. Okay. So if we go into a deep category here at the moment, we get a 404 not found because it can't find category based on that URL.
11:11
Okay. So we're going to take all of the code that we've added here and cut it out. So we've got a nice clean controller. Next up, we'll head over to the app service provider and under boot, we can
11:21
go ahead and use the root facade to manually define out how we want to resolve this model out based on what we sent through, so let's go ahead and pull in the facade for root and we want to use bind here and give a name in. Now I'm not going to do that just yet because I've copied all of that code over.
11:37
I'm just going to directly paste all of this into here just for now. So all of the code that was in the controller has moved over. Let's go back over to here and we call this category from path. So let's define that as the category from the path.
11:52
Now into this, we'll get the path that we're trying to resolve. Let's die dump on that path just here and let's have a look at what we get. Okay, great. So we're back to square one, but now we are using custom root model binding.
12:05
We already know that all of this functionality works, so this should all work, but the last step that we need to take is actually return the category itself that we plucked out based on that last item, so this is just kind of a check in between just to abort the request if we don't have that within it.
12:23
And then finally we return the category. So remember over in our category show controller, we're dumping this resolved model. Let's give that a refresh and yeah, it looks like it has worked.
12:35
So let's come over and get rid of that die dump. And now that category from path can be passed down and that's just a normal category model. Let's head over and give that a refresh.
12:45
And there we go. So we've now moved that over to root model binding. This is all nicely working. We can click into any of these categories and we've got a nice built up URL.
9 episodes 46 mins

Overview

Let’s master creating nested categories and displaying breadcrumbs for easy navigation.

We’ll start by setting up and seeding nested categories, recursivly iterating over and displaying them with a nested Blade component, then render and customise breadcrumbs to show a trail of category ancestors.

Finally, we’ll finish up with looking at using wildcard routes to show each category slug for perfectly clean URLs.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!