Why do We need View Presenters?
View should be dumb, they shouldn’t do much other than showing data to the user. So where’s the correct place to place your view specific logic in Laravel. Like you need the gravatar url of the user to see if the user already has a gravatar set to save the user from uplaoding a new one in your site. Or maybe you save user name as two field in db, as first_name
and last_name
, now you need to create user’s full name, but you also want apply the function ucfirst
first on them. Or maybe you have users profile pic’s filename saved in database, now you need to create profile pic url from that by concatenating the upload directory.
Where should we put this sorts of logic. Eloquent Model? This can seem a very tempting choice, but why should the Model handle View specific logics. Then where is the place to put this kind of things?
Well maybe in their own class whose only responsibility will be to do all the logic needed in view using a specific model.
In laravel, there’s ins’t any built in way to do 1-to-1 Model-Presenter mapping out of the box but it’s incredibly easy to set up one.
So what do we need
- define a present mentod or any method in every Eloquent model which will return a presenter instance for that model and if you use anything like
$model->present()->gravatar_url()
or$model->present->gravatar_url
both will actually call a function namedgravatar_url
on thePresenter
class. - make it easy for the view to use it, like view shouldn’t be requried to know where that url is coming from.
- and finally maybe hide away some common Presenter functionality into an abstract class so every presenter class wont have to implement those
Implementing the present() method in every Eloquent Model
The responsiblity of the present()
method will be to
- first somehow dertermine the FQN(Fully Qualified Name) of the Presenter class so we can instantiate them
- After we get that class name, we check if that class exists, and if they doesnt exist they you can either throw an exception( which is better ) or fail silently if you want. And if it’s there then just instantiate it and return the instance.
How to determine the FQN of the Presenter Class
There are several ways we can do that, it entirely depends on how we want to desing our app. We can find the Current Model’s class name append Presenter
in their name and search in the same directory or in a separate Presenters
directory which is on a different namespace to find the presenter class.
But it’s better not to hard code that logic inside a function and make it more flexible. Like we can look for a $presenter
variable in the Model where we will save the Presenters full path so our funciton can use it. That way we can save the Presenter anywhere we want.
Creating a Presentable Trait
The functions of the present()
method seems to be same for every models, so we could just use a trait for it and use it in every model. Lets see a reference implementation of the trait,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
The functionality of the trait is fairly straight forward. We have a variable called $persenterInterface
to save the Presenter Instance
for further use. I have added comments on the code to make it easier to understand.
Creating a base Presenter class
We can ignore this if we want, just adding the Presenter Class
is enough for the functionality. But lets add some sytactic sugar using the __get()
magic method so when you do something like $user->present()->gravatar_url
it actually calls the method with that name. Though gravatar_url
is actually a method on the presenter we will be able to use it like a property.
Lets check the following abstract Presenter class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
In the above trait we actually passed the model instance to the presenter while instantiating that. And hence we have received that in the constructor of the base class.
Usage
Lets see how can we use this in a project with an Eloquent Model.
Lets do it for the User
model. First we will define the Presenter
class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
We see in the user Presenter we used $this->email
, $this->first_name
and $this->last_name
. We are being able to do that due the magic getter method we defined which automatically returns the property on the associated model if a function with that name doesn’t exist in the Presenter
itself.
Now lets create an User
model.
1 2 3 4 5 6 7 8 9 10 11 |
|
And we are done. We can now use it like,
1 2 3 4 5 6 7 8 |
|
You can use those methods anywhere on any model
instance which has the Presenter
implemented. Be it inside your controller or in your bade templates.
So now you have a clean way to place all your presentation specific logic. As they are in a single class it’s easier to maintain and you can refactor any method’s body anytime without worrying about breaking anything on other parts of your application.
Hope you enjoyed it.
N. B. This post is inspired by Jeffrey Way’s View presenter related lessons on Laracasts.