Whenever building applications with CodeIgniter, I always try my best to follow the Model-View-Controller design pattern as much as possible. MVC tells us that views are the presentational filter of the model's data. Over the course of an application's development, it can be very common for views to grow and accumulate cruft. I find that presenters can be a really sleek way of hiding presentational logic away in a class.
Let's look at a simple view that displays some information about a user's bank account:
<div id="account">
<h1><?= $this->bank->get($account->bank_id)->name ?> - <?= $account->title ?></h1>
<p class="information">
<strong>Name:</strong> <?php if ($account->name): ?><?= $account->name ?><?php else: ?>N/A<?php endif; ?><br />
<strong>Number:</strong> <?php if ($account->number): ?><?= $account->number ?><?php else: ?>N/A<?php endif; ?><br />
<strong>Sort Code:</strong> <?php if ($account->sort_code): ?><?= substr($account->sort_code, 0, 2) . "-" . substr($account->sort_code, 2, 2) . "-" . substr($account->sort_code, 4, 2) ?><?php else: ?>N/A<?php endif; ?>
</p>
<p class="balances">
<strong>Total Balance:</strong> <?php if ($account->total_balance): ?><?= "£" . number_format($account->total_balance) ?><?php else: ?>N/A<?php endif; ?>
<strong>Available Balance:</strong> <?php if ($account->available_balance): ?><?= "£" . number_format($account->available_balance) ?><?php else: ?>N/A<?php endif; ?>
</p>
<p class="statements">
<?php if ($this->statements->get_by('account_id', $account->id)): ?>
<?= anchor('/statements/' . $account->id, 'View Statements') ?>
<?php else: ?>
Statements Not Currently Available
<?php endif; ?>
</p>
</div>
This is a rather typical view; it's displaying bits of content from an object, checking for a value's existence and pulling in bits from other database tables. It's fine, but it's all a bit messy, and we're duplicating a fair bit of code. Ideally, we want our view to look something like this:
<div id="account">
<h1><?= $account->title() ?></h1>
<p class="information">
<strong>Name:</strong> <?= $account->name() ?><br />
<strong>Number:</strong> <?= $account->number() ?><br />
<strong>Sort Code:</strong> <?= $account->sort_code() ?>
</p>
<p class="balances">
<strong>Total Balance:</strong> <?= $account->total_balance() ?>
<strong>Available Balance:</strong> <?= $account->available_balance() ?>
</p>
<p class="statements"><?= $account->statements_link() ?></p>
</div>
This clears up our view considerably and removes a bunch of the duplication. It also strips out as much logic as possible from the views, and can make for some very succinct code.
We're going to create an application/presenters directory, and inside there an account_presenter.php file. This file will contain the presenter class for our account object. Let's start by extracting the title:
<?php
class Account_Presenter {
public function __construct($account) {
$this->account = $account;
$this->ci =& get_instance();
}
public function title() {
return $this->ci->bank->get($this->account->bank_id)->name . "-" . $account->title;
}
}
We make sure that we can access the CodeIgniter superobject in $this->ci and the account object that we're presenting in $this->account. We can then port the logic that was previously in the view into a title() method.
We can now go ahead and tidy up the information section:
public function name() {
return $this->account->name ?: "N/A";
}
public function number() {
return $this->account->number ?: "N/A";
}
public function sort_code() {
if ($sc = $this->account->sort_code) {
return substr($sc, 0, 2) . "-" . substr($sc, 2, 2) . "-" . substr($sc, 4, 2);
} else {
return "N/A";
}
}
Similarly, we can tidy up the balances section:
public function total_balance() {
return ($this->account->total_balance) ? "£" . number_format($this->account->total_balance) : "N/A";
}
public function available_balance() {
return ($this->account->available_balance) ? "£" . number_format($this->account->available_balance) : "N/A";
}
And finally, the statements link:
public function statements_link() {
if ($this->ci->statements->get_by('account_id', $this->account->id)) {
return anchor('/statements/' . $this->account->id, 'View Statements');
} else {
return "Statements Not Currently Available";
}
}
Fantastic. Finally, in our controller, we need to pass through an instance of the presenter rather than the account object itself.
public function show($account_id) {
$account = $this->account->get($account_id);
$this->data['account'] = new Account_Presenter($account);
$this->load->view('account/show', $this->data);
}
...and don't forget to load the presenter at the top of your controller:
require_once APPPATH . 'presenters/account_presenter.php';
...and we are done!
As I'm sure you can see, using presenters allows you to tidy up your views in a really object-oriented way. It'd be great to hear your thoughts about presenters and whether or not you plan on using them.