Adding Status Filtering0:06
All right, so now we move on to filtering. It would be helpful if I could say, show me only the completed ideas, or show me only the pending ideas. So if you think about it, every idea has a status, right? So if I went to idea controller, well, would it be possible to just say, well, give me the ideas where the status is pending. Is that an option? Yeah, it is. So if I come back and give it a refresh,
Is that an option? Yeah, it is. So if I come back and give it a refresh, now we only see the pending ones. And yeah, of course if I swapped this out with a completed, give it a refresh. Now we get only the completed ones. Okay, so it sounds like we just need this to be dynamic based on something within the query string, right? So maybe we could say request, um, status
the query string, right? So maybe we could say request, um, status and we'll default to pending. All right, one step there. So if I come back and give it a refresh, uh, we haven't explicitly said anything for status, so it gives us pending, but if I said status is in progress, now we get only in progress, right? If I change it to completed, you get the idea, okay? But yeah, it would be nice if there was a way to say, well,
Conditional Query with when1:11
If I change it to completed, you get the idea, okay? But yeah, it would be nice if there was a way to say, well, if there is no query string in the status, just default to all and we can't really do that, right? So here's what we can do though. Let's put this on their own line. I really wanna say, okay, when we have something in the request for status, then apply this query scope. So as it turns out, eloquent has a win method.
then apply this query scope. So as it turns out, eloquent has a win method. When we have a status, then accept the query builder, accept the status from the query string and apply, um, the condition. So query where status is status. All right? Does that make sense? Take a look at it. Pause if you need to, when, and of course we need the request when we have something in the request for status, then
and of course we need the request when we have something in the request for status, then and only then do we append to the query and we add this, um, wear clause, then get the results. Okay? So now if we reload this page, we don't have any filtering, so we show everything. But if we do add a filter, it's working and it's so easy, isn't it? I love it. So now if I come back to my editor, let's return to our view and yeah, it sounds like we need a place
Building Filter Pills UI2:25
I love it. So now if I come back to my editor, let's return to our view and yeah, it sounds like we need a place to display little pills so that I can choose which, uh, filter I want to apply. So I dunno, maybe we'll put it right up here, at least to start. This will be an anchor tag that will hit the idea page, uh, or ideas. And then yeah, if we hard coded the status just to show you, come back, let's clear out the status,
And then yeah, if we hard coded the status just to show you, come back, let's clear out the status, and now we have an UNILE button, but when I click on it, sure enough, it's working. Okay. So it sounds like I need one for each status that we have, and I want it to look nice. So why don't we start by saying, well, can we style this like a button? Yeah, but you know what? That makes it look like it's selected
Yeah, but you know what? That makes it look like it's selected even if it's not, right. So maybe we could say button outlined. Yeah. Now it just looks like it's a selectable button, but it's not currently on, but when I do click on it, I want it to look like it's on. So it sounds like button outlines should only be applied, um, if it's not on, okay, so we could say, look in the request for status, and if that equals,
um, if it's not on, okay, so we could say, look in the request for status, and if that equals, and I'm just hard coding everything right now, then um, it, we don't have anything. But otherwise we have button outlines. Yeah, it's a little confusing, but I think that's right. Yeah, it's turned on. But if I remove that entirely, it's effectively turned off. Yeah. So you can imagine we could do the exact same thing and we're gonna make this dynamic,
Yeah. So you can imagine we could do the exact same thing and we're gonna make this dynamic, but just to show you What we could do there, right? So give it a refresh here is pending here is in progress. It's just not that hard. Okay? But I don't wanna duplicate all of this. Uh, so instead, why don't we loop over all of the options for our enum here pending in progress completed. Okay, so why don't we say for each,
for our enum here pending in progress completed. Okay, so why don't we say for each, and I'm just gonna reference idea status directly. Uh, some people might say that's big. No-no, you should pass it from the controller. Yeah, sure. Okay. Also, sometimes it just doesn't matter. So I could say, for each idea status, give me all of the various cases, and for each one as status, then render, um, a link. Okay? So now I can get rid of that entirely,
then render, um, a link. Okay? So now I can get rid of that entirely, and I'm sorry, I thought I imported that. And if I come back now, we should have three that are still hard coded. So now we make them dynamic and let's add a little more real estate. Okay? So now this can be swapped out. Check the value of the status. If it's equal to what's in the request query
Check the value of the status. If it's equal to what's in the request query string that works. Then we can do, um, excuse me, status and do the label come back pending in progress completed. Here's completed. Ooh, we have to update the URL of course. All right, one more time. We have pending, we have in progress, we have completed, but we probably need an extra one for all. All right, so why don't we just duplicate that? All right.
but we probably need an extra one for all. All right, so why don't we just duplicate that? All right. So as you can imagine, there won't be anything in the query string in that case, uh, the text or the inner content will be all. And then how do we determine whether it should be outlined? Well, maybe kind of tricky. Let's come back and just figure it out. All right, so currently it is activated no matter what. So we could say button outlined.
All right, so currently it is activated no matter what. So we could say button outlined. All right, Good. But then when I click all, it doesn't turn on. So when I click all though, that means there's no, um, there's no status set. Okay? So let's do this. Request has, do we have something for status? If that's the case, then we would do button outlined. Yeah, I think, I think that would work. Let's come back.
Showing Counts per Status6:07
If that's the case, then we would do button outlined. Yeah, I think, I think that would work. Let's come back. Yeah, here's all, here's pending. Yeah, a little confusing, right? Uh, but nonetheless, that's gonna work just fine. Okay, so next, I think it would be helpful to know how many, um, cards or ideas I have for each one, right? So maybe after completed for this one, we'd see one for end progress, we'd see one and four pending. We would see the number two right directly within the pill.
for end progress, we'd see one and four pending. We would see the number two right directly within the pill. How can we do that? Well, let's do this again, apologies for a screencast. You just don't have much real estate. So we'll do something like that. Alright? So we'll have our label and then we'll have a span. And I'm just gonna hard code two for now and we'll just add, uh, we'll make it extra small and just a little padding to the left
and we'll just add, uh, we'll make it extra small and just a little padding to the left to push it away from the label itself. So now, yeah, you can see two and this gives you the idea, alright? So this needs to be calculated dynamically. So how can we figure out how many ideas we have, um, for each status? Well, if you think about it, that probably needs to be its own query, right?
Well, if you think about it, that probably needs to be its own query, right? So let's see, why don't we do a little bit of database school? Let's go into table plus. And of course, here is our ideas. Of course, each idea has a status. So I wanna inspect the counts here. All right? So let's write one by hand and then we will convert it into, uh, an eloquent query. I think that will help you understand
and then we will convert it into, uh, an eloquent query. I think that will help you understand and gr it select counts from ideas. All right, so this will tell us that we have a total of five ideas. Fine, but next, think about it. I wanna get the status itself, but I wanna say like, give me the status and the count for each of that status, right? So I wanna get the count,
and the count for each of that status, right? So I wanna get the count, but group them into little categories for each status. So group is the key word there, right? We would say slick status from ideas, group by status. There we go. So now you can see for completed we have one for in progress, we have two. And for pending, uh, we have three. And is that correct? Pending, pending, pending. We do. Cool. Alright.
And for pending, uh, we have three. And is that correct? Pending, pending, pending. We do. Cool. Alright. So this is effectively the query that we want to run using eloquent. So I'll show you how to handle some of these cases where it doesn't perfectly map to a simple, uh, eloquent method. It's pretty easy still. So what I'll do is right here, I will simply paste this in so we can refer to it when needed.
So what I'll do is right here, I will simply paste this in so we can refer to it when needed. Okay? So why don't we call this, I don't know, status counts or something like that. All right, so we're gonna start with idea. I'm gonna create a new query and I'm gonna do not just a select, I'm gonna do a select raw, this is raw database query stuff. And yep, I'm just gonna grab exactly what we had here. Next, I'm gonna group by the status, right?
And yep, I'm just gonna grab exactly what we had here. Next, I'm gonna group by the status, right? So we're just reproducing what we had before and let's get the results. And then I'm just gonna return it directly from the route so I can inspect it. Okay? So if I give it a refresh, there we go. It's working. Cool, but notice count star, we wanna give that a name, okay? Fair enough. Why don't we change the count to as count?
that a name, okay? Fair enough. Why don't we change the count to as count? Alright, there we go. But I think we do have one problem here, actually, now that I think of it. Um, let's imagine that we didn't have anything for completed. Well, let's simulate that by going back to idea and let's just change this one to pending. Okay, well, once again, if I run that query by hand,
and let's just change this one to pending. Okay, well, once again, if I run that query by hand, notice completed is omitted entirely, and ugh, that's not quite what I want. I I really wanted every possible status and then the count next to it. But if we don't have one, it gets excluded here. All right? So there's a couple ways we can deal with this. I know that idea status contains all of the, the various cases, right?
I know that idea status contains all of the, the various cases, right? Every possible status we have is, uh, included here. Okay? So why don't we collect those and then I'm gonna map over them. But I'm actually gonna use one called map with keys. It sounds scary. It's not, it's not scary at all. Map, you know, loop over, um, a collection and return something for each item, right? Map with keys is just return something for each item,
and return something for each item, right? Map with keys is just return something for each item, but also include what the key should be. That's the only difference. Okay? So this will accept the current status, right? And it's going to return an array. The key is going to be, um, the value, right? So this would be pending in progress. That's our key, all right? What is associated with it? The count, right? So I can say status counts
That's our key, all right? What is associated with it? The count, right? So I can say status counts and get me the value for this status. And can we set a default here? Uh, yes we can. Cool. So we can default to zero. Yeah. It turns out this is exactly what we want. So if I were to return this, and then I'll explain it to you. 'cause I know it's maybe a little bit confusing if I come back and refresh.
'cause I know it's maybe a little bit confusing if I come back and refresh. Now notice all three cases are represented, but the values are not quite right. Let's see, real quick, let's debug together. All right, here's our status counts. We have model instances. Yeah. So in that case, I can't do that. Can I, I can't say get status because I'm actually dealing with the model.
Can I, I can't say get status because I'm actually dealing with the model. Okay? So in that case, why don't we just pluck what we need? I don't need a full eloquent instance. I just need to pluck, uh, the status. So notice if I were to say pluck the count, and once again, I just wanna show you this one step at a time. Uh, now we're gonna get just a plain old collection that contains the count.
Uh, now we're gonna get just a plain old collection that contains the count. However, if I then say the status as the key, come back and refresh. Now I have a collection where, um, the key is the status and the count is um, the count, cool or the value is the count. Okay? So now that should work. I think we can now say, okay, get me the value associated with this key.
I think we can now say, okay, get me the value associated with this key. So if I were to say pending, if I come back, I get four. If I do an progress, I get one. We're just getting the counts there. Okay? So now we loop over all of our idea statuses, and for each one we're just gonna return pretty much the same thing pending equals, well, how many do we have there, uh, completed? And then how many do we have there?
how many do we have there, uh, completed? And then how many do we have there? And zero, if we don't have any come back refresh. And now we have pending four and progress one completed zero. It's a little bit more complex, but it's, it's just not too bad. Let's clean this up and I'll refactor. Good. Okay, so now here's the especially cool thing. What about, all right, we should have one for all
Okay, so now here's the especially cool thing. What about, all right, we should have one for all that represents all ideas for this user. So I can put to this collection, I can say, put a new one called all, and I'm just gonna hard code it to zero for a second. And you can see now we get a new one there. All right? So the value should just be, oh, how many ideas do we have? And in this case, we would just have to do one more query off user ideas count,
And in this case, we would just have to do one more query off user ideas count, come back, refresh, and we get uh, four. And actually, you know what, this just showed me something. We gotta be a little bit careful, right? So we have four, but notice that this adds up to five. And that's because we forgot to scope this query to the user, which is why, like I said before, I always prefer to start with the authenticated user and then grab the ideas in that way.
before, I always prefer to start with the authenticated user and then grab the ideas in that way. This way, no matter what, I can always assume that it's scoped to the user, come back, refresh. And now we have solved, but pretty big problem. Uh, but this of course is what tests would reveal as well. Okay? So why don't we just rename this to counts. Maybe this one will become status counts, like so. And then we will send that to the view, all right?
counts, like so. And then we will send that to the view, all right? And then later we're gonna move this into the model itself. I think that's a good place for it to go. All right, so now we have a new status counts. We can simply fetch that, uh, as we loop through these like this instead of hard coding two, this will now be status counts. Get for the label, gimme the count associated with, um, I'm sorry, the value of the status.
Get for the label, gimme the count associated with, um, I'm sorry, the value of the status. All right, let's cross our fingers. And there we go. We have three pending and it checks out. We have one in progress and it checks out. We have zero completed and it checks out. So yeah, I get it. It's maybe a little bit tricky, but you know what? Once you've written stuff like this, it's actually, it makes perfect sense to me, but I get how initially it's a lot to remember.
Extracting Counts to Model15:02
it makes perfect sense to me, but I get how initially it's a lot to remember. Okay, so now I'm just gonna wrap up by extracting this into the idea model like this. So I'm gonna go into idea and let's just add a method maybe right here. And this is gonna be static and status counts, I dunno, whatever you wanna call it. And I'll paste all of this in. Alright, so why don't we accept the user here
And I'll paste all of this in. Alright, so why don't we accept the user here and then I can update these, all right? That way it can be dynamic. And then I can see here at this point, there's no reason to assign this to a variable that's superfluous. So I can return that directly and this is going to give us a collection. Alright? All right, makes sense. We're just moving this code somewhere else
Alright? All right, makes sense. We're just moving this code somewhere else that maybe makes more sense. And could there be situations where elsewhere in our code base, I might wanna know, well what are the status counts for this user? Well now I just have a simple method I can call. I like it. So now I can replace this with idea status counts and I'll send through the authenticated user. All right, we cross our fingers.
Code Review and Validation16:07
status counts and I'll send through the authenticated user. All right, we cross our fingers. Here's all pending in progress completed. It's working. That's great. Okay, so let's just run code rabbits. Let's then format our code using Pint and Rector and we will call it an episode. Alright, let's do this. Now let's go into Code Rabbit. Here's some files to review. Let's do it. Alright, and here we go. Let's just work through them very quickly on card.
Alright, and here we go. Let's just work through them very quickly on card. Let's see what we have here. Component always renders as anchor, but usage includes non link cards. Ah, so in this case, yeah, it's saying you have an anchor tag and you don't hard code the HF because we're gonna pass that in from the outside. We could make that explicit. But you know what, this is not something I'm worried about.
We could make that explicit. But you know what, this is not something I'm worried about. Okay, let's go into idea controller. Catch the authenticated user and validate the status filter. Yeah, here there're saying, Hey, you reference auth user twice. Maybe cache that in a variable, it doesn't really matter. Big whoop. Uh, next though, this is, this is valid, this is valid. We should validate the status filter.
this is valid, this is valid. We should validate the status filter. So that's saying, Hey, you're just assuming you get a status that we support. What if the status is gibberish, right? Well, we don't get anything at all. Maybe that's okay for you, but you also might want to validate that if one is included, uh, it must be pending in progress or completed. So I'll show you, if we wanted to pass this off to ai, what
uh, it must be pending in progress or completed. So I'll show you, if we wanted to pass this off to ai, what that would look like, you would click fix with ai it prepares the prompt. Alright, so let's see the changes. Uh, so they did the thing where they cached the variable. Alright, that's fine. And then they update it here, no problem. And then here, let's see what they do. So they grab all of the uh, idea statuses pending
And then here, let's see what they do. So they grab all of the uh, idea statuses pending and progress completed and they store that in a variable and then they add some request validation that the status from the query string can be knowable or uh, it, it needs to be within this list of options. And then they apply this query conditionally based upon whether we have a validated uh, item for status. If we don't have one, then one was not
uh, item for status. If we don't have one, then one was not included or it wasn't valid. So this is okay, I don't think I would actually do this to be honest. Uh, one quick little note is it may not know about this, but there is a rule enum you can reach for instead. So you could do idea status, uh, that would work, but I'm not even sure request validation would be the way to go here.
but I'm not even sure request validation would be the way to go here. And I also would end line something like this directly here just to clean it up a bit. Instead, what I would probably do is something like, um, check or let's grab the status from the request if we have anything at all. And then I could say, well if it's not in the array of pending and progress completed, um, I'll show you real quick.
pending and progress completed, um, I'll show you real quick. If I hard code this in progress and then we will swap it out. If it's not in that array, then let's just reset it. Let's just set it to null, right? We're not gonna deal with anything there. So now only on the condition that status is pending in progress or completed, do we apply this?
that status is pending in progress or completed, do we apply this? So if we try that out, this works pending in progress. But if I do something different, uh, notice it just gets ignored entirely and I think that would be fine. Uh, so if we were to make this dynamic, it's the strangest thing with PHP, you would think you could do something like idea status and you can do cases, right?
you would think you could do something like idea status and you can do cases, right? But if you just want the backed values, like give me an array of the backed values as you can see here, you can't do that. Uh, I'll show you two steps for what you could do. We'll do a little Professor Jeffrey thing here. Uh, you could say array column and you could do idea statuses, uh, cases and then give me the value for each of them.
and you could do idea statuses, uh, cases and then give me the value for each of them. Um, so that's the same as before when we were doing status value, right? So we're just getting a new array that contains the values that would work. So if I give it a refresh, yep, but if I do in progress, that works as well. Um, but if this is something you might do more than once, you could of course add your own little
Um, but if this is something you might do more than once, you could of course add your own little helper method like this. Let's add a static method and we're gonna call this values and we're just gonna do an array map. So let's map over all of the cases so I can do static cases and for each one that will be status. I'm gonna return the value. Yeah, lemme take a look at that. Is that right? Yeah. Map over 'em, pass through the cases for each one, return the value.
Is that right? Yeah. Map over 'em, pass through the cases for each one, return the value. Um, that's fine. So you can add the things that aren't available to you. You could even extract this to a reusable trait if you have a lot of enums in your project. Okay, so I think that should still work and it would allow us to clean this up a good bit. I can instead say idea status, values. I think that's right.
and it would allow us to clean this up a good bit. I can instead say idea status, values. I think that's right. Let's come back. Yep, yep, yep. Let's go to gibberish. Yep, we get everything. It's working. Cool. So anything else here? I wanna go a little bit quick. No comments? Yeah, this is fine. Everything looks good. So now we're gonna wrap up by opening the terminal and I'm gonna run composer run format. This will run Rector on my code base. It's gonna add, uh, strict types
This will run Rector on my code base. It's gonna add, uh, strict types and things like that that we didn't do manually. And then at the very end it ran pint. So I would now commit my changes and I am ready for the next episode.
