Implementar Haanga como sistema de plantillas para OpenCart

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 %}

Relacionados