Defining Featured Collections0:40
Right here, I have a bunch of these on the page. Now internally, I refer to these as featured collections. So a featured collection has a title, a description, and then a set of series to go along with it. So here's one called Keep Going. That is the user's in-progress collection. Here are the series the user is currently working through. Here's another one. It looks like we have one series on our watchlist, and that's stored as a dynamic featured collection. Here's another one.
You get the basic idea. Here's another one with a custom layout for leveling up your testing. I think you get the idea. Now if we go to the LaraCast code base, I'm going to scope this down to only the relevant files. All right. This is what I've set up for us. And again, this is heavily modified code to give us lots of opportunities to clean it up. So it's not quite what I have on the site, but it's enough to get us going.
Database Model and Tables1:50
up. So it's not quite what I have on the site, but it's enough to get us going. So here you can see a featured collection. All right. Fair enough. Looks pretty basic. It's a model, which means it's backed in the database. Here we go. Here's my featured_collections table. It has a title and a description.
Here's my featured collections table. It has a title and a description. So these, like for example, how about build an app? Build an app. There it is. Here's the corresponding featured collection, and in this case, it has three series. So these three series are going to be in a pivot table. Standard stuff. You've probably written something similar in your own projects. And here's that relationship.
You've probably written something similar in your own projects. And here's that relationship. So belongsToMany between a FeaturedCollection and the Series table. Okay. So this Query class now, in particular, is fairly horrid. There's so much going on here. It is not open to extension. Any change we'd have to make would require lots of modification. It has far too much knowledge of different components of the site. It's a big old mess that we're going to clean up together.
Tests Before Refactoring2:52
It has far too much knowledge of different components of the site. It's a big old mess that we're going to clean up together. However, I would never refactor a set of code without a suite of tests backing me up. And the reason is it's just too dangerous. The likelihood that you're going to introduce a bug is almost 100%. It's just not worth it. So if I didn't have a set of tests here, I would write those tests. And only at that point, if I were confident, would I perform the refactor. So let's have a look here. First, we have a set of tests for the featuredCollection Eloquent model.
So let's have a look here. First, we have a set of tests for the featured collection Eloquent model. And this is really simple. It has a title. All right. Let's test it by creating a featured collection, persisting it, and then checking the title. This is super simple. It's almost a syntax check. It makes sure the column exists in the database, and it makes sure we can access it as a property. That's it.
It makes sure the column exists in the database, and it makes sure we can access it as a property. That's it. The same is true for a description. And then we have one for the series relationship. So given I have a featured collection, and given I have a series, well, if I add that series to the featured collection, then if I fetch all series associated with that collection, of course, there should be a total of one. And then specifically, if I fetch this series, it should contain the one that we created above here. And of course, that passes.
Introducing the Query Class4:55
I can't hard-code that relationship in a pivot table. Recently updated. Same thing. This will change every single day when we update a series. Trending. This is dependent upon what the whole community is watching. I can't hard-code that in a pivot table. So this is where we have to delegate to this query class. Now a query class is exactly what you think it is. It is a wrapper around a database query or some kind of complex query.
Now a Query class is exactly what you think it is. It is a wrapper around a database query or some kind of complex query. Now I don't want you to focus too much on this code because the whole point, I've constructed it in this way to allow us to piece by piece make changes without having to take in all that's going on here. But at a quick 30 second level, when we call this query->get(), real quick notice query->get(), that performs the query. It looks like we're going to cache it. We're going to fetch any Feature collections we have in the database. We're going to shuffle them in random order.
Identifying Design Problems5:50
We're going to fetch any feature collections we have in the database. We're going to shuffle them in random order. And then we're going to prepend these dynamic collections. So get trending collection. Here's what's happening there. Now this method alone, I see so much that I hate. Just think about it. This is a query class. And yet it knows exactly how we track and store popular series on the site. And even more than that, it knows that we use Redis.
And yet it knows exactly how we track and store popular series on the site. And even more than that, it knows that we use Redis. It knows the specific Redis method or action. I don't know what that term is called. But whatever the call or action or method would be, it knows what that is. It knows the key we use to identify popular series. And then finally, it knows that if you run this command, maybe it's called command, if you run this command, it returns a set of IDs. That is way too much awareness. Next, it looks like this method sometimes returns an object and sometimes returns false,
That is way too much awareness. Next, it looks like this method sometimes returns an object and sometimes returns false, which I generally don't like. The more you do that, the more complexity you add and likelihood for bugs. Next, we got something going on here. It looks like we are fetching the series in a specific way to retain the order. There's a call to filter here. Usually when you see filter with no callback, you're just filtering out falsy values. And then it returns, well, this looks like a featured collection, right? We got title, description, an optional theme, and then the associated series.
And then it returns, well, this looks like a featured collection, right? We got title, description, an optional theme, and then the associated series. But we're not returning a featured collection. We're returning a plain old php object. And in fact, if we look at these other methods, they're doing the exact same thing. They get a series and then they return a primitive object. Here's another one for the users in progress series. And yet again, look at all of this knowledge. Think of the singleResponsibility principle. That's a hotly debated principle in terms of its usefulness.
Think of the single responsibility principle. That's a hotly debated principle in terms of its usefulness. But I always think of it on a practical level. Is it the responsibility of this class to know about that? That's how I interpret it. So in this case, our database query class, is it the responsibility of this class to know the key for storing a user's in progress videos? For knowing how we access it and what is returned from that Redis action? Is it the responsibility? I don't think so.
Is it the responsibility? I don't think so. I think this is a very procedural way to do it. Because really, if we didn't have this code, we would want something like whether we went from the User direction, like user in progress, or series in progress, or if you wanted to go in the opposite direction. Maybe we have a Series model, and then you want to say, well, give me all the Series that are in progress for the given User. This is kind of what we want. But notice, for whatever reason, it's not there.
Yet again, the open-closed principle says that code should be open for extension, but closed for modification. So we can put this to the test. What if I wanted to add a new custom collection, like frequentlyRequestedSeries, or frequentlyRewatched? Okay, well, it would probably be something down here. And actually, a little tip. The way you structure your code is the way others will contribute to your code. In most of the cases, especially for open-source contributions, they're not going to completely restructure and refactor what you have.
Refactor Goals and Next Steps11:02
So I think you get the basic idea. We have a general idea of what's going on here, but it seems very complicated and very messy. So in the next episode, our job is to start the refactor. We know where we want to end up. We want to end up in a point where any time I have a new dynamic collection, I should be able to write new code. I should be able to create a class to add that functionality, and it should just work. I shouldn't have to modify this code every single time I return to it. So if you're ready and excited, we'll get started in the next episode.
I shouldn't have to modify this code every single time I return to it. So if you're ready and excited, we'll get started in the next episode.
