»
S
I
D
E
B
A
R
«
Fast and good-looking templates in PHP
Aug 14th, 2009 by Sven

This is the second article in serie of articles about using PHP in a better way than an average script-kiddie. You want to be better than an average, right?;) Then read on…

In my house no one likes spaghetti when it’s in code. That’s why we at Wasabi developed a way to separate presentation from content and code already hmm… how many years ago. I don’t know. I know that buzzword of MVC was not around yet… :) However – since we don’t need yet another mambo-jambo-fancy templating language then we need to use minimum set of code in templates also. So, let’s start.

It uses best parts of PHP like dynamic including and output buffering.

First Model-View-Controller a.k.a. MVC. It’s good, it’s easy but don’t take it like it would be the “final solution” or like it would solve all your problems. It won’t.

First let’s create a simple controller:

<?php
/**
 * Controller is invoked when web server gets a request.
 * For simplicity's sake we don't implement
 * fancy URL-to-controller-and-action mapping logic
 * This is usually done by our framework PHP-X.
 *
 * @author Sven Varkel
 */
class Controller
{
    /**
     * This is the initial action
     * that will be called by default
     *
     * @return string
     */
    public function defaultAction()
    {
        /*
         * Let's create model. This holds
         * our data. Model can be used to load data
         * from database or anywhere else.
         */
        $model = new Model();
        $model->setTitle('Mr');
        $model->setAge(33);
        $model->setFirstName('Sven');
        $model->setLastName('Varkel');
        $model->addHobby('Music');
        $model->addHobby('Fishing');
        $model->addHobby('Cooking');

        /*
         * Here we instantiate View
         * that loads a view
         * and then fills the template with data
         * that comes from the prepared
         * model. After that we render
         * the view to a wanted format.
         * In our case the format is HTML
         * but it can be PDF, XML, WML ...
         */
        $view = new View('person');
        $view->setModel($model);
        $renderedView = $view->render();

        /*
         * Finally we return rendered view
         * for output
         */
        return $renderedView;
    }
}
//EOF Controller.php

Then we create a Model …

<?php
/**
 * Model is a structure that
 * holds our data. Model or model helper
 * can be used to pull and push data from/to a data source
 * (e.g database)
 * It can also include operations for data manipulation.
 * Just to keep things clear and easy-to-understand I recommend
 * to NOT put too much logic inside a model. Use helpers for that.
 *
 * @author Sven Varkel
 */
class Model
{
    private $title = null;
    private $age = 0;
    private $firstName = null;
    private $lastName = null;
    private $hobbyList = array();

    /**
     * Gets Title
     *
     * @return Title
     */
    public function getTitle(){
        return $this->title;
    }
    /**
     * Sets Title
     *
     * @param Title
     */
    public function setTitle($value){
        $this->title = $value;
    }
    /**
     * Gets Age
     *
     * @return Age
     */
    public function getAge(){
        return $this->age;
    }
    /**
     * Sets Age
     *
     * @param Age
     */
    public function setAge($value){
        $this->age = $value;
    }
    /**
     * Gets FirstName
     *
     * @return FirstName
     */
    public function getFirstName(){
        return $this->firstName;
    }
    /**
     * Sets FirstName
     *
     * @param FirstName
     */
    public function setFirstName($value){
        $this->firstName = $value;
    }
    /**
     * Gets LastName
     *
     * @return LastName
     */
    public function getLastName(){
        return $this->lastName;
    }
    /**
     * Sets LastName
     *
     * @param LastName
     */
    public function setLastName($value){
        $this->lastName = $value;
    }
    /**
     * Gets HobbyList
     *
     * @return HobbyList
     */
    public function getHobbyList(){
        return $this->hobbyList;
    }
    /**
     * Sets HobbyList
     *
     * @param HobbyList
     */
    public function setHobbyList($value){
        $this->hobbyList = $value;
    }
    /**
     * Adds hobby with name $aHobby
     * to the list of hobbies
     *
     * @param $aHobby
     * @return
     */
    public function addHobby($aHobby){
        $this->hobbyList[] = $aHobby;
    }
    /**
     * In this override helper we
     * create getter names
     *
     * @param $theName
     * @return mixed
     */
    public function __get($theName){
        $getterName = sprintf('get%s', $theName);
        return $this->$getterName();
    }
}
//EOF Model.php

Then we create a View…

<?php
/**
 * This is the view that will
 * be rendered using a template
 *
 * @author Sven Varkel
 */
