تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Adding Email Confirmation0:00

Hey, hey, welcome back. Between the last lesson and this lesson, I added one small feature, which is sending the User an email confirming their order. First, I added this to our test. First, obviously, I faked the Mail facade, then I added an assertion to make sure that we're sending the User an order received Mailable. On the code side of things, I had to pass the user email to this PurchaseItems class, and I also had to actually send the email to the User. Very common feature in any e-commerce app. Now, this isn't a big deal, but as you might have noticed, first of all, this method is

Explaining Temporal Coupling0:37

Very common feature in any e-commerce app. Now, this isn't a big deal, but as you might have noticed, first of all, this method is growing rather big. We all needed a userId, now we also need a userEmail, and the problem is, let's say you want to add a new feature where if a user happens to purchase something above a given price, for example, if they spend more than $2,000, you want to give them a price. So you would do something like, if the orderTotal in cents is greater than $2,000, which is 100,000 cents, you want to execute them. We call this temporal coupling. It happens when for action B to execute it, you need action A to execute first.

We call this temporal coupling. It happens when for action B to execute, you need action A to execute first. For example, for us to send an email to the User with their order details, we need the Order to be created first. For us to give them a price based on how much they spend, we also need the Order to be created first. For us to decrease the stock of a Product, we need to have the Cart items first as well. So as you can imagine, this will very quickly turn into some spaghetti code. A unique way to solve this is with events. You see, we have the core action here, which is creating the Order, and we can dispatch

Dispatching OrderFulfilled Event1:48

A unique way to solve this is with events. You see, we have the core action here, which is creating the order, and we can dispatch an event letting the system know that an order was created, and then the system can react to it in any way it wants, be it sending an email, be it giving the user a price, whatever new functionality you need to add, you just need to react to an event, that is, listen to an event. So let's see how we could do this in this situation. First, I'm going to get rid of this code, and if we run a task, it is going to fail because we are not sending the email, and now I'm going to inject a new dependency to this class.

because we are not sending the email, and now I'm going to inject a new dependency to this class. It's called dispatcher, and this is the event dispatcher. We want the contract, so let's call this events, and then we can dispatch an event just before returning the order. We can say, hey, we want to dispatch an event. Let's call it OrderFulfilled. We need to create this class. Let's put it inside events, and we need to pass it some information. For example, we need to know the orderId, that's important.

Let's put it inside events, and we need to pass it some information. For example, we need to know the orderId, that's important. We need to know the userEmail, and we also need to know the userId in case we want to do some operation with them. For now, this should be enough. Let's go and write this. So we need the orderId, which we have. We need the userId, which we also have, and we need the userEmail, which we also have. All right, we're dispatching an event. If we run a task, it is still going to fail, and we actually got a second error, an expected

All right, we're dispatching an event. If we run a task, it is still going to fail, and we actually got a second error, an expected token return. Oh, we missed a semicolon here. All right, so we only have one task failing now, and the problem is we're not really reacting to this event. So within the Order module, which is where I think this should react to it, inside providers, we're going to go into our OrderServiceProvider. We're going to register a new one. This time, we're going to register an event ServiceProvider.

Creating Event Listener3:53

We're going to register a new one. This time, we're going to register an EventServiceProvider. We need to create this class. We want to create this. No, I don't want to import it. I want to create it. We need it to extend the base EventServiceProvider, and it can replace this with an alias. We can say base EventServiceProvider. Now we have a proper listen here, and we can listen to events.

We can say EventServiceProvider. Now we have a proper listen here, and we can listen to events. The event we want to listen to is OrderFulfilled, right? And we want to add a listener. For example, let's say SendOrderConfirmationEmail. We can create this class. Again, it's a very simple class. Let's create it under events, and it's going to have a handle method where it receives the event like this. If we were to jump inside this, let's run a test.

the event like this. If we were to jump inside this, let's run a test. We can see that the code is stopping right here. So we're reaching this part of the execution. This can return void. Now, a cool thing with listeners is you can also queue them. I'm not going to do this now, but you can. Let's close this. We're already reacting to this event, so let's focus on this piece. Next we have all the information we need within this class, which really, if you think

We're already reacting to this event, so let's focus on this piece. Next we have all the information we need within this class, which really, if you think about it, this is a plain old php object, but this is pretty much a data transfer object. We're just transferring data between layers and giving the listeners some data as well. We can just do what we were doing. So we want to send an email to the User, order confirmation email, and this is where things get a little bit tricky. We don't have the necessary information here. We want to know how much they spent to add to the email. We want the localized amount.

