cult3

Model Presenters in Laravel 4

Mar 03, 2014

Table of contents:

  1. What is a Model Presenter?
  2. Creating the Presenter structure
  3. Using ArrayAccess
  4. Injecting the object
  5. Get or pass through
  6. Creating an interface
  7. Creating your model presenters
  8. Creating the Presenter service
  9. Using the Presenter Service in your Controller
  10. Conclusion

When working with data in a web application, it is often desirable to display data differently from how it is stored in your database. Two common scenarios I encounter in just about every project I work on is displaying dates and money values. Generally speaking you will want to store dates as a timestamp, and money as integers, but that is not how you want to display them on the user interface.

However knowing where to deal with this issue isn’t really that obvious, particularly when you are just starting out as a developer.

At first you probably deal with it in the View, but that means you will repeat the same logic whenever you need to display the same field on a different page. Abstracting the logic to the model is one option, but your model shouldn’t care how the data is presented in the View.

Instead of adding extra responsibility to your Models, Views or Controllers, you should slip in an extra layer to deal with presenting the data of your application. This keeps all of your presentation logic in one place and it ensures you keep to the single responsibility principle.

In this tutorial I’m going to look at dealing with Model Presenters in Laravel 4.

What is a Model Presenter?

Before I jump into the code, first a little explanation as to what the hell I’m talking about.

A Model Presenter is basically a class that accepts an object from the database and then wraps it in some specific logic to alter the returned values without having to modify the original object.

If you’ve been following along with this series, you might remember that this process sounds oddly familiar to how we implemented caching as a service.

This process is known as the The Decorator Pattern. It is where you are able to modify an instance of an object without affecting any other instances of the same object.

This would be in contrast to extending an abstract class that held the presentation logic. If you extended an abstract class, then all instances of the object would be affected where we don’t necessarily want that side effect.

Creating the Presenter structure

So the first thing to do is to establish a little bit of structure. If you’ve been following along with this series you will know that this means creating a new namespace to hold these specific classes.

In this tutorial I’m going to create a new directory under my Cribbb namespace called Presenters.

Next I will create an AbstractPresenter to hold all of basic logic of all of the specific presenter instances:

<?php namespace Cribbb\Presenters;

abstract class AbstractPresenter
{
}

Using ArrayAccess

The first thing I’m going to do is implement the ArrayAccess interface:

<?php namespace Cribbb\Presenters;

use ArrayAccess;

abstract class AbstractPresenter implements ArrayAccess
{
}

This allows you to use an object as if it was an array for setting, unsetting and getting data from it. This basically means you can get and set values on this object in the same way that you do with an array. This is often useful when using the object in the View.

It’s important to note that implementing the ArrayAccess interface does not magically turn an object into an array. The interface simply forces you to implement the methods. The benefit is you can inspect the object and determine if you can access it like an array or not.

The ArrayAccess interface specifies that we need to implement the following four methods:

offsetExists

The offsetExists method allows to check to see if a property is set on the project using the isset function.

/**
 * Check to see if the offset exists
 * on the current object
 *
 * @param string $key
 * @return boolean
 */
public function offsetExists($key)
{
    return isset($this->object[$key]);
}

// Example
isset($object['name']); // true / false

offsetGet

The offsetGet method allows you to retrieve a key from the object using the square bracket syntax:

/**
 * Retrieve the key from the object
 * as if it were an array
 *
 * @param string $key
 * @return boolean
 */
public function offsetGet($key)
{
    return $this->object[$key];
}

// Example
echo $object['name']; // Philip Brown

offsetSet

The offsetSet method allows you to set a property on an object using the square bracket syntax:

/**
 * Set a property on the object
 * as if it were any array
 *
 * @param string $key
 * @param mixed $value
 */
public function offsetSet($key, $value)
{
    $this->object[$key] = $value;
}

// Example
$object['name'] = 'Philip Brown';

offsetUnset

And finally offsetUnset allows you to unset a property on the object using the unset method that you would normally use for arrays:

