Sunday, August 21, 2011

A simple view controller in PHP

So I've been playing with frameworks lately, and it seems like there are some good options out there. But what if you don't want a whole framework? What if you just need something simple to help you keep your logic and your view separate? There are templating engines out there (Smarty, PHPTal, Twig), but they all require you to add a bunch of files and learn a whole new syntax than you already know. What if you just have a simple project, and don't feel like re-inventing the wheel? Wouldn't it be nice to be able to write simple php that you already know, but have a class that can help you keep your view separate from your controllers?
  I'm going to show you how to make a view controller (ViewController source, less than 50 lines with comments) that does just that, so you can take something like this...
User Information:<p>
{foreach $names as $name}
Name: {$name|capitalize}<br>
Address: {$address|escape}<br>
{/foreach}
...into something you already are familiar with, like this...
<html>
<head>
<title><?php echo $title; ?></title>
</head>
<body>
User Information:<p>
<?php foreach( $names as $name ): ?>
Name: <?php echo $name; ?><br>
Address: <?php echo $address; ?><br>
<?php endforeach; ?>
</body>
</html>

Now you're first thought is probably the most logical one. Why would I want to write more code? The first example has way less code. The problem with this is it relies on a templating system, and with that requires you to not only learn another syntax and set of rules, but also introduces opportunities for a new set of bugs to have to go through. The code on the bottom is more verbose, but it has the advantage of using only stuff you already know.
  So how do we create this dead simple view controller? First, let me show you how we implement it. In your controller, You instantiate the class like so...

$View = new ViewController( 'view_example.php' );
$View->title = "Waffles";
$items = array( 'foo' => 'bar', 'waffle' => 'iron', 'apple' => 'pie' );
$View->items = $items;
$View->render();
...where view_example.php looks like this...
<!DOCTYPE HTML>
<html>
<head>
 <title><?php echo $title; ?></title>
<head>
<body>
 <h1><?php echo $title; ?></h1>
 <ul>
<?php foreach( $items as $k => $v ): ?>
<li><?php echo $k . ' - ' . $v; ?></li>
<?php endforeach; ?>
</body>
</html> 

Simple enough? Let's break that down, line by line. First off, we instantiate the class with the name of the view that we want, like so...
$View = new ViewController( 'template_example.php' ); 
...then we are able to set the values of any of the variables we need to, that are in our view. So to set this...
<h1><?php echo $title; ?></h1> 
...we set a property for our object like that class had it built in, like so...
$View->title; 
"Wait a second, I thought you said that we weren't going to have to learn a whole new set of rules? It looks like I can only use variables that are already set up in the class." That's where PHP's magic methods come in. By overriding the __get() and __set() Magic Methods for this class, we are able to interrupt what happens when you call $view->new_property_name. So why is this important? Well, this allows us to assign value to any variable in our view by setting the appropriate value with...
$View->another_variable_name = 'waffles';
$View->any_name_you_wish = "some more stuff";
$View->a_list_of_items = array( 'foo' => 'bar', 'apple' => 'pie');
...so when we eventually go to render our view, we take all these stored properties, and extract them into that view. Sound complex? It really isn't. First we use the extract() function to take all of our stored values into the local scope of $View->render(), then we include our view which is stored in the private $View->view property. What does this actually look like?

function render()
{
extract( $this->views_variables );
require_once( $this->view );
}
And there you have it. The worlds simplest php view controller (not shortest, this isn't code golf). No  need for templating engines or complex frameworks.

Source Code: https://github.com/JoeCortopassi/DeadSimpleFramework/blob/master/ViewController.php

2 comments:

  1. No need for the OOP stuff. PHP arrays are sufficient.

    // even simpler templating example

    <?php
    // a view is described by a template file and an array of variables
    $template = "template_example.php";
    $vars = array("title" => "Waffles");
    $vars["items"] = array(
        "foo" => "bar",
        "waffle" => "iron",
        "apple" => "pie"
    );

    render($template, $vars);

    // view rendering function
    function render($template, $vars)
    {
        extract($vars);
        require($template);
    }
    >>

    ReplyDelete
  2. I can't get my code to format in my comment. See http://news.ycombinator.com/item?id=2911353

    ReplyDelete