Implementar Haanga como sistema de plantillas para OpenCart

738 palabras
4 min de lectura

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

Comentarios

Artículos relacionados

05
Jul 2025
6 min

1170 palabras

La primera versión alpha de PHP 8.5 acaba de ser liberada, y debo confesar que me tiene más emocionado que las últimas versiones. No es solo por las mejoras técnicas (que son muchas), sino porque PHP 8.5 introduce características que van a cambiar la forma en que escribimos código.

Y cuando digo “cambiar”, me refiero a ese tipo de cambios que, una vez que los usas, no puedes volver atrás. Como cuando apareció el null coalescing operator (??) en PHP 7, o las arrow functions en PHP 7.4.

4 min

717 palabras

Hace unos días leí un artículo de Dominiek sobre los 5 principios para usar IA profesionalmente y me encontré asintiendo constantemente. Después de años viendo llegar y evolucionar tecnologías, la IA me genera las mismas sensaciones que tuve con otras “revoluciones”: entusiasmo mezclado con una necesaria dosis de escepticismo.

El artículo de Dominiek me resonó especialmente porque describe perfectamente lo que estamos viviendo: un mundo donde la IA se está metiendo en todo, pero no siempre de la manera más útil o sensata.

14 min

2873 palabras

La Filament v4 Beta ha llegado oficialmente, y es sin duda la actualización más ambiciosa y completa en la historia de este framework. Después de explorar en detalle todas las nuevas características, puedo afirmar que esta versión representa un salto cuántico en términos de rendimiento, facilidad de uso y capacidades de desarrollo.

En este análisis exhaustivo, vamos a explorar cada una de las nuevas características de Filament v4, explicando no solo qué es nuevo, sino también cómo estas mejoras pueden transformar tu flujo de trabajo y las posibilidades de tus aplicaciones.

6 min

1247 palabras

Idempotencia en Laravel: Cómo Evitar Duplicados en tus APIs con Elegancia

En el desarrollo de APIs modernas, uno de los desafíos más críticos es garantizar que las operaciones no se ejecuten múltiples veces de forma accidental. Imagina un usuario que realiza un pago y, por problemas de conectividad, hace clic varias veces en el botón “Pagar”. Sin las medidas adecuadas, podrías procesar múltiples pagos por la misma transacción. Aquí es donde entra en juego la idempotencia.