Creating Names Endpoint0:00
Today, I'll show you a nasty little gotcha related to returning JSON from a route that is first filtered. I'll show you what I mean. Let's create an endpoint here called names. And all it's going to do is create a collection of names. So we'll say Jack, Jill, Jane, June, Jim. And to start, we'll simply return that from the route. So of course, we know that will be cast to JSON. If I switch to Firefox and load that route, here's what we get. And this is the raw data.
Building Filter UI0:29
If I switch to Firefox and load that route, here's what we get. And this is the raw data. All right, no problem yet. So let's keep going. We'll now load our welcome view. And we're just going to set up a dummy little thing where it spits out a list of names, but you have a select menu to choose the first letter of the name. So if I choose A, it'll filter it down to all names that start with A. Okay, you'll see that I pulled in Alpine here to make it pretty easy to make this a bit more dynamic.
Okay, you'll see that I pulled in Alpine here to make it pretty easy to make this a bit more dynamic. And here's the plan. I'm going to render those names within a list item. So with Alpine, if I want to loop over a collection, for example, a collection of names, then I'll have to surround this with a template tag. Then I can say x-for='name in names'. And actually, why don't we grab the index there? Because as a general good practice, you want to always specify a key. So we'll use the index there.
Because as a general good practice, you want to always specify a key. So we'll use the index there. Okay. And when we initialize, now I could say the text for this list item is going to be the current name in the loop. Okay. So as an example, if I have foo, bar, baz, and we load this in Firefox, it works. You get the idea. Very simple. So now what we're going to do is say, well, when we initialize this little component here,
Fetching Names with Alpine1:40
Very simple. So now what we're going to do is say, well, when we initialize this little component here, why don't we fetch the list of names? And then with the response, I actually want the JSON response there. And then once we have our results, that will be the list of names. We will simply assign it. names equal results. And we'll clear that out. All right. So with any luck, if I come back to Firefox, there we go.
Wiring Letter Selection2:27
menu with an option, something kind of like this. So if I come back and give that a refresh, if I change this to another letter, it'll give me only the results that start with that letter. So luckily, php makes this pretty easy by using the range function. So I could say foreach range(A, Z as letter), then use that letter. So letter, like so. All right. Let's come back to Firefox, give it a refresh, and now I have a long list of letters. But of course, when I select one, we're not doing anything. So let's change that by saying, when you change, when you choose an option, let's fetch the
But of course, when I select one, we're not doing anything. So let's change that by saying, when you change, when you choose an option, let's fetchNames again. So it sounds like we need a helper here, like this. fetchNames, and I can take all of this and now bring it up. So when you call this method, we'll make a AJAX request to fetch the list of names and then assign it to the names property. Okay, so now when we initialize, we're going to fetchNames. But because this is now in a method, I bet it's not going to work. No, it doesn't work.
But because this is now in a method, I bet it's not going to work. No, it doesn't work. And that's because now that we're in a method, names, we actually have to do this.names. And that should bring us back. All right, so it's true if I open up the network tab, if I were to change one of these, yep, we make an AJAX request. But we're not doing anything with that letter. So it sounds like I need to track what that letter is. That's easy enough by using XModel, and we'll call it letter, where that will default to A. Great.
That's easy enough by using X model, and we'll call it letter, where that will default to A. Great. So now that we're tracking which letter you select, why don't we include that as part of the request? And we'll just pass this through the query string, the firstLetter equals the letter that you passed in. Okay, so have a look. We're going to give this a refresh. And now you'll see when we make that AJAX request, we do include the firstLetter. Okay, so now, if I come back to our main route here, we could maybe say, well, if we have
Filtering Names Server-Side4:23
And now you'll see when we make that AJAX request, we do include the first letter. Okay, so now, if I come back to our main route here, we could maybe say, well, if we have something in the request for the firstLetter, we'll return our names. But first, we're going to filter it down to only the names that begin with the given letter. So let's actually save this. And how can we say, check if this string begins with the letter J? You could probably do str_starts_with. I believe PHP will eventually have startsWith. Because we're using Laravel, I can just say str_starts_with and use its implementation. The haystack is the full name and the letter is what we're looking for.
Because we're using Laravel, I can just say stringStartsWith and use its implementation. The haystack is the full name and the letter is what we're looking for. All right, so now, if I come back one more time, we will make a selection for J. And no, that's not right. It made a request to first letter of A. What? That's not right. What's going on here? Give it a refresh. When in doubt, always do a refresh.
Give it a refresh. When in doubt, always do a refresh. And it still doesn't work. What's going on there? So we've bound to K, but it's still making an old AJAX request. All right, let's have a look. X model is letter. And when you change the select, fetch the names. Huh, this is right. By some chance, does that need to come first?
Let's say we have names that also start with B or C. If we come back, give it a refresh. Let's go to J, and it doesn't work. Is there something about our JavaScript that doesn't like the names Bill and Chuck? I remove it. We'll choose J. It works. But as soon, if I only added Bill, it doesn't work. B doesn't work, and J doesn't work. This is a super perplexing ditch you can easily fall into. Now the issue is related to filtering, collection, or an array that we then cast to JSON. So let's tinker for a little bit.
Fixing JSON Object Bug6:55
Now the issue is related to filtering, collection, or an array that we then cast to JSON. So let's tinker for a little bit. I'm going to remove Bill again, and of course we know that's going to work. But if I go forward, let's go back to that names endpoint and have a look. So if we get names filtered by the letter J, yeah, we get that. Raw data is an array. Everything looks good. But then we add Bill, and if we try it again, take a look at the raw data, notice it's no longer an array. It's an object.
So notice when we filter, there's nothing that resets the values or reorders the values. And have a look. This makes it even easier to understand. Let's go ahead and cast this to JSON, and then I have a way to run this with TinkerWell inline. So yes, we get our array. But as soon as we add indexes to this, so maybe something like this, well, of course, now if we run it again, we have an associative array. So that will be cast to an object. So yeah, now we're starting to see, well, when you filter it down, if you're removing
So that will be cast to an object. So yeah, now we're starting to see, well, when you filter it down, if you're removing items, we'll have an associative array that will be cast to an object, which means when we make the request right here, names is not an array that you can loop over. It's an object. So yeah, these are the sorts of things that can easily fall through the cracks, and they won't often show up in your tests. So the lesson of the day is, if you're calling filter or you're performing some action that's going to change those keys, make sure you reorder them if you expect an array when you decode it.
going to change those keys, make sure you reorder them if you expect an array when you decode it. And you can do that super easy with a collection just called values. Behind the scenes, it's just going to pass it to array_values. So you could use that directly as well with vanilla php. All right, come back. Give it another try. We choose j. What else do we have? Bill.
