Create Listener Class0:00
Here's the EventListener that we constructed in the last episode. I like it. But at the moment, we still have it within our routes file. All my other routes are down here out of sight. So I'd like to get this cleaned up and move to its own EventListener class. So that'll be our first step. php artisan make:listener. And what's the name here? Well, we know that the event name is UserEarnedExperience. And generally, when naming listeners, I like to follow a when-then approach.
Well, we know that the event name is UserEarnedExperience. And generally, when naming listeners, I like to follow a when-then approach. So I might say to myself, when a UserEarnedExperience, then what do I do? And it sounds like I need a name that describes what this is. Well, let's just walk through it together. We grab the user's achievements, and we need to sync up a pivot table. So we're syncing it by grabbing all achievements in the system. We filter it down to only those achievements that the user qualifies for. And then we grab the ModelEye key for each of those achievements. And that is what we sync up.
And then we grab the ModelEye key for each of those achievements. And that is what we sync up. So yeah, we're syncing a User's achievements, or basically, we are awarding achievements to the User. Why don't we call it AwardAchievements? Or SyncAchievement, you know, whatever you want. We can change it if we need to. Okay, so if we switch over there, this is what we get. Now, if we scroll down, we can see our event that we're responding to. So I have my main test suite for achievements running.
Now, if we scroll down, we can see our event that we're responding to. So I have my main test suite for achievements running. But of course, if I were to comment this out and run it, it all fails. Okay, let's try to get back to green, and we'll do it one step at a time. Event, listen for, once again, when a User earned experience, and then we will award achievements. Okay, if I run it again, it's still going to fail because we're not doing anything yet. So let's grab that handler code and paste it in and uncomment. All right, let's give it another run, and we're back to green. All right, so that was a decent refactor.
Discuss Project Structure Options1:52
All right, let's give it another run, and we're back to green. All right, so that was a decent refactor. And now we at least have a dedicated event listener class for awarding achievements. But now I'd like to get this out of the routes file as well. And that brings us to the main topic for this lesson, how we structure things. So take a look at this. In our AwardAchievements class, and we can just clean that up, refactor or reformat, that looks good. Anyways, at the moment, it's stored within a listeners directory. And the AchievementServiceProvider is stored within the app/Providers directory.
Anyways, at the moment, it's stored within a listeners directory. And the AchievementServiceProvider is stored within the app/Providers directory. And then each of the individual achievements is stored within, well, an achievements directory. So we need to think about how we want to structure things in a Laravel project. And there's kind of two different approaches. Neither one is preferable to the other. It just depends on how large your project is and how you want to structure things. So for example, you know, even for something like Laravel tasks, often I'm just throwing my providers in a providers directory, my events in the events directory. And Laravel tasks is a decent size code base, maybe 30,000 lines of code, something like
my providers in a providers directory, my events in the events directory. And LayerTasks is a decent size code base, maybe 30,000 lines of code, something like that, 20 or 30. But another approach, equally valid, is to divide things up according to the component. So in this case, we have this Achievements component, if you think about it. And as part of that, we have a service provider, an event, a listener, an Eloquent model, these individual Achievement classes, and the abstract AchievementType class there. So another equally valid approach would be to throw everything within that achievements directory. And later, if you ever do decide to extract this to a reusable package, I have no plans.
directory. And later, if you ever do decide to extract this to a reusable package, I have no plans to do that. But if you did, it's really easy because everything's already stored within the same directory. You distribute it and you're all set to go. So these are things to think about. Now I don't really have any issues with, for example, the provider for the achievements being stored within the providers directory. But just to illustrate it, let's see what it might look like if we were to instead move that to my achievements directory.
Move Provider Into Component3:47
But just to illustrate it, let's see what it might look like if we were to instead move that to my achievements directory. Now if you want to be super organized, you could even reproduce these subdirectories. So within the achievements folder, you might have providers, you might have listeners. If you have a really large component or package that you're working on, that's a good idea. Otherwise, I often just throw everything into that directory. Okay. So now this is stored within the achievements directory along with the achievements themselves. Okay. If I give our tests a run, of course, everything's going to blow up because we need to update
Okay. If I give our tests a run, of course, everything's going to blow up because we need to update some namespaces. So let's do that together. And now we can see we no longer need to import these because they're within the same directory, which is great. So I can get rid of that. Okay. Next, within my config/app directory, I need to update this namespace. So let's just do it again.
Move Listener and Event4:34
Next, within my config/app directory, I need to update this namespace. So let's just do it again. All right. Let's run our tests again, and we're back to green. Great. If we take that approach now, we could do the same thing with the listeners. So let's move this once again to my achievements directory and run that. And then I'll update this one as well. Okay. So now if I run the test again, it blows up because we have to update the path here.
Okay. So now if I run the test again, it blows up because we have to update the path here. So now we're going into the achievements directory, run it, and we're back to green. All right. Well, that begs the question. When a User earns experience, maybe that too should be within the achievements directory. I could go either way on this one, but yeah, let's go all in. So we're going to move that once again to achievements, update the namespace, and then once again switch back here. Give our tests a run.
once again switch back here. Give our tests a run. How are we doing? Hmm. It looks like in the Experience class and the test, we have to fix that. All right. So Experience needs an update, run it, and then our listeners handle method needs to be updated right here. And again, as soon as we fix that, it's superfluous because this file is now within the same namespace. So I'll get rid of that, run it again, and it looks like we are back to green.
Move Model, Fix Namespaces5:51
And again, as soon as we fix that, it's superfluous because this file is now within the same namespace. So I'll get rid of that, run it again, and it looks like we are back to green. One more. What about the Eloquent model itself? It's currently within the app directory. All right. We're all in here. So let's open this to the achievements directory. I'll update that. Okay.
I'll update that. Okay. Let's run our tests. Everything's still failing at achievements test. Okay. Right here, run it again. We have to update our model factory. So achievements factory. Now this one, I would not move to the achievements folder. I'm going to keep that within the factories directory.
Now this one, I would not move to the achievements folder. I'm going to keep that within the factories directory. Run it again. And we changed the error, which is good, but it looks like there's a conflict on the awardTo method. Yeah, we just have to now import User. Run it again. Achievement not found. So yeah, you can see there's a number of little things we have to update. What did we miss here?
So yeah, you can see there's a number of little things we have to update. What did we miss here? Hmm. That's all right. The end user. Yeah. Let's update that. Run it again. And it changed again. AchievementType.
And it changed again. Achievement type. We're almost there. Don't worry. All right. So yeah. Here, that can now be removed. There we go. Now we're back to green. And it took a while, but at this point, if we go back to AchievementsServiceProvider,
Register Listener in Provider7:21
Now we're back to green. And it took a while, but at this point, if we go back to AchievementsServiceProvider, take a look at that directory. This is what we now have. The Eloquent model, the service provider, the achievements themselves, which we might even want to put within a nested directory. That might be a good idea, as well as the event and the event listener. Anyways, if we now go back to our routes file, we have our event listener. So if I pluck that out, run our test again, everything's going to fail. And instead, we can just throw it into the service provider.
So if I pluck that out, run our test again, everything's going to fail. And instead, we can just throw it into the service provider. So let's do it within our boot method. So yeah, we could start with that. Run our test again. See if it's picking up. Yes, it is. However, at this point, once again, we're within the layer cast achievements directory already, which means these could be shortened. Like so.