/**
 * Unset a key on the object
 * as if it were an array
 *
 * @param string $key
 */
public function offsetUnset($key)
{
    unset($this->object[$key]);
}

// Example
unset($object['name']);

Injecting the object

Now that we’ve set up the boilerplate ArrayAccess we can get down to actually creating the Presenter functionality using a couple of PHP’s Magic methods.

So the first thing to do so it to inject the object we want to work with. Normally you would do this through the constructor, but in this case I’m going to create a set method instead. The reasoning behind this decision will become clear later in this tutorial.

/**
 * The object to present
 *
 * @var mixed
 */
protected $object;

/**
 * Inject the object to be presented
 *
 * @param mixed
 */
public function set($object)
{
    $this->object = $object;
}

This is essentially exactly the same as doing it through the constructor however it gives us the flexibility to not supply the dependency when the object is instantiated.

Get or pass through

The next method is where the magic really happens. On each Presenter class I’ll be making a method that corresponds with a particular property on the object.

So for example, if I wanted to transform the created_at property, I would make a created_at method on my Presenter.

When I call $object->created_at, first I want to check to see if there is a presenter method available. If the method is available I will use that method. If it is not available I can simply pass the call to the original object.

To listen for property accessor calls we can use the __get() magic method:

/**
 * Check to see if there is a presenter
 * method. If not pass to the object
 *
 * @param string $key
 */
public function __get($key)
{
    if (method_exists($this, $key)) {
        return $this->{$key}();
    }

    return $this->object->$key;
}

This magic method listens for calls such as $object->created_at. The property that you are trying to access will be passed to the method as the $key argument.

Next we can use the method_exists function to determine if the $key exists on the current object ($this).

If the method does exist we can return the value from that method. If it doesn’t exist, we can pass the call to the original object.

Creating an interface

I’m also going to implement the individual Presenter classes with an interface:

<?php namespace Cribbb\Presenters;

interface Presentable
{
}

Whilst this empty interface seems a bit pointless, it will ensure that when working with Presenter classes at runtime you can determine if the object is “presentable”.

At some point in the future I will likely need to have methods available on these classes. Setting up this kind of structure will ensure future development remains consistent.

Creating your model presenters

Now that we have the basic boiler plate set up, we can extend the AbstractPresenter to create specific rules for each model in the application.

For example, here is how I would modify the created_at date on the User object:

<?php namespace Cribbb\Presenters;

class UserPresenter extends AbstractPresenter implements Presentable
{
    /**
     * Present the created_at property
     * using a different format
     *
     * @return string
     */
    public function created_at()
    {
        return $this->object->created_at->format("Y-m-d");
    }
}

If you wanted to always use a specific format for displaying dates in your application it would probably make sense to abstract this method to the AbstractPresenter so all of your individual presenters will pick up this functionality.

Creating the Presenter service

Now that I’ve set up the AbstractPresenter and the UserPresenter classes I can start using them in my Controllers.

To do this I will create a service wrapper class that will do the heavy lifting for me.

<?php namespace Cribbb\Presenters;

class Presenter
{
}

The first method to create on the Presenter service is for presenting a single Model instance:

/**
 * Return an instance of a Model wrapped
 * in a presenter object
 *
 * @param Model $model
 * @param Presentable $presenter
 * @return Model
 */
public function model(Model $model, Presentable $presenter)
{
    $object = clone $presenter;

    $object->set($model);

    return $object;
}

In this method I will pass the instance of the $model and an instance of the $presenter.

Next I will clone the $presenter and then set the $object and return it.

You might be thinking, “why have you cloned the $presenter rather than just setting the $model on it directly?” This is because I want to use the same method to present multiple model instances in a Collection or a Paginator.

The clone keyword is an interesting aspect of PHP that doesn’t get used that often. When you make a copy of an object in PHP, the copied object maintains a reference to the original object. When you use the clone keyword, the clone is a shallow copy of the original object.

Run the following code to see the difference for yourself:

$dolly = new stdClass();

$polly = $dolly;
$molly = clone $dolly;

