Presenters

What is a Presenter?

A Presenter is a class that contains the logic that is needed to generate your view (or views). When the controller is done with your user input and is done with whatever actions it needed to take, it turns execution over to the Presenter to retrieve and process whatever data is needed for the view. A Presenter shouldn't do any data manipulation but can contain database calls and any other retrieval or preparation operations needed to generate the View's data.

Presenters are optional. If you don't need them, you can use Views directly, and keep the pre-processing logic in your controller.

Creating a Presenter

First we'll create an empty Presenter class in APPPATH/classes/presenter/index.php:

class Presenter_Index extends Presenter
{
}

Then you create the view that is associated with the presenter in app/views/index.php:

<h1><?php echo $title; ?></h1>

<ul>
<?php
	foreach ($articles as $a)
	{
		echo '<li>'.$a->title.'</li>';
	}
?>
</ul>

On view names
A Presenter and its view are by default expected to share the same name. Thus a Presenter Presenter_Index expects the view to be in app/views/index.php. And underscores work here the same as with classes, which means that the view for Presenter_Some_Thing is expected to be in app/views/some/thing.php.
This default can be overwritten by setting a non-static $_view property in your Presenter with the View name (without its suffix), or passing a custom View name when forging the Presenter.

And last we'll create the Presenter from the controller:

$presenter = Presenter::forge('index');

Now we have everything setup; however, there is still no data passed to the view. It still needs to get a $title string and $articles array passed to it. We do this by adding a view() method to the Presenter which will assign this data:

class Presenter_Index extends Presenter
{

	public function view()
	{
		$this->title = 'Testing this Presenter thing';

		$this->articles = Model_Articles::find('all');
	}
}

And you're done.

In your code, Views and Presenters are interchangeable. You can return Presenters from your controller actions, you can set a Presenter as a Theme partial, or assign it to a section of your page template. The basic API of the Presenter is compatible with the View. This makes it easy to swap a View for a Presenter in your code without having to do a major code overhaul.

Passing functions to views

To pass a View specific function from your Presenter to your View, you use an anonymous function or Closures:

// In the Presenter
class Presenter_Index extends Presenter
{

	public function view()
	{
		// set a closure that returns a value
		$this->somevar = function() { return "somevalue"; };

		// this works the same
		$this->set('othervar', function() { return "othervalue"; });

		// for closures with arguments, use set_safe to prevent encoding
		$this->set_safe('echo_upper', function($string) { echo strtoupper($string); });
	}
}

// Which you can then use in your presenter view:
echo $somevar, $othervar;   // Outputs: "somevalue" "othervalue"
$echo_upper('this string'); // Outputs: "THIS STRING"

Closures are also treated like variables when it comes to filtering. If you have a closure that returns a value, use the set() method, so the value will be encoded according to your filtering setting. If you have a closure that will be used as a modifier, like in the example above, or you have a closure that returns a value that should not be encoded, use the set_safe() method, or pass false as the third parameter of set().

For legacy applications that expect a closure to never be filtered, edit your applications config.php configuration file, add the following key:

/**
 * Whether to filter closures as well
 */
'filter_closures'  => false,

Security

It works the same as with View. This means that anything set on the Presenter will be output encoded as long as you don't switch that off. You can use the same set($name, $value, $encode) method on the Presenter as you'd use on the View directly. More on this in the Security section of View.

Advanced usage

More methods

If there are different ways of parsing the same View, you can add multiple methods to the Presenter other than the default view() method. To use those, you need to add the method's name as the second parameter to the Presenter::forge() method:

// will call other_method() upon the Presenter from the above example
$presenter = Presenter::forge('index', 'other_method');

Before and after methods

If you need to have some data added for all the methods in the Presenter, you can add a before() or after() method. just like you can with Controllers.

Changing the view

By default, the $this->_view gets a View object assigned to it. You can replace this object by making your own set_view() method in the Presenter and setting the $this->_view to an object of your choosing.
However, this object must allow you to set properties on it (which are used as the template data) and must have a __toString() magic method that will render and return the parsed contents. In other words, it must be compatible with the behaviour of the View class.
The name of the expected view is available in the $this->_view property.

Using a different view file, or an existing View object

You can also tell the Presenter instance to use a different view, and not use the automatic mechanism for determining the view to load, by using the forth parameter of the forge() method:

// use the 'other/index' view instead of the 'index' view
$presenter = Presenter::forge('index', 'other_method', null, 'other/index');

// you can also pass a View object directly
$view = View::forge('other/index');
$presenter = Presenter::forge('index', 'other_method', null, $view);

Accessing the view

You can use the get_view() method to get access to the View object from outside the Presenter.

Using Presenters from other namespaces or not Presenter_ prefixed

If you want to use these, you have to use the full classname with the forge(), including the namespace. In these cases, the default naming will often not work as expected so setting the $_view property, or passing a custom view name, is encouraged.