class View
{
    private $name = null;
    private $path = null;
    private $model = null;
    /**
     *
     * @param $theName
     * @return
     */
    public function __construct($theName){
        $this->setName($theName);
    }
    /**
     * Gets Name
     *
     * @return Name
     */
    public function getName(){
        return $this->name;
    }
    /**
     * Sets Name
     *
     * @param Name
     */
    public function setName($value){
        $this->name = $value;
    }
    /**
     * Gets Model
     *
     * @return Model
     */
    public function getModel(){
        return $this->model;
    }
    /**
     * Sets Model
     *
     * @param Model
     */
    public function setModel($value){
        $this->model = $value;
    }
    /**
     * Gets Path
     *
     * @return Path
     */
    public function getPath(){
        return $this->path;
    }
    /**
     * Sets Path
     *
     * @param Path
     */
    public function setPath($value){
        $this->path = $value;
    }
    /**
     * This method renders the view.
     * It loads template with slots
     * and it loads data from the model into
     * template slots.
     *
     * @return string
     */
    public function render(){
        /*
         * First we start output buffering.
         * This is critical!
         */
        ob_start();
        /*
         * Then we include the template.
         * Look at the fields inside the template!
         * Also look at how we create path of
         * the template.
         */
        $templatePath = sprintf('view/%s.tpl.php', $this->getName());
        /*
         * This is the funny trick to include template here.
         * Template becomes PART OF this class!
         * So we can use all methods from this class
         * inside our template
         */
        include $templatePath;

        /*
         * Here we collect the contents from
         * the buffer to variable $outstr
         */
        $outstr = ob_get_contents();

        /*
         * Here we end output buffering for this level
         */
        ob_end_clean();
        /*
         * Return rendered view
         */
        return $outstr;
    }
    /**
     * Returns slot value.
     * It loads value with given name from
     * the model
     *
     * @param $theName
     * @return mixed
     */
    public function getSlot($theName){
        return $this->getModel()->__get($theName);
    }
}

... and here is the template

<div>
 <h3>Personal information</h3>
 <div>
 Name and title: <?=$this->getSlot('Title')?> <?=$this->getSlot('FirstName')?> <?=$this->getSlot('LastName')?>
 </div>
 <div>
 Age: <?=$this->getSlot('Age')?>
 </div>
 <div>
 <h3>Hobbies</h3>
 <?php
 /**
 * Pay attention to this foreach. This is the elegant way
 * to use php code inside a template.
 * Code is nice and clean, easy to read.
 *
 * Use of PHP short tags is recommended, it makes code much nicer.
 * However - you can use long opening tag as well but then
 * you have to use echo instead of =
 */
 ?>

 <?foreach( $this->getModel()->getHobbyList() as $hobby ):?>

 <div><?=$hobby?></div>

 <?endforeach?>

 </div>
</div>

… and then we clue all pieces together

<?php
/**
 * This script may be called as Front Controller.
 *
 * This one takes the hits. It can ve called directly
 * but it's also very common to use rewrite that makes the URL-s
 * nicer and sends all requests to this script regardless of their
 * visible URL.
 */

/*
 * First include our required classes
 */
require_once '../lib/Model.php';
require_once '../lib/View.php';
require_once '../lib/Controller.php';

/*
 * Instantiate our default controller
 */
$controller = new Controller();

/*
 * Call default action in our controller
 */
echo $controller->defaultAction();

//EOF index.php

OK, that’s it!

This is a simple way to make MVC work for you. The trick of template is that we include it inside a class. The template which is actually made of fragments of PHP code becomes part of the class. Template can use methods of class. It can load data into itself in a clever, fast and elegant way.

All comments to this technique are welcome!

Good-looking easy to understand PHP code… Why not?
Jul 30th, 2009 by Sven

Hereby I start a serie of articles about PHP. I try to share my knowledge on writing good-looking, easy-to-read and understand PHP code. I have eaten too much of spaghetti. I like spaghetti when I’m in Italy sitting outdoors in some nice restaurant. But not in coding… I even have seen spaghetti in great products like Magento… That’s not good.

I always wonder when people try to do something in a strange or complicated way. Not in a natural way. It’s quite common amongst PHP developers. I guess it’s because PHP is rather simple language and it’s really easy to get going with PHP. However – getting going and “going” – there’s a difference.

First – I like OOP (Object Oriented Programming).

And I like it in PHP. I try to avoid “loose” functions as much as possible. OOP is nothing special. OOP is a way (one of many ways) to arrange your code. Don’t believe the bullshit that OOP tries to reflect objects from real world or whatever… In computers nothing but the noise of the cooling fans is real! Everything else is limited by our fantasy.

Secondly – we like our classes to be loaded automatically and named in a way that makes sense.

In this sense Java makes sense. Class name === file name. One file contains exactly one class. I guess that is a really good approach because everything is very very clear. Well defined.Maybe I I like this approach because I have wide experience with different programming languages/frameworks? I have developed .NET (C#, VB.NET), Java, Pascal, RubyOnRails, Basic, even PL/SQL… I’ve seen many of them and I guess I can feel the difference.

That’s why I don’t like too much Zend framework (that Magento also uses). It makes some stupid tricks and string magic inside its stomach so that no one really understands on the fly what’s going on. How are class names composed? Why are they composed like that? Why they cannot be the same as file name? Oh yes – ambiguity… PHP does not support namespaces and then you cannot have 2 classes with the same name. That’s correct. Or… is it? PHP supports namespaces now. Even with no support for namespaces I have managed to develop my very own PHP-X framework since 200? (2,3,4 – who remembers…:)) without namespaces and in a way that I don’t have 2 classes with the same name. It can be done.

Note – there will be more stories about PHP-X published soon. It’s helpful for me. I’m going to release the code to public and I hope it will be helpful for someone else, too. Later…

Now I’m  running out of time and let’s continue the story next day… Comments to PHP article series are welcome. Please add your ideas and wishlist what you would like to read about. I try to my best to not become lazy and continue writing:) I’m overloaded anyway – that’s not a problem. There is an old saying in Estonian (maybe it exists in other languages also): Kes rohkem teeb, see rohkem jõuab. It means something like “The one who does more, can do (even) more”. I support it with a good idea said by Linnar Priimägi that the more you divide time the more you get time.

»  Substance: WordPress   »  Style: Ahren Ahimsa
© Copyright Wasabi Ltd 2009