Understanding the data formats
From Invenzzia wiki
- This article is a stub. You can help us by expanding it.
Data formats are one of the most interesting OPT features. As you know, the templates display the data generated by the script. In OPT, they are represented by simple variables, like $variable. But do we need to know, what such variable really is while writing a template? The answer is no, and to be more precise - we should not even know that. Data formats provide an abstraction layer over several OPT syntax elements, so they can represent various PHP structures, like arrays, objects, etc. This makes your templates more portable and allows more flexible code refactoring, as you do not have to rewrite your templates so often.
Contents |
What is affected?
The OPT data formats can affect the following elements so far:
- Script variables:
$variable - Containers:
$variable.subitem - Sections
By default, OPT evaluates all of them to simple PHP arrays, but the default formats provide also a nice support for objects and data generators. Using them, your script may generate some particular data if and only if the templates really need them, a common situation when our website handles several layouts with different features. Of course, from the template side it does not matter, what data format we are using and what features it supports; the syntax is always the same.
An example: list of news
Let's take a look at the following section that displays news:
<div class="news" opt:section="news">
<h1>{$news[title]}</h1>
<p>{u:$news[body]}</p>
</div>
Although it is correct, is has one critical disadvantage - we have specified explicitely that the section variables are stored as an array. Modern ORM-s, like Doctrine, can generate lists of objects and the code above will be useless then. We can deal with it by replacing the square brackets with object operator:
<div class="news" opt:section="news">
<h1>{$news::title}</h1>
<p>{u:$news::body}</p>
</div>
If we know that we have a list of objects, we are at home. The news are displayed, but another problem has appeared. As we look at the Doctrine manual, we can read that unless we are using advanced ORM features, we should generate the result rows as arrays due to optimization purposes. So, our new template must be rewritten once again. Later, we want to re-use the code in a different part of the website or event in another project. The story is the same: here are arrays, there are objects...
The solution is very simple. OPT documentation and all the examples found on this wiki recommend the containers here. A container uses dots as a separator between the elements: $foo.bar.joe:
<div class="news" opt:section="news">
<h1>{$news.title}</h1>
<p>{u:$news.body}</p>
</div>
OPT will automatically recognize that $news.title and $news.body refer to the section elements named "title" and "body". Moreover, it can use data formats now. The template above will work without modifications both for objects and arrays, and all we have to do is to inform the compiler on the script side, what we need:
$query = Doctrine_Query::create()->select('*')->from('News');
// When working with arrays
$view->news = $query->execute(array(), Doctrine::HYDRATE_ARRAY);
// When working with objects
$view->news = $query->execute();
$view->setFormat('news', 'Objective');
As you see, for arrays we simply do nothing, as OPT assumes by default that the data are arrays. However, in the second case Doctrine generates a Doctrine_Collection object that contains some Doctrine_Record. They implement the standard PHP interfaces that allow to traverse and access the particular elements. We must choose the Objective data format for the section, so that OPT knows how to iterate through the objects instead of arrays.
After the data format change you have to recompile your templates! It can be easily done by deleting the contents of the directory pointed by the compileDir directive.
More complex examples
In the previous example, we assumed that both the collection of section items and the list of item variables are of the same data format. However, OPT allows even more flexibility here. The code below generates an array of objects that represent separate article. OPT is still able to handle them correctly:
$view->articles = array(
new Article(1),
new Article(2),
new Article(3)
);
$view->setFormat('articles', 'Array/Objective');
Let's take a look at the last line: $view->setFormat('articles', 'Array/Objective'). The data formats can use the decorator design pattern to extend their features dynamically. Here, we requested to decorate the Objective format with Array, which causes that OPT evaluates iterating through the list as an array, and the access to the single article data as object. The opposite situation: $view->setFormat('articles', 'Objective/Array') is also possible. It means that we want to iterate through an object that returns the article data as arrays.
Variable formats
Data generators
Creating custom formats
The data formats work at the compilation level, so they act as PHP code snippet suppliers for the compiler and OPT instructions. It gives you extra performance, as the data format issue does not have to be evaluated during the runtime, but is hard-coded in the compiled template. Unfortunately, this makes creating a custom data format a bit harder. The compiler communicates with the data format using the interface provided by the abstract class Opt_Compiler_Format. In order to write a custom format, you should extend it and overwrite the appropriate methods.
In the near future, there will be introduced an extension for OPT providing a new data format that is going to simplify this process and reduce it to simple implementation of an interface in a class at the cost of worse performance. The details will be provided soon.
Conclusion
The data formats provide a powerful abstraction layer for the data used by the templates. They bring you extra portability, flexibility and simplify the code refactoring process by separating the representation of the data in OPT from what they really are. We encourage to use them in your apps.