We want to know how much they spent to add to the email. We want the localized amount. So for now, let's add a very dirty solution, which is let's just add the orderTotal right here. So let's say something like totalInCents and then localizedTotal. Now we can use that and pass this to the mailable. Oops. This is not actually what we want to send. The name of the mail is OrderReceived. My bad.

The name of the mail is OrderReceived. My bad. I was passing to the listener. OrderReceived. There we go. If we rerun this, it is going to fail because we're not passing enough arguments. So let's go back to our purchaseItems and pass total in cents and also pass the localizedTotal. All right, let's rerun the task and now they're passing again. Now that we have this event being dispatched, we can also remove this piece of code from

Cross-Module Event Handling6:31

All right, let's rerun the task and now they're passing again. Now that we have this event being dispatched, we can also remove this piece of code from this particular class. We can react to it on an event listener. So let's get rid of this. Now, the interesting thing, though, is although we are on the order module and we're dispatching this event, which belongs to the order module, the event is just really a data transfer object and we can listen to that event on another module, again, cross boundary communication. So let's listen to this event on the product module. First, we're going to also create an event service provider on the product module.

So let's listen to this event on the Product module. First, we're going to also create an EventServiceProvider on the Product module. We want to extend the base EventServiceProvider. Let's replace it with an alias called base EventServiceProvider. Again, we want to listen to the OrderFulfilled event. Let's import this class and then we want to react with something like decreaseProductStock. Let's create this class. We can put it under Product Events. This class is going to listen to the event on the handle method and now we can do whatever.

We can put it under product events. This class is going to listen to the event on the handle method and now we can do whatever we need to. Let's space what we had. So the first thing is we need to have access to the ProductStockManager dependency. Let's go on the constructor and add this here. Okay, we have access to it. Now we also need to register this fresh created EventServiceProvider. Let's go to ProductServiceProvider and also register it. This runs on the Product module, just like we did on the Order module.

Let's go to ProductServiceProvider and also register it. This runs on the product module, just like we did on the order module. So we register the EventServiceProvider. We're listening to the event. We need to do something with it. If we run the test now, it is going to fail because of this, but let's run it. Obviously failing, this variable does not exist. So let's see what we can do. If I look at the event, we don't have any property that would allow us to have access to the orderLines.

If I look at the event, we don't have any property that would allow us to have access to the order lines. So let's go with a quick and dirty solution. First, let's make this class read-only, shall we? Then let's add a property called, we're going to expect a correct item collection and add it to a correct items property. Let's import this class. There we go. Very dirty, but that's what we need right now. Let's access the property within the event and let's format this a little bit.

Very dirty, but that's what we need right now. Let's access the property within the event and let's format this a little bit. If we run this, it is going to fail because we're not passing this argument to the event. So let's go back here and pass that argument. There we go. And if we rerun the test, now they're passing as well. Perfect. So now we've extracted both the email sending as well as the stock measurement to events. If we were to go back to our controller task, we could also remove the assertions related to both the mailable as well as the stock, and we could just assert that an event was

If we were to go back to our controller task, we could also remove the assertions related to both the mailable as well as the stock, and we could just assert that an event was dispatched. Let me show you. Let's comment this out. Let's comment this out. And we could just fake the event buzz. And then after we do the action, we could just assert that a certain event was dispatched, for example, OrderFulfilled like this. Now if we run a test, they're passing.

for example, order fulfilled like this. Now if we run a test, they're passing. If we were to go here and comment this, it now fails. However, on this lesson, we're not going to do this because it would require us to write tasks for each one of the listeners. We're going to do that later, not now. For now, let's keep it as it was. Let's rerun a test, they're passing, and now we're done with this very lengthy lesson. All right. You might have noticed that we have a small problem.

Addressing Bloated Event Data10:23

All right. You might have noticed that we have a small problem. If we go into this class, we can see that the order fulfilled event is quite bloated, and this method as well is very bloated. And the problem is whenever we need to add more information to do something, for example, remember the price example, we might need some more information. We would need to add a new property to this event and probably also add a new argument to the handle method of this class. This does not look good, and this does not scale well. One solution is encapsulating data in data transfer objects, and this is what we're going

This does not look good, and this does not scale well. One solution is encapsulating data in data transfer objects, and this is what we're going to take a look in the next lesson. All right. This is it for now, and I'll see you in the next lesson. Bye-bye.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