Presenter Pattern Idea0:00
Another option you have when refactoring a messy model is to use what we might call a Vue.Presenter or a Vue.Model, like this. I'm going to go to my User class. This is a very common place where you would want this kind of functionality. And if you ever find yourself trying to create a method that is specifically for the Vue, so basically you just don't want to repeat the same logic over and over, so instead you move it into a method on your model, and then you reference it there to dry things up a bit. Yeah, that's an option. So here's the most common example, is something like this,
So maybe you have another one for like a welcome message, and this basically figures out their username when they signed up, and it gives them maybe a nice little message in the nav bar. If you wanted to put that in your model, you might do something like this. Let's do sprintf. And how about welcome, person'sName, you signed up however long ago. So now, yeah, let's reference the person'sName. That'll be for the first argument. And the second one, well, I could do this createdAt. You already know that will be a Carbon instance,
And the second one, well, I could do this createdAt. You already know that will be a Carbon instance, which means I could say diffForHumans, and that'll translate to something like 20 minutes ago or something like that. Yeah, putting this all in the Vue, especially if it has to be repeated more than once, not ideal. Instead, we could extract it to a presenter method. So this is great. But yeah, now imagine you've added a couple more and a couple more, and you don't want that in your User class.
Designing Presenter API2:15
But yeah, now imagine you've added a couple more and a couple more, and you don't want that in your User class. You want to clean things up. All right, one option is, yeah, create a dedicated class for the Vue. And this is why some call it a Vue model or a Vue presenter. Let's figure out how we would want to interact with it. So let's just whip up a quick User here. I'm just going to use a factory. That'll give me a dummy User in the database. And then I'm going to pass that to the welcome view.
That'll give me a dummy User in the database. And then I'm going to pass that to the welcome view. Okay, let's go over there. Let's get rid of some of this stuff right here. Yeah, all right. So we could say user. And currently, right now, because it's on the model, you could just do something like this. But as we've discussed, we would like a different option. We don't want the model to be responsible for this. So instead, what do we do?
We don't want the model to be responsible for this. So instead, what do we do? We write out the API. We write out the workflow we would most like. So maybe something like this, User, present. And then you pass that in as a string. That could be cool. Or maybe this present method will return to us a different class, like a UserPresenter class. So maybe you could do something like User, present, welcomeMessage. Why don't we go with that one for this lesson?
So maybe you could do something like User, present, welcome message. Why don't we go with that one for this lesson? Well, first things first, we need a present method. So I will create that. And notice, by the way, that I'm not immediately looking for some package to pull in. That adds some extra complexity. That adds more for you to learn, at the very least. You have to figure out how this thing works. When in reality, it might just turn out that one or maybe two models could use a Presenter class. In which case, keep it simple, stupid.
Building UserPresenter Class3:51
When in reality, it might just turn out that one or maybe two models could use a presenter class. In which case, keep it simple, stupid. OK, so in this case, I'm just going to new up a UserPresenter class right here. And then I'm going to pass a reference to the User itself into the constructor. Nice and simple. OK, let's create it. UserPresenter.php. Let's set this up. Namespace app. And our class.
Namespace app. And our class. Now I can go back to my User model, take any of those relevant view-specific methods, and we'll move those into our UserPresenter, like so. But now what else? Well, we're trying to reference properties on the User model. But obviously, those aren't available. Now I'll show you how to sneak that in. But for now, well, don't forget, we're passing through the User model into the constructor. So at the very least, let's accept that, like so.
But for now, well, don't forget, we're passing through the User model into the constructor. So at the very least, let's accept that, like so. Now, if only temporarily, I could say $userName and $userCreatedAt. But now here's the next thing. Within my welcome view, I still want to access these as properties. So for example, yes, this would totally work, and it's fine. But we kind of liked that system with Laravel and Eloquent where we could say $userName, and you're not calling it as a method. OK, well, if we wanted to allow for that, why don't we do something like this? Let's add a magic method here, something like this.
Magic Property Access5:17
OK, well, if we wanted to allow for that, why don't we do something like this? Let's add a magic method here, something like this. So if you try to call a property that does not exist on the class, then this magic method will automatically be triggered. Then I could just say, well, check to see if a method exists with that property name. So check this object for a method with that name. And if you found one, call it. Call user_func on this object called property. All right, does that make sense? So if you say user present welcomeMessage, well, that property doesn't exist.
All right, does that make sense? So if you say user present welcomeMessage, well, that property doesn't exist. So we then check to see, is there a welcomeMessage method on this object? Yes, there is. So let's call it and return the result. It's kind of a shortcut there just to make the API that much more familiar or enjoyable to use. All right, so take a look. On our User model, we've taken the simplest possible approach, which I really like. Add a method and new up a new class.
On our User model, we've taken the simplest possible approach, which I really like. Add a method and new up a new class. So now in our welcome page, we present our welcome message, and this should be good. Let's try it out in the browser. OK, so if we load this, sure enough, we used the factory builder to create a dummy User in the database. And we can see, welcome, Donald, you signed up one second ago. So it worked. But now, what if you try to reference something that doesn't exist? Well, right now, it literally does nothing.
But now, what if you try to reference something that doesn't exist? Well, right now, it literally does nothing. And that's because, once again, on our UserPresenter, this magic method was called. There was no method with that name, so we literally did nothing. Let's fix that. Maybe we'll give some feedback or we will throw an Exception. Maybe something like this. A message of the class name does not respond to the property property. And let's also say or method, just to make it clear. Looks good.
And let's also say method, just to make it clear. Looks good. So now at the bottom, we're going to throw a new Exception. And then we'll pass this to sprintf once again. The message and the values will be the class name, as well as the property in question. Look good? Come back. Refresh. And now we have a little more feedback, which is useful. So this is all great.
Refactor to AbstractPresenter7:34
And now we have a little more feedback, which is useful. So this is all great. But one thing is, my UserPresenter class is getting a bit more bulky than I would like. It's not as enjoyable to create it. So if I have another thing, like a PostPresenter, now I have to deal with that stuff again. No way. Instead, let's refactor this and figure out, almost like we were writing documentation. In a perfect world, what would it feel like to create a UserPresenter class? Well, as I see it, you shouldn't have to inject the User. You'll almost always do that.
Well, as I see it, you shouldn't have to inject the User. You'll almost always do that. So we will make that the default. Or for the situations where you need to override the constructor, you can do that. OK. Next, this setup here. Yeah, that belongs up a level. It doesn't need to be here. So this is my perfect scenario. You create a UserPresenter class.
So this is my perfect scenario. You create a UserPresenter class. You have it extend some kind of abstract class. And then you create all of your presenter methods. And you're done. There's nothing else for you to do. OK, well, let's make that work. I'm going to undo a couple of steps to bring it back as we had it before. And now I will create a AbstractPresenter class. Namespace app.
And now I will create an abstract presenter class. Namespace app. Abstract, because you should never instantiate this class on its own. You will always instantiate a subclass. Abstract class Presenter. Abstract class Presenter. OK, so now, yeah, we're going to take all of this stuff, all of this shared logic, and we're going to put it on the parent. And then the same thing down here. This magic method.
And then the same thing down here. This magic method. OK, great. So now if we go back to UserPresenter, we have our perfect scenario. And now everything should work just like it did before. So we come back to Chrome. Give it a refresh. Same error message. UserPresenter does not respond to this property. And actually, on that note, why don't we wrap that within quotes?
User presenter does not respond to this property. And actually, on that note, why don't we wrap that within quotes? Yeah. OK, now we can go back, do the welcome message, refresh the page, and it all works exactly like we would want. But now that logic is out of the User. Now, in terms of taking this a bit further, honestly, to reiterate, keep it as simple as you can. So from my experiences, my need for something like a Presenter class is usually two different things.
When to Use Presenters9:54
So from my experiences, my need for something like a Presenter class is usually two different things. And we've already talked about this. It's the two things that are at risk of becoming God objects. So it will be your User class will often need a Presenter. And then also the main model for the project you're working on. So like for Laracasts, it would be the Video class. That might be useful. Other than that, I'm not necessarily using these presenters all over the place. So I don't see a huge need to create a package or add more complexity.
Other than that, I'm not necessarily using these presenters all over the place. So I don't see a huge need to create a package or add more complexity. But what you could do if you wanted to is this could then become maybe a trait. And then at the top, you could say, use Presentable. Yeah, that would be an option as well. And because we have the late static binding. So for example, like down here, we get to use static class. That will always refer to the model in question. So yeah, that's an option. Why don't you play around with that if you want some extra credit?
So yeah, that's an option. Why don't you play around with that if you want some extra credit? But otherwise, I'm perfectly fine keeping it as simple as this. Present the User model. Looks great.
