OpenCart es una plataforma de comercio electrónica hecha en php, con un desarrollo impecable, 100% MVC. Como prueba de concepto he reescrito el sistema de plantillas de OpenCart a Haanga (plantillas “Django” para PHP, über eficiente) de César Rodas.

¿ Por que Haanga ?, simplemente porque me gusta.

¿ Por qué OpenCart ?, simplemente porque me gusta como está desarrollado.

Implementación, he creado una clase TemplateEngine con el siguiente código:

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() {
            // incluimos 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);
        
        }
    
    }

Ahora sólo queda indicarle al opencart que haga uso de esta clase:

en /system/engine/controller.php

Modificando los métodos render y fetch, renombrando el antiguo fetch a __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 . '!');
        }
    }

[...]

Para evitar utilizar Haanga en la administración se comprueba que la url no sea de la administración, de ser así se utiliza el motor de plantillas original.

[...]
            if ( substr( $_SERVER['REQUEST_URI'], 0, 7) == '/admin/') {
                return $this->__fetch( $filename);
[...]

Una de las particularidades de las plantillas es que hacen uso de $$variable a la hora de mostrar la salida de los módulos, y es por esto (imposible, por lo que parece, en Haanga) que he añadido un método y comprobación para que cada módulo devuelva a la plantilla su salida en un elemento “output” de la matriz y es el que mostraremos.

Así, al final, una plantilla como la de la visualización de las categorías queda en algo como los siguientes ejemplos:

/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  &#038;&#038;   !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 %}
                   &lt;option value="{{ sort['href'] }}" {% if sort_order == sort['value'] %} selected="selected"{% endif %}>{{ sort['text'] }}&lt;/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 %}