Seeding More Puppies0:00
The next thing we wanna convert to the Inertia way of doing things is the search filtering functionality. So let's dive right in. Our app is pretty cool and old, but it only has nine puppies to this point. So to make the search and filtering a little bit more interesting, I'm going to generate a whole lot more puppies. So I will open the PuppySeeder file where remember we had created nine puppies here.
So I will open the puppySeeder.php file where remember we had created nine Puppy instances here and so I could use a factory and faker to generate data, but I feel like AI makes a much better job at making data that is really on brand with what we're trying to do. So I'll select all of these and I'll ask Copilot to make 100 more Puppy instances with the same shape and using images from 1.jpg to 22.jpg.
and using images from 1.jpg to 22.jpg. And the reason I'm saying these files is remember if I go to storage/app/public/puppies, I have these 22 images sitting there, so I'm going to press enter and we can watch the show together. I think it's going to generate some really interesting and fun puppy names and traits. So let's see what we've got. Uh, it's generating a lot of them.
So let's see what we've got. Uh, it's generating a lot of them. All right, Cody, great listener. Jasper loves to dig. Yeah, it's doing exactly what I was hoping for, which is realistic dog names and dog traits. Which one is your favorite so far? Distinguished gentleman. Winston, this is great. Uh, maybe I didn't need to go for a hundred, maybe 50 would've been sufficient, but we may as well have fun.
maybe 50 would've been sufficient, but we may as well have fun. Puppies are always welcome. Alright, so it should be done very soon and I think this is going to really make the search experience a lot more fun. Alright, and it looks like it is all done. And I'm going to run php artisan migrate:fresh --seed. So it's going to create all these puppies.
fresh dash dash seed. So it's going to create all these puppies. It is also going to delete the users and liked puppies that we had done, but that's not a big deal. We can go back and like puppies again. So let's go check out our app and we should have a whole lot more puppies now. So I will refresh the page and let's start scrolling and yay, look at this.
So I will refresh the page and let's start scrolling and yay, look at this. We got so many puppies, it's recycling through the images but I think honestly that's good enough. Uh, sweet disposition, pure and innocence. That's very true. Uh, I'm happy with that. Alright, so having 109 puppies is going to make us such a lot more entertaining and realistic and also it's going to underline and expose certain trade offs that we can make.
Client vs Server Filtering2:51
and also it's going to underline and expose certain trade offs that we can make between client side filtering, server side filtering. So let's dive in. All right, so let's scroll down here. And so let's search for something like loves to wow there's a lot of results and you can see the search results are super snappy and arguably I'd say that client side search here, setting the puppies to state and then filtering
and arguably I'd say that client side search here, setting the puppies to state and then filtering and updating the puppies state is actually a really good way to have a super snappy dynamic search filtering. But we still are going to see what this experience looks like on the server side search with Inertia and talk about the trade-offs of both. Okay, so right now it for sure feels super snappy. Uh, but it assumes that all the puppies are loading on the page.
Uh, but it assumes that all the puppies are loading on the page. Here we are loading a hundred puppies, which is probably an issue because of the images we are on local hosts here, so it's not a problem. But on a real deployed host this might be problematic. And once you introduce things like pagination, the search uh, dynamic from the client side is not going to work anymore unless again you load all the puppies.
the search uh, dynamic from the client side is not going to work anymore unless again you load all the puppies. So instead of requiring to have all the puppies loaded to the client, we are going to have a search parameter that we pass to the backend and then Laravel is gonna decide what puppies to send to the front end. And so let me show you what it looks like to set that up within Inertia. Okay, so the first thing we're going
Removing Client Filtering4:10
that up within Inertia. Okay, so the first thing we're going to do is break our client side filtering so we don't get confused with what's doing what. The first thing I'll do is go in the PuppiesList component and you can see that here we receive a searchQuery and then we filter the list of puppies by bytes. So I'm going to get rid of that whole filter line and also get rid of the received prop. So the app should still work,
and also get rid of the received prop. So the app should still work, but if I do a search, nothing is going to be filtered because we are not filtering through it anymore. Alright? We've started breaking things to fix them again. So because we are not accepting the search query here, lets me go in the index page and down here we are going to not pass the search query to the IES list. Okay, next, let's go in the search component.
to the IES list. Okay, next, let's go in the search component. And again here we sort of want to get rid of the searchQuery. So I will remove the controlled input value that was set to searchQuery and for the onChange, let's just have a void function for now. So because we've made our input field uncontrolled by removing the value binding, once again the app should still work.
by removing the value binding, once again the app should still work but not work in terms of search filtering. Alright, things are even more broken. That's great. And once again, let me go in the index page. So if I scroll up, we are not using the search query anymore so I can remove it and it looks like we are still using the search query and I imagine it's for the reset. Yep. This is here. So this is the reset button here. So when we type something we can reset
Yep. This is here. So this is the reset button here. So when we type something we can reset but now we've broken it. So we are going to also remove the searchQuery here. Let's keep that here for now. Uh, and so I can also remove that and so the search receives no props anymore. I can delete the state types, which means I can also go back in the index page. And here we are not going to pass searchQuery,
which means I can also go back in the index page. And here we are not going to pass search query, no set search query so I can remove them, which means guess what? I can also get rid of the search query state completely. If you think about it, it sort of makes sense that we are removing a whole lot of client side states because the search functionality is going to move to the server. Okay, so let's take another look at our search components.
going to move to the server. Okay, so let's take another look at our search components. Okay? Uh, here there is this type error because the inputs could be null actually it's set to null here at the start the inputRef. So we need to check uh, if there is a current value and we are able to focus. And here it's probably still doesn't know about the focus method because we need to tell our inputRef that useRef is used on an HTML.
Inertia Search Requests6:44
method because we need to tell our input ref that useRef is used on an HTML input element like so. So now TypeScript should be happy. Okay, great. The next thing we want to do here is whenever we type something in the search input, we want to make a request to the server and pass this searchString as a query parameter. So whenever I type something here, I want to make a GET request to the server for the home route.
So whenever I type something here, I want to make a GET request to the server for the home route. And I want to pass this string as a search parameter that can be used by Laravel to figure out what puppies to send back to us. Alright, so this looks like this is going to happen in the inputs on change here. Basically we want to receive the event which contains the event that targets that value, which going to be the string of the input.
events that targets that value, which going to be the string of the input. And here we will use the router to do a manual visit like you've seen in the previous lesson. So router that we will import from Inertia and then yes we want the get method. So router that get takes a URL first and here this is going to be a route in our case the home route.
and here this is going to be a route in our case the home route. And then we can pass some data, which is where we're going to pass our query parameter. And then we can have some options that we're going to set up. The first argument is going to be a Ziggy route for home because this is where we get all the puppies and pass them to the front end. For the second argument,
and pass them to the front end. For the second argument, let's have Yap a search field which is going to be set to $targets value, which is the value inside the input. So this is going to append a question mark. search equals that value to the URL we are requesting. And so let's start with just that as a starting point. Now I'm going to open the network tab so we can see what's happening and I'm going to press the key A.
so we can see what's happening and I'm going to press the key A. And you can see that we have sent a GET request to localhost:8000, which is the home route. And then search equals A. But our field has been emptied and so every time I try to search it gets emptied again and I can only press one letter at a time. So what's happening is whenever we do a GET request to the server,
So what's happening is whenever we do a get request to the server, the page state is lost by default, but Inertia conveniently offers us a preserved state option that we can pass if we wanna preserve the state. So let's try this out in our router gate as the third parameter here we are going to pass options and I can pass preserveState and set that to true. And so very much like we had preserveScroll through preserveState true is going.
And so very much like we had preserve scroll through preserveState true is going to do the same but for the state. So let's clear the console and this time if I type A, B, C, you can see that my input field is preserved and the search param is being populated accordingly. All right, so let's try to search for loves two and we still have a million puppies and that makes sense. We are actually making GET requests to the server,
Filtering in Laravel9:30
and that makes sense. We are actually making GET requests to the server, but the server doesn't do anything with the query parameter that we passing to it. We need to now tell Laravel endpoint to actually get the search per and filter the results that it passes back to us. Alright, so we are going to go in a PuppyController where we had created the index function here, which is our homepage essentially.
where we had created the index function here, which is our homepage essentially. And so here let's receive the request so we can get the request parameters. So I can go $search = request and I can go directly search like this. And so here we are going to keep our collection, but we are going to modify how we retrieve the data. So let's me comment that out here and we will manually replace it.
So let's me comment that out here and we will manually replace it. Copilot, go away with puppy and we'll do query here. So we can have multiple line statements. Oh my god. And if we have a search parameter, we want to do some filtering so we can use when here and when we have a search, we are going to have a function that accepts the query and also accepts the search query parameter. And so the way we want to modify the query here is go query.
and also accepts the search query parameter. And so the way we want to modify the query here is query where and I'll stop fighting copilot. Basically we want the condition where the name is like the search query parameter and the person signed before and after means that any string can come before and after our search string. And so I'll accept this, but instead of name and breed, we will try to match the name
And so I'll accept this, but instead of name and breed, we will try to match the name or the trait fields from the puppies. So this is the name and this is the trait. So our search filtering is going to try to look for the string. We search for both in the name and the trait, which is pretty cool. Alright? After this we wanna make sure that we still load our User and likedBy relationships.
Alright? After this we wanna make sure that we still load our User and likedBy relationships. So I'm going to go with User and likedBy. And finally I'm going to get the results. So instead of loading all the puppies, we query the puppies. If we have a search parameter, we try to match the name or traits to this search parameter. We load the relationships and we get the data. And so this is a little bit more complicated, but this is now only going to return whatever is filtered.
And so this is a little bit more complicated, but this is now only going to return whatever is filtered by the search parameter. So I can delete this. And so let's give it another shot and I will search for the love term once again, loves, ooh, it looks like it's working. All the puppies remaining here have the word loves inside the description, which is a whole lot of puppies. All right, let's try something else like jump.
inside the description, which is a whole lot of puppies. All right, let's try something else like jump. No one's jumping sleep. Well no dogs is sleeping or jumping. Alright, so we're getting somewhere, but there's definitely some issues with our search filtering at this point. For example, I don't know if you've noticed, but once again we are jumping back to the top of the page every time we do a search.
but once again we are jumping back to the top of the page every time we do a search. So let me show you. If I scroll down and I search for loves, you can see that I've jumped back to the top and so you know how to solve this already. So let's do that quickly. We are preserving the state and we can also preserve the scroll. Let me scroll down to a specific point and search for loves. And indeed we are staying in the same place right now.
Let me scroll down to a specific point and search for loves. And indeed we are staying in the same place right now. It's jumped now, but it's because the list got shorter. But believe me that if there's enough space, the list is going to stay at the exact same spot as I search. Okay, let me show you another thing that you might not notice right away, but it's definitely a big problem. So if I search for eat, obviously we have dogs that love to eat and I look at the address bar,
Syncing URL and Input13:07
So if I search for eat, obviously we have dogs that love to eat and I look at the address bar, we have the eat parameter in the search query, which is great, but now let me refresh the page. Okay, the eat parameter is still here and we only have the dogs that loves to eat, but our input field is empty here. Oops, that's not great. And so if I had happened to do a search that retrieves no result.
And so if I had happened to do a search that retrieves no result and then refresh the page with it, it looks like we have zero puppy in our application, which is definitely not cool. So what we want to do is populate our input field with the search parameter if there is a search parameter essentially doing this when the page loads. And so we could for sure do that on the client side, but I think it makes a little bit more sense
And so we could for sure do that on the client side, but I think it makes a little bit more sense to do this from the server. So the server is going to pass the search parameter to the front end. Let's set that up right now. And back in our PuppyController we go. And so we are passing the puppies to our inertia route and we also want to pass the search, but we might end up having more than just the search
and we also want to pass the search, but we might end up having more than just the search in terms of filtering. So why don't we pass a filters attribute and just like suggested we are going to pass the search in a search key. Alright? So to make our front end work a little bit simpler, let's go in our types and add these filters, type types, index. And somewhere we are going to export interface filters
types, index. And somewhere we are going to export interface Filters and exactly like this, we're going to have this search, which is a string that we know, but let's make this optional. And then we are going to use these key string, but instead of string or undefined, we are going to have unknown. And that allows a number of other parameters. You can see that pattern used for the User.
And that allows a number of other parameters. You can see that pattern used for the User. And so we've done the same thing here. All right, so now we can use that filter type whenever we want to know what sort of shape the filter data has. So we need to receive this searchFilter in the SearchComponent. But remember this is first pass to the whole page components.
But remember this is first pass to the whole page components. So in the index page here, our main app is where we are going to receive the puppies and the filters. And so I can type the filters to be our filters type that we've created. And we are going to pass these filters to the main component filters equals filters, so that here we can receive it. Filters, filters, filters,
that here we can receive it. Filters, filters, filters, and so that we can finally pass it to our search. So the search functionality used to have a searchQuery and now this is replaced by the filters passed from the backend. So filters equals filters. And you can see TypeScript is complaining because we haven't told the search components to accept a filters prop, which is again going to be filters.
because we haven't told the search components to accept a filters prop, which is again going to be filters. Filters. And so I don't know if you remember, but in our original input we had a value that was set to the search query. And so here we could set it to filters, do search, I'm going to search for protect. Hey Jake, protective guardian. And so now I'm going to refresh the page, even hard, refresh the page.
And so now I'm going to refresh the page, even hard, refresh the page. And you can see the query parameter has been populated here from the search parameter from the URL. So if once again we had the situation where we had this completely broken search, at least if I was refreshing the page, it would be clear why there is no purpose showing. Okay, great. Let's be careful though, because here using the value attribute on the input,
Okay, great. Let's be careful though, because here using the value attribute on the input, we have made this input a controlled input where we control the value. And I don't think that we should be doing this. We should let the browser control the value. And so instead of value, I want to use defaultValue here. So let me change the value to defaultValue. And so this is going to set the initial value of the input when the page mounts to the filterSearch,
And so this is going to set the initial value of the input when the page mounts to the filter search, but it's not going to try to control this input. And so everything should work the same. So if I search for loves and then I refresh the page, I hard refresh the page. The loves parameter is still there and everything works as we expect. So keep that in mind. Only use the value attribute on the input if you want.
So keep that in mind. Only use the value attribute on the input if you want. React to make this a controlled input where React maintains ownership of what value is inside the field. Otherwise, use defaultValue and let the browser do browser things.
