Defining Subject Interface2:02
But let's continue on with this. So what kind of interface would a Subject have? Well, if we think we could have some kind of attach method, so we would need to attach an observer. Next, we'd need some way to detach an observer, so maybe something like detach. And then finally, we would need either something like a notify method, and that would be responsible for actually triggering any observers that have been registered. Or other people don't like the object to be responsible for that. Instead, they would want some kind of release method that would simply clean out your array of observers, and then return that to the client.
Instead, they would want some kind of release method that would simply clean out your array of observers, and then return that to the client. And that way, the client can then be responsible for dispatching them however he or she wants to. Let's stick with this notify. This is sort of the classical and traditional example, so we will keep that in. Now what about an observer? Think of the observer as the subscriber or the listener. Well, the observer does need to be triggered. So maybe the method would be anything from handle, or classically they would call it
Defining Observer Interface2:58
Well, the observer does need to be triggered. So maybe the method would be anything from handle, or classically they would call it update. Any of these are fine. Let's stick with handle. All right, so that's good for a first stab at our two interfaces. So now, let's create a couple concrete classes to demonstrate how this might work. So imagine that when a User logs into your application, you need to respond in a number of ways. Maybe in special circumstances, an email should be fired off.
of ways. Maybe in special circumstances, an email should be fired off. Maybe you need to perform some logging. Maybe there's X number of things that need to happen. So let's say we have class Off. Now if we want this Off class to be a Subject, then it needs to implement that contract, like so. But on that note, notice how that's a little vague. Class Off implements Subject. File that away just for now, and then we will return to it.
Implementing Concrete Subject3:48
Class off implement subject. File that away just for now, and then we will return to it. Anyhow, if we add these method stubs, if we want to notify observers, then we need to offer these three methods. And here's what that might look like. You might have an array of observers. And then each time somebody calls an attach method, you will append to that array. Notice observers, add a new one, and make it equal to what we passed in. Next, we come to the detach method. And this is going to be very, very basic.
Next, we come to the detach method. And this is going to be very, very basic. Because we aren't associating any kind of identifier or a key, well, this becomes a little more difficult. So to keep it very simple, and then later we're going to delete everything you see here. But just as an example, maybe you just give it the index of the observers. I know a little basic, but just go along with me. So then we would say, unset this observers, and then pass in the index. And we'll call it a day on that one. Finally, for the notify method, we need to filter through all of the observers and trigger.
And we'll call it a day on that one. Finally, for the notify method, we need to filter through all of the observers and trigger them, so to speak. So you might say something like, for each $observers as $observer. Then we could call $observer and then the handle method that we defined. So on that note, what assurance do we have that a handle method will exist? Well, right now, none. We could even pass in a string here. So instead, why don't we type into this. We are going to say, yes, we can attach an observer.
So instead, why don't we type into this. We are going to say, yes, we can attach an observer. However, it does need to implement the Observer interface. And that way, we can be sure that when we do filter through these, the classes will offer a method called handle. But just remember, we will need to update our interface. All right. So like I said, this is very, very rudimentary, but hopefully gets the idea across. So maybe rather than auth, we could call this login or something. It's specifically responsible for logging you in.
Creating and Attaching Observers5:36
So maybe rather than auth, we could call this login or something. It's specifically responsible for logging you in. So maybe you would have something like this, new login. And now we want to attach some listeners or some subscribers. So in that case, we could say attach. And now what do we need to do when a User logs in? Well, maybe we have some kind of LogHandler. So with that in mind, let's create that class LogHandler. And now we will have it implement this interface, implements Observer. So now we will add the method stubs, and we will simulate this with a var_dump, log something.
And now we will have it implement this interface, implements Observer. So now we will add the method stubs, and we will simulate this with a var_dump, log something important. Okay. So we have this listener or this handler. I'm going to attach it now. Attach new log handler. So at this point, the observers array is equal to that one class instance. But maybe we have another one. Maybe like I said, in some situations, an email needs to be fired off to an administrator.
But maybe we have another one. Maybe like I said, in some situations, an email needs to be fired off to an administrator or something like that. So email notifier or handler or whatever convention you want. And this time we'll say fire off an email. Okay, now we have two. Let's go ahead and attach that one, email notifier. But now maybe you want to focus on the interface a little bit. For example, maybe you want to do something like that. Well in that case, just make sure that you return the instance.
Supporting Arrays and Validation6:57
For example, maybe you want to do something like that. Well in that case, just make sure that you return the instance. Or alternatively, maybe you want to pass an optional array. So something like this, new EmailNotifier. And now you want this style to be supported as well. Okay, well if we want to do that, we need to change things up. We can no longer expect a specific object. We just want some kind of observable object or an array of objects. So let's update this. And now we can do a quick check.
So let's update this. And now we can do a quick check. If what was passed in is an array, then in that case, we should just filter through that array and then call this method recursively. For each observable as observer, then I will call the attach method again and then pass through the observer and return. So I hope that makes sense. We're just saying if you gave us an array, then filter through that array and for each item, I want you to recall this method. At which point, this entire check will fail and we simply assign it like we did before.
item, I want you to recall this method. At which point, this entire check will fail and we simply assign it like we did before. So now that will once again work. However, we have lost that ability to enforce the implementation of the observer by type hinting. And while there are some RFCs in PHP to allow for that, right now there's no way to specify that we require an array of specific instances of a class. So to work around that, you could always do some kind of check. If not observer instance of Observer, then we could throw an exception. And now finally, we can just clean this up.
If not observer instance of observer, then we could throw an exception. And now finally, we can just clean this up. I can extract this to a method called attachObservers and then return that result there. And that cleans it up just a bit. And then finally, we just have to update our interface. So now, yes, you can pass in a single object here or you can pass in an array. So finally, when you call the actual method on your class that performs the login, maybe something like loginFire, well, then that method would be responsible for triggering this right here. So let's add this.
this right here. So let's add this. How about at the very bottom here? Method fire. And now we will perform the login and then respond to any potential registered listeners. This notify. All right. So we perform the login and then we trigger this method, which will filter through any observers that we might have. And for each one, we call a handle method.
And whoops, I need to update this. All right. So why don't we review this? I have a Laravel app here, but I just stored everything in a routes file. So that means if I boot up a server, I should just immediately hit that file. And we do. So notice we immediately echo out log something important, followed by fire off an email. So now if I require a new functionality in response to a user logging in, I can simply create a simple object, have it implement the interface, and then pass it in here. Maybe we need something for reporting.
create a simple object, have it implement the interface, and then pass it in here. Maybe we need something for reporting. So how about LoginReporter? And that's it. So I will just create a new class here for LoginReporter. And when we handle it, do some form of reporting. And that's it. We're done. And we've now extended the functionality without having to update the Login class at all. So if we run this again, now we're doing three things in response.
And we've now extended the functionality without having to update the login class at all. So if we run this again, now we're doing three things in response. Okay. So now we understand the basic idea of the observer pattern. A change in one object needs to have a nice, flexible, decoupled way to notify other objects of this change. And that way they, in turn, can respond however they need to. Or in much more simple terms, this gives us a way for objects to notify one another without being intrinsically linked. So next I want to switch over to Laravel.
Observer Pattern in Laravel11:08
being intrinsically linked. So next I want to switch over to Laravel. What does all of this stuff, the implementation of this pattern, look like in a Laravel application? And I think you're going to like it. So as a first step, let's just use the event facade. But there's also an interface that you could reference as well. So let's say we want to listen for a User to log in. How about something like user.login? Now when that happens, I want to execute some kind of function. I can either reference this as a closure here, or I could alternatively reference a path
Now when that happens, I want to execute some kind of function. I can either reference this as a closure here, or I could alternatively reference a path to a class. So now, once again, we're going to var_dump and say fire off an email, like we had before. Next, let's say we have another one, and this is for that reporting option. Do some reporting. All right, that's a good first step. Now let's set up our route. When we get the home page, we are going to simulate or assume that the user has logged in for the demo.
When we get the home page, we are going to simulate or assume that the user has logged in for the demo. So I will say event fire user.login. And that's it. So if you were to view that in the browser and we hit the home page, sure enough, we responded in two different ways to that single event being fired. But now let's switch this over to what it might look like in a controller. So we'll say router. When we get the home page, once again, let's just reference a HomeController here. All right, if I open up my controllers, there we go.
When we get the home page, once again, let's just reference a HomeController here. All right, if I open up my controllers, there we go. So let's get rid of all of this stuff here. And now I still want to do something like event fire. However, maybe I don't want to use facades in my controller. Well, instead, I can use method injection, which is new in Laravel 5, to reference a contract. Now, specifically, what I want is the event dispatcher contract. All right, so now I can simply change that to dispatcher fire user login. Or if you like a different naming convention, I like to do this sometimes, user has logged.
All right, so now I can simply change that to dispatcher fire user login. Or if you like a different naming convention, I like to do this sometimes, user has logged in. We make it in past tense, and we also write it out exactly the way we'd speak it, a user has logged in. And then, of course, you can pass through the related user. So the benefit to this is our controller is 100% decoupled. In fact, if you wanted to unit test this, it would be the easiest thing in the world. Simply mock the contract and make sure that the dispatcher collaborator does receive a call to the fire method.
Simply mock the contract and make sure that the dispatcher collaborator does receive a call to the fire method. But next, how do we go about listeners? Well, we've removed all of that listener code from our routes file, so right now, we don't have anything registered. All right, well, here's what we can do. I'm going to show you two different ways. First, within my app directory, I'm going to have a listeners folder, and let's do that first one, EmailNotifier. Now, when we create our handle method here, once again, I will say fire off an email.
first one, EmailNotifier. Now, when we create our handle method here, once again, I will say fire off an email. Okay, so our class is created, but how do we register the binding? Well, in Laravel 5, here's what we do. Within my providers folder, I will open up EventServiceProvider and specify that when we hear an event of User has logged in, then I'm going to list any number of responders, essentially, or subscribers. In this case, we have app\Listeners\EmailNotifier. Now, by default, it's going to look for a handle method, but if you want to change that, then you can use this syntax and then update your method accordingly.
Now, by default, it's going to look for a handle method, but if you want to change that, then you can use this syntax and then update your method accordingly. But this is fine. So now, if I switch back and refresh, now that's working, but this is a cleaner way to go about it. Whenever I need to respond in a different way to a user logging in, I simply create the class and then reference it right here. And that's it. We're done with that entire process. Or if you like to be on the cutting edge, let me show you something that you might not
We're done with that entire process. Or if you like to be on the cutting edge, let me show you something that you might not be familiar with. If I run php artisan and scroll up, you'll see a newer command called event:scan. So what this does is it allows us to use annotations to designate what our event listener should be. Let me show you what that looks like. Imagine within here, we didn't have any of this stuff. I will comment it out entirely. Now in EmailNotifier, I will say this method hears when a User has logged in.
I will comment it out entirely. Now in EmailNotifier, I will say this method hears when a User has logged in. Now, doing this alone is not enough. For example, if I switch back to the browser, no, we don't see anything. And that's because we haven't yet scanned all of these files to detect this annotation. So let's try it. php artisan event:scan. And now here's what that will do. If I open up storage/framework, you'll find this new file here that literally has the correct syntax to register the listener.
If I open up storage, framework, you'll find this new file here that literally has the correct syntax to register the listener. And behind the scenes, Laravel knows to check to see if this file exists. And if it does, it imports it. So that means if we come back and refresh, that works. So once again, we see that in Laravel, it literally couldn't be simpler. Let's try another one. Let's do that login reporter thing. login reporter. And now within our handle method, we'll just say report something important.
Login reporter. And now within our handle method, we'll just say report something important. Okay, so we've created this class, and I want to specify that it should respond to or listen for or hear when a User logs in. So we add the annotation. Here's User has logged in. And now this file has changed, so I need to rerun php artisan event:scan. Or alternatively, you could use a file watcher, something like a gulp task that will detect when these files are changed and then immediately run the command. That way, you'd have the benefit of simply making a change and resting assured that your
