Feature overview with Redis0:00
On the Laracast series page, I have this section that lists your currently in-progress series. And basically, the way this works behind the scenes is, each time you complete a lesson, it doesn't update a pivot table or anything like that. It in fact pushes to Redis. That way, at any given time, you can see a handful of the series that you're currently working your way through. So yeah, as it turns out, this is a great use case for Redis. I'm going to show you an example here. I've set up a new project where I just have a bunch of lessons. If we visit them, we see it. Very, very simple stuff. So I want to implement a system like Recently Viewed or Recently Visited or Currently in Progress. The same basic concept. When I visit one of these, yeah, it's going to be pushed to Redis, so that when I come back, at the very top, you will see it. Recently Visited or Currently in Progress. Let's get started. Here we have
Choose Redis data type0:44
yeah, it's going to be pushed to Redis, so that when I come back, at the very top, you will see it. Recently Visited or Currently in Progress. Let's get started. Here we have the controller for what you just saw. When you visit the home page, fetch all lessons, and then load a view. That view will filter through the lessons and display each within a list item. And that's what you see here. Next, when you click on one, we hit the show method. That loads a show view. And as you can expect, yeah, super, super simple. We're not even using layout files, just basic stuff here. Okay, so it sounds like when we visit a specific lesson, we will push to Redis. Now, what kind of data type do we use? Because, of course, we could push to a list, we could push to a set, or we could push to a zset, a sorted set. What is the best choice? Well, first, maybe a quick recap. I know it's easy to forget the differences. For list, again, just think of it as
lesson. But just remember, yeah, if you want, you could always cast that to an array. And then this is especially good for like news items where you want to say five most recently published news items. You display it in a little widget in your sidebar, and all of that data is dynamic, but also never touches the database when rendered. Very, very cool. But yeah, in our case, let's keep it simple. Let's just stick with IDs. Okay, so I will import that at the top, Illuminate\Support\Facades\Redis. And you know what? Let's just see if it's working. So we're going to go back to Chrome and visit any item. Okay, I will next open up the Redis command line. And let's fetch a ZRange on user.1.inprogress, starting at the first, ending at the very last item. And we get 16. And that happens to be the ID of the lesson, as you can see right here. Let's do one more. Let's do 25. And then finally the last lesson, which has an ID of 30. Okay, 16, 25, and 30. So if I were
And that happens to be the ID of the lesson, as you can see right here. Let's do one more. Let's do 25. And then finally the last lesson, which has an ID of 30. Okay, 16, 25, and 30. So if I were rendering these, I actually want them in reverse, right? I visited the lesson with an ID of 30 most recently, so that should be on top. We can do that by simply saying ZREVRANGE, or reverse range. Now though, what about the score aspect? Let's come back and visit the lesson with an ID of 16 again. And it doesn't matter how many times we click it, because we're not using INCRBY. We're not saying ZINCRBY. We're just saying ZADD. And when you call ZADD and the item currently exists, it just updates the score. That's it. But this does present a problem, because I give it a bunch of clicks and I would want that to jump to the top, but it's always at the bottom no matter what. If we go to the lesson with an ID of 25, again, that won't jump to the top. It stays where
Use timestamps for recency4:51
bunch of clicks and I would want that to jump to the top, but it's always at the bottom no matter what. If we go to the lesson with an ID of 25, again, that won't jump to the top. It stays where it is. So it sounds like we don't want to use a score of 1. We are instead going to use the current time. So now think about it. Whenever you visit a lesson, or a video, or a news article, at all times, because we're using a timestamp, when we are sorting those results, that most recently visited article will always be at the top of the stack. Let's try it out. Give it a refresh. And now if we run it, and do note the ID 25. Anyways, now if we run it, 25 is at the top. And in fact, if we add with scores, we can see the current timestamp here. Okay, so now that means if we go to the lesson with an ID of 16, the current timestamp is applied. And by the way, if you're not familiar, when you're using zadd, if the item already exists,
Okay, so now that means if we go to the lesson with an ID of 16, the current timestamp is applied. And by the way, if you're not familiar, when you're using zadd, if the item already exists, it simply updates the score. So anyways, if we were to give this another refresh, now this one is at the top, because that timestamp is most recent. Pretty cool. So let's grab that. We're going to come back, and we can spit this data out now. Here's what we typed into the command line, and we'll convert it. Z, revrange, user:1:nprogress, 0, -1. But you know what? We don't want all of the items. Let's say we want the three most recent ones. Well, we can always just say not 0, -1, but 0, and let's just say 0, 1. So if we do that, we get two items, the first item and the second. If we want three, then we say 0, 2. Makes sense? So let's update this. Now, right now, we don't have a collection of lessons. We have a collection
Load lessons and preserve order6:35
we get two items, the first item and the second. If we want three, then we say 0, 2. Makes sense? So let's update this. Now, right now, we don't have a collection of lessons. We have a collection of IDs. So if we come back and give this a refresh, those are the three most recently visited items. So if I go to lesson/4, and we go back, now 4 will be at the top. So it sounds like we should do this. Yes, we could just do Lesson::find, or findMany, it's going to do the same thing, and then feed it an array of IDs. And it is true, you will get those. So do note, though, 4, 16, 25. We run this, though, and of course, just by coincidence, the order happens to be as we would expect. But let's try to mess it up. Okay, so now, even though we just visited the lesson with an ID of 30, when we performed the query, it was at the bottom. And you can even see right here,
Okay, so now, even though we just visited the lesson with an ID of 30, when we performed the query, it was at the bottom. And you can even see right here, if I were to say return in progress, you'll see it at the top. So 30, 4, 16 is the order that we want. But once we ran it, yeah, it's not going to be consistent. When you're working with Eloquent and a MySQL query, there's no guarantee about the order. So you can solve this in a number of ways. One, you perform the query, and then you handle the sorting on the collection side, either by using sortBy or form of map, where maybe you'd save all of your lessons, and then you'd just say return the lesson where the index matches this, you know, there's lots of ways to do this. However, we're only fetching three items. So even if we perform three quick database queries, I think that's easy and simple enough. So why don't we do this?
Render in-progress list8:17
there's lots of ways to do this. However, we're only fetching three items. So even if we perform three quick database queries, I think that's easy and simple enough. So why don't we do this? Let's collect inProgress into an array, and then we'll map over them. And for each ID, I'm just going to return the lesson associated with that ID. And if it helps, why don't we say inProgressIDs, and then we'll save this to inProgress. Okay, so now they should be in the proper order, and they are. Simple enough, easy way to go. All right, so let's give it a refresh. And the only remaining step is to display these. So I'm going to go to lesson/index. And why don't we, why don't we say all lessons, why don't we say all lessons, and then I'm going to duplicate this. And we'll say, inProgress, and filter through them. Okay, so let's pass inProgress through
