ViewModels

ViewModels are optional. If you don't need them, you can use Views.

What is a ViewModel?

A ViewModel is a class that contains the logic that is needed to generate your 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 ViewModel to retrieve whatever data is needed for the view. A ViewModel 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.

Creating a ViewModel

First we'll create an empty ViewModel in app/classes/view/index.php:

class View_Index extends ViewModel {}

Then you create the view to which this belongs 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 ViewModel and its view are expected to share the same name. Thus a ViewModel View_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 View_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 ViewModel with the View name (without .php suffix).

And last we'll call the ViewModel from the controller:

ViewModel::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 ViewModel which will assign this data:

class View_Index extends ViewModel
{

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

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

And you're done.

Passing functions to views

To pass a View specific function from your ViewModel to your View, you use Closures:

// In the ViewModel
class View_Index extends ViewModel
{

	public function view()
	{
		$this->echo_upper = function($string) { echo strtoupper($string); };
	}
}

// Which you can then use in your view:
$echo_upper('this string'); // Outputs: "THIS STRING"

Security

It works the same as with View. This means that anything set on the ViewModel 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 ViewModel 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 ViewModel other than the default view() method. To use those, you need to add the method's name as the second parameter to the ViewModel::forge() method:

// will call other_method() upon the ViewModel from the above example
ViewModel::forge('index', 'other_method');

Before and after methods

If you need to have some data added for all the methods in the ViewModel, 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 ViewModel 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.
The name of the expected view is available in the $this->_view property.

Using ViewModels from other namespaces or not View_ 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 is encouraged.