$dolly->name = "dolly";

echo "Polly is called " . $polly->name;
echo "Molly is called " . $molly->name;

In the code above, $polly maintains a reference to $dolly whereas $molly does not.

Next I can create a method for presenting Collections:

/**
 * Return an instance of a Collection with each value
 * wrapped in a presenter object
 *
 * @param Collection $collection
 * @param Presentable $presenter
 * @return Collection
 */
public function collection(Collection $collection, Presentable $presenter)
{
    foreach ($collection as $key => $value) {
        $collection->put($key, $this->model($value, $presenter));
    }

    return $collection;
}

In this method I’m simply iterating over the collection and resetting the key with the wrapped $value.

And finally I can create a method for presenting pagination:

/**
 * Return an instance of a Paginator with each value
 * wrapped in a presenter object
 *
 * @param Paginator $paginator
 * @param Presentable $presenter
 * @return Paginator
 */
public function paginator(Paginator $paginator, Presentable $presenter)
{
    $items = array();

    foreach ($paginator->getItems() as $item) {
        $items[] = $this->model($item, $presenter);
    }

    $paginator->setItems($items);

    return $paginator;
}

Again in this method I’m iterating over the paginator and resetting the values.

Using the Presenter Service in your Controller

Now that we’ve set up the UserPresenter and the Presenter service we can use start to use them in the Controller to pass the presented data to the View.

The first thing to do is to inject the Presenter into the Controller through the constructor:

use Cribbb\Presenters\Presenter;
use Cribbb\Presenters\UserPresenter;
use Cribbb\Repositories\User\UserRepository as User;

class UserController extends BaseController {

/**
 * @var Cribbb\Repositories\User\UserRepository
 */
protected $user;

/**
 * @var Cribbb\Presenters\Presenter
 */
protected $presenter;

/**
 * Construct
 */
public function __construct(User $user, Presenter $presenter)
{
    $this->user = $user;
    $this->presenter = $presenter;
}

Next, in your methods you simply pass the returned object from the Repository into the Presenter service:

/**
 * Show
 *
 * @param int $id
 * @return View
 */
public function show($id)
{
    $user = $this->user->find($id);

    if ($user) {
        $user = $this->presenter->model($user, new UserPresenter);

        return View::make('users.show', compact('user'));
    }

    App::abort(404);
}

If you wanted to use a Collection or a Paginator, all you have to do is to use the correct method on the Presenter:

Using a Collection:

/**
 * Index
 *
 * @return View
 */
public function index()
{
    $users = $this->users->all();

    $users = $this->presenter->collection($users, new UserPresenter);

    return View::make('users.index', compact('users'));
}

Using a Paginator:

/**
 * Index
 *
 * @return View
 */
public function index()
{
    $page = Input::get('page', 1);

    $data = $this->user->getByPage($page, 50);

    $users = Paginator::make($data->items, $data->totalItems, 50);

    $users = $this->presenter->paginator($users, new UserPresenter);

    return View::make('users.index', compact('users'));
}

Now when you fire up your View, the properties that you want to present differently will automatically be converted for you.

Conclusion

The Presenter pattern is really a perfect solution to the problem of too much presentation logic in your View files. It can be tempting to copy the same chunk of code or stuff it in the model, but this will quickly become unmaintainable.

The Presenter pattern allows you to maintain appropriate separation of concerns whilst also allowing you to create self contained classes that have the single responsibility to present the data of your application in a specific way. This makes having different forms of presentation possible for the different ways of using the data in your application whilst also making it obvious and self documented when you or someone else needs to alter that presentation logic.

If you want to use Presenters, but you don’t want to set them up yourself you should take a look at the following Laravel PHP packages from Shawn McCool and Robert Clancy.

These two packages also allow you to do some fancy things like automatically calling your Presenters when the View file is loaded. Personally I’m not a huge fan of this approach because I don’t want to have to set a Presenter in the model and I’d rather manually create the Presenter in the Controller, but I do think this would be a very good solution in smaller projects.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.