OpenCart is an e-commerce platform made in php, with impeccable development, 100% MVC. As a proof of concept, I have rewritten the template system of OpenCart to Haanga (Django templates for PHP, über efficient) by César Rodas.
Why Haanga?, simply because I like it.
Why OpenCart?, simply because I like how it is developed.
Implementation, I have created a TemplateEngine class with the following code:
class TemplateEngine {
public $template = null ;
public $default = array();
public $config = array();
function __construct( $templateDir = null ) {
$this->loadDefaults( $templateDir);
}
protected function loadDefaults( $templateDir) {
$this->default = array();
$this->config = array(
'template_dir' => (( is_null( $templateDir )) ? Settings::getInstance()->getValue('Web.root') .'templates' : $templateDir),
'cache_dir' => Settings::getInstance()->getValue('Web.root'). 'cache.templates',
'compiler' => array(
'if_empty' => FALSE,
'autoescape' => FALSE,
'strip_whitespace' => TRUE,
'allow_exec' => TRUE,
'global' => array( ),
'use_hash_filename' => FALSE
)
);
}
function loadEngine() {
// include Haanga
require_once Settings::getInstance()->getValue('Web.root') . 'lib/vendors/Haanga.php';
Haanga::configure($this->config);
}
function loadTemplate( $name ) {
$this->template = $name ;
}
function display( $vars = array()) {
$this->loadEngine();
$vars = array_merge( $this->default, $vars);
Haanga::Load( $this->template , $vars);
}
}
Now we just need to tell opencart to use this class:
in /system/engine/controller.php
Modifying the render and fetch methods, renaming the old fetch to __fetch
[...]
protected function render($return = FALSE) {
foreach ($this->children as $child) {
$action = new Action($child);
$file = $action->getFile();
$class = $action->getClass();
$method = $action->getMethod();
$args = $action->getArgs();
if (file_exists($file)) {
require_once($file);
$controller = new $class($this->registry);
$controller->index();
$this->data[$controller->id] = $controller->output;
$this->addToModule( $controller->id, $controller->output );
} else {
exit('Error: Could not load controller ' . $child . '!');
}
}
if ($return) {
return $this->fetch($this->template);
} else {
$this->output = $this->fetch($this->template);
}
}
protected function addToModule( $module , $output ) {
$i = 0 ;
if ( !isset( $this->data['modules'] )) { return ;}
foreach( $this->data['modules'] as $item ) {
if ( $item['code'] == $module ) {
$this->data['modules'][$i]['output'] = $output;
return ;
}
$i++;
}
}
protected function fetch($filename) {
if ( substr( $_SERVER['REQUEST_URI'], 0, 7) == '/admin/') {
return $this->__fetch( $filename);
} else {
ob_start();
$this->templateEngine->loadTemplate( $filename );
$this->templateEngine->display( $this->data);
$content = ob_get_contents();
ob_end_clean();
return $content;
}
}
protected function __fetch($filename) {
$file = DIR_TEMPLATE . $filename;
if (file_exists($file)) {
extract($this->data);
ob_start();
require($file);
$content = ob_get_contents();
ob_end_clean();
return $content;
} else {
exit('Error: Could not load template ' . $file . '!');
}
}
[...]
To avoid using Haanga in the administration, we check that the url is not from the administration, if so we use the original template engine.
[...]
if ( substr( $_SERVER['REQUEST_URI'], 0, 7) == '/admin/') {
return $this->__fetch( $filename);
[...]
One of the peculiarities of the templates is that they make use of $$variable when displaying the output of the modules, and it is because of this (impossible, it seems, in Haanga) that I have added a method and check so that each module returns its output to the template in an “output” element of the matrix and it is the one we will show.
Thus, in the end, a template like the one for displaying categories remains something like the following examples:
/catalog/view/theme/default/common/column_right.tpl
<div id="column_right">
{% for module in modules %}
{{ module.output }}
{% endfor %}
</div>
/catalog/view/theme/default/product/category.tpl
{% extends "layout/default.html" %}
{% block content %}
<div id="content">
<div class="top">
<div class="left">
</div>
<div class="right">
</div>
<div class="center">
<h1>
{{ heading_title }}
</h1>
</div>
</div>
<div class="middle">
<table style="padding-bottom:10px;">
<tr>
{% if thumb %}
<td>
<img src="{{ thumb }}" alt="{{ heading_title }}" />
</td>
{% endif %}
{% if description %}
<td>
{{ description }}
</td>
{% endif %}
</tr>
</table>
{% if !categories && !products %}
<div class="content">
{{ text_error|default:"" }}
</div>{% endif %}
{% if categories %}
<table>
{% for category in categories %}
<a href="{{ category['href'] }}"><img src="{{ category['thumb'] }}" title="{{ category['name'] }}" alt="{{ category['name'] }}" style="margin-bottom: 3px;" /></a><br />
<a href="{{ category['href'] }}">{{ category['name'] }}</a>
</table>
{% endfor %}
{% endif %}
{% if products %}
<div class="sort">
<div class="div1">
<select name="sort" onchange="location = this.value">
{% buffer sort_order %}{{sort}}-{{order}}{% endbuffer %}
{% for sort in sorts %}
<option value="{{ sort['href'] }}" {% if sort_order == sort['value'] %} selected="selected"{% endif %}>{{ sort['text'] }}</option>
{% endfor %}
</select>
</div>
<div class="div2">
{{ text_sort }}
</div>
</div>
{% inline "elements/lista_productos.html" %}
<div class="pagination">
{{ pagination }}
</div>
{% endif %}
</div>
<div class="bottom">
<div class="left">
</div>
<div class="right">
</div>
<div class="center">
</div>
</div>
</div>
{% endblock %}













Comments