2. More about data formats

Our play with XML tags does not contain contain anything that would help OPT to determine, where the sections should get the data from and what they exactly are. Now we are going to introduce a concept of data formats which will allow us to do that. The idea of data formats is quite similar to data types in programming languages, except that they can be applied to any element of a template. They decide, what a particular element should be after translating it to the PHP code during the compilation. In OPT 2.0, data formats work with variables and sections, but the future versions are going to implement them in much more template instructions. In our case, the data formats decide on the nature of the sections and their data.

The default data format is called Array and it is automatically set to all sections and variables. It treats the list as an array with indexes enumerated from 0 to n-1 (n is the number of elements in the array). Each list element can be either a scalar value or an array of variables. Moreover, it tells the section to look for the array in a template variable named the same, as the section. Our PHP code that assings the data to the section would look like this:

$view = new Opt_View('list.tpl');
$view->items = array(0 =>
 array('id' => 1, 'title' => 'News 1', 'date_formatted' => '15.10.2009'),
 array('id' => 2, 'title' => 'News 2', 'date_formatted' => '16.10.2009'),
 array('id' => 3, 'title' => 'News 3', 'date_formatted' => '18.10.2009'),
 array('id' => 4, 'title' => 'News 4', 'date_formatted' => '21.10.2009'),
);

Such a list can be easily created from the database results. Below, we show and example for PDO library:

$stmt = $pdo->query('SELECT * FROM news ORDER BY `date` DESC');
$items = array();
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
  $row['date_formatted'] = date($config->dateFormat, $row['date']);
  $items[] = $row;
}
$stmt->closeCursor();
$view->items = $items;

Probably the arrays will be the most commonly used data format, but it is worth to remember that at any time we can make a section to iterate through an object:

$view = new Opt_View('list.tpl');
$list = new SplDoublyLinkedList();
$list->push(new News(1));
$list->push(new News(2));
$list->push(new News(3));
$view->items = $list;
$view->setFormat('items', 'Objective');

This time our list is an ojbect of SplDoublyLinkedList class available since PHP 5.3.0 in the Standard PHP Library. We have also a new method call: setFormat(). It sets the data format to the specified element. The Objective format causes that OPT treats both the list and its elements as objects.

The data formats cannot be changed dynamically. Every change requires the template to be recompiled manually. You need to find the corresponding PHP file in the compileDir directory and remove it.

It is the right time to more advanced examples. Section data formats can control, where to take the data from. By default, OPT looks for a template variable named exactly the same, as the section, but it is not a rule. Suppose we are writing a CMS with a skin system. The script generates various lists with the data for the templates (newest comments, recommended links, banners etc.), but the skin authors do not have to use them all. We would like to optimize the script not to waste the time to retrieve the unused data. Here, the StaticGenerator format will help us a lot. Instead of the data, the section retrieves an object that knows, how to generate the data. If we are using the MVC pattern, all we have to do is to make our models implementing a proper interface.

class News_Model implements Opt_Generator_Interface
{
    /**
     * Generate the data for OPT.
     *
     * @param string $what Section name
     * @return array
     */
    public function generate($what)
    {
       $db = Registry::get('db');
       $stmt = $db->query('SELECT * FROM `news` ORDER BY `date` DESC');
       $list = array();
       while($row = $stmt->fetch(PDO::FETCH_ASSOC))
       {
            $row['date_formatted'] = date($config->dateFormat, $row['date']);
            $list[] = $row;
       }
       $stmt->closeCursor();
       return $list;
    } // end generate();
} // end News_Model;
 
$view = new Opt_View('list.tpl');
$view->items = new News_Model;
$view->setFormat('items', 'StaticGenerator/Array');

The first time the template accesses the section, OPT executes the generate() method, retrieving the data from the database and returing them to the section. If the skin does not use a particular section, the query is not executed and we safe some time and improve the system resource usage. Note how the data format is set. After the StaticGenerator name, we put a slash and the name of another data format name. The OPT data formats implement the decorator design pattern which means that we can create complex data format compositions. StaticGenerator does not work alone (you can try - it will report an exception), because it only decides, where to get the data from, but the section still needs information, what they are (arrays, objects, etc.).

Decoration can be used in other situations, too. Suppose our list is an array, but each element is an object representing a single database row. It is a common situation if we are working with an ORM library. Then we set the format Array/Objective. OPT now iterates through the array, but accesses the element variables as object fields. If we reverse the order, the list becomes an object, and the elements - arrays. As an exercise, we encourage to think what the StaticGenerator/Objective/Array means for OPT. The answer can be found at the end of the article.

It is time to sum up everything that we have learnt so far. Sections are used to display list, but contrary to ordinary loops, they hide as many technical and implementation details as possible from the template and template designers. They can focus on their task without worrying about them and the script structure. Of course, OPT is not an oracle and does not throw them away. The technical decisions are left to the script which selects the data formats. They decide, what the sections are and how they actually work. Think, what are the pros of moving these decisions from a template to scripts?