Smarty es un templates engine, sirve principalmente para mantener separados los procesos del diseño y del contenido, pero va más allá gracias a su sistema de cache, sus archivos de configuración y la facilidad de extenderlo a través de plugins
Son archivos HTML con algunas variables a ser sustituidas por Smarty. En ellos se pueden incluir algunas estructuras de control tales como foreach, if, ifelse, pero sólo con motivos de presentación de la información.
Smarty parsea los templates y crea a partir de ellos scripts php, se les conoce como templates compilados.
Smarty puede guardar el resultado (HTML) de procesar los templates compilados junto con las variables que son sustuídas dentro de ellos.
Supongamos que necesitamos una aplicación que tendrá diferente diseño para cada cliente, a la que se le puede cambiar el idioma de la interfase, que recibirá muchas visitas y es necesario optimizar las consultas a la base de datos (evitándolas de ser posible)
Comenzaremos creando una sencilla clase que configura a Smarty y hace uso de algunas de sus funciones teniendo en mente lo que necesitamos, el cache, los temas y los idiomas
Una parte muy importante de esta clase es su constructor, aquí se definen los parametros que usará Smarty, es decir, aquí es donde lo configuramos
20: function Theme($dir, $cache = true, $cacheLife = 43200){ 21: $this->_smarty = new Smarty(); 22: $this->_smarty ->caching = $cache; 23: $this->_smarty ->cache_lifetime = $cacheLife; 24: $this->_smarty->use_sub_dirs = true; 25: $this->_smarty ->compile_check = true; 26: $this->_smarty ->template_dir = "templates/".$dir."/"; 27: $this->_smarty ->compile_dir = "templates_c/"; 28: $this->_smarty ->config_dir = "language/".LANG."/"; 29: $this->_smarty ->cache_dir = "cache/"; 30: 31: $this->_smarty ->register_block('dynamic', array($this, 'smarty_block_dynamic'), false); 32: }
Línea 22 establecemos si se va a guardar cache o no
Línea 23 establecemos el tiempo en segundos que durará el cache. Existen otra
opción para establecer el cache como permanente
L ínea 24 configuramos a Smarty para crear subdirectorios del cache, este valor
vienen por default en false a partir de smarty-2.6.2
Línea 25 configuramos a Smarty para que verifique por cambios en el template,
en tal caso regenera los templates compilados. Cuando la aplicación esté finalizada,
se recomienda poner este valor en false, para optimizar el desempeño, pero implicará
que los cambios en el template no se reflejen en nuestro sitio web. a su vez,
significa que podríamos distribuir una aplicación sin HTML, es decir solamente
con los templates compilados es suficiente para Smarty :D
L ínea 26 establecemos el directorio que contiene a los templates, la
variable $dir, es para el tema
Línea 27 establecemos el directorio para los templates compilados
Línea 28 establecemos el directorio de archivos de configuración,
que en nuestro caso, usaremos para los idiomas del sitio
Línea 29 establecemos el directorio donde guardaremos el cache
Línea 31 registramos un plugin que no es cacheable
38: function setTemplate($template){ 39: $this->_template = $template.".html"; 40: if($this->_smarty ->template_exists($this->_template)!=true){ 41: die('no existe el template: <b>'. $this->_template.'</b> del idioma: <b>'.LANG.'</b>'); 42: } 43: }
Esta función establece un template o en caso de que no exista, mata a la aplicación. Nótese que si se quiere distribuir una aplicación sin templates deberíamos omitir la llamada a la funcion template_exists.
50: function fetch($id = 'void'){ 51: return $this->_smarty ->fetch($this->_template,$id.THEME.LANG, THEME); 52: }
Esta función tiene doble propósito, como hemos configurado a Smarty para crear cache, al llamarla se crea el cache y nos devuelve el valor del template ya compilado, parseado y con las variables sustituidas, es decir puro HTML. Las constantes THEME y LANG son para poder distinguir entre las diferentes versiones del cache que existen debido a los temas e idiomas del sitio.
58: function display($id = 'void'){ 59: $this->_smarty ->display($this->_template,$id.THEME.LANG, THEME); 60: }
Esta función es similar a la anterior, la única diferencia es que esta no nos devuelve nada, sino que lo escribe directamente en el HTML en el momento de llamarla, es equivalente a echo $theme->fetch();
66: function isCached($id){ 67: return $this->_smarty->is_cached($this->_template, $id.LANG, THEME); 68: }
Esta función verifica si ya existe el cache para los parámetros de idioma y tema que estemos usando
73: function smarty_block_dynamic($param, $content, &$smarty) { 74: return $content; 75: }
Esta es una función cualquiera para registrar un plugin de Smarty
Archivo de configuración que uso como archivo para idioma
1:Title="Introduccion a smarty" 2:Intro="Bienvenido al mundo de Smarty" 3:Menu="Menú" 4:Dynamic_data="Sin cache" 5:Plugins="Algunas funciones de smarty" 6:Threeofthesame="Tres maneras de presentar la misma información (un arreglo asociativo)" 7:selectdate="html_select_date" 8:selectime="html_select_time" 9:table="html_table" 10:Nocontent="No hay contenido para mostrar" 11:timewhencached="Esta es la hora en que el template fue cacheado" 12:Content="Contenido"
Template para el menú
1:{dynamic} 2: {section name=menu show=$entries} 3: <ul> 4: {foreach item=entry from=$entries} 5: <li><a href="{$self}?id={$entry.news_id}">{$entry.news_title}</a></li> 6: {/foreach} 7: </ul> 8: {/section} 9: <br/> 10: {$smarty.now|date_format:"%A, %B %e, %Y"}<br/> 11: {$smarty.now|date_format:"%H:%M:%S"} 12:{/dynamic}
A continuación el template principal para la aplicación
1:<html> 2:{config_load file="txt.txt"} 3: <head> 4: <title> 5: {#Title#} 6: </title> 7: 8: {literal} 9: <style> 10: body {font-family:verdana, tahoma;} 11: h1 {font-size:18px;} 12: h2 {font-size:16px;} 13: h3 {font-size:14px;} 14: p {font-size:12px;} 15: a, td {font-family:verdana, tahoma; 16: font-size:12px;} 17: </style> 18: {/literal} 19: 20: </head> 21: <body> 22: <h1>{#Intro#}<h1> 23: <table style="border: 1px dotted gray;" cellspacing="0" cellspadding="0" width="70%"> 24: <tr> 25: <td width="30%" valign="top" style="border: 1px dotted gray;">{#Menu#}<br/> 26: <h2 style="display:inline">{#Dynamic_data#}</h2> 27: <div style="border: 1px solid red"> 28: {dynamic} 29: {$menu} 30: {/dynamic} 31: </div> 32: <div style="border: 1px solid blue"> 33: <p>{#Plugins#}</p> 34: 35: <p> 36: {#selectdate#}<br/> 37: {html_select_date} 38: </p> 39: 40: <p> 41: {#selectime#}<br/> 42: {html_select_time use_24_hours=true} 43: </p> 44: 45: <p>{#table#} 46: {html_table loop=$data cols=4 tr_attr=$tr}<br/> 47: </p> 48: 49: <p>{#Threeofthesame#}</p> 50: <select name=customer_id> 51: {html_options options=$cust_options selected=$customer_id} 52: </select><br/><br/> 53: {html_checkboxes name="id" options=$cust_options selected=$customer_id separator="<br />"}<br/><br/> 54: {html_radios name="id" options=$cust_options selected=$customer_id separator="<br />"} 55: </div> 56: 57: <p>{#timewhencached#}<br/> 58: {$smarty.now|date_format:"%A, %B %e, %Y"}<br/> 59: {$smarty.now|date_format:"%H:%M:%S"} 60: </p> 61: 62: </td> 63: <td valign="top" style="text-align:justify; padding:10 px;"> 64: 65: {section name="content" show=$content} 66: <h2>{#Content#}</h2> 67: <p style="padding:10px;">{$content|nl2br}</p> 68: {/section} 69: 70: {if $content eq ''} 71: <p>{#Nocontent#}<p> 72: {/if} 73: 74: </td> 75: </tr> 76: </table> 77: </body> 78:</html>
Para la aplicación usaré PEAR::DB, que junto con Smarty son llamados el "Dúo dinámico", más información de PEAR::DB en el sitio de pear
La aplicación comienza estableciendo constantes para conexión de base de datos, el idioma y el tema del sitio.
Y trayendo desde la base de datos la información para el menú, ya que este nunca estará en cache. (por el caso de que se agreguen más entradas a la base de datos y porque necesitaba un pretexto para demostrar el uso de partes no cacheables de un template :P )
29:$query = 'SELECT news_id, news_title FROM news'; 30:$result = $db->query($query); 31: 32:$entries = array(); 33: 34:while ($row = $result->fetchRow()) { 35: $entries[] = $row; 36:}
42:$theme = new Theme(THEME); 43: 44:$theme->setTemplate('menu'); 45:$theme->_smarty->assign('self', $_SERVER['PHP_SELF']); 46:$theme->_smarty->assign('entries', $entries); 47: 48://el menú es el mismo para cada caso de id 49:$menu = $theme->fetch();
Línea 42, se crea una instancia de la clase Theme
Línea 44 establezco el template a usar: menu
Línea 45 asigno la variable self para el template menu
Línea 46 asigno la variable entries (que son las entradas de la consulta
para el menú, un arreglo asociativo) para el template self
Línea 49 obtengo el resultado de la sustitución de variables en
el template y lo guardo en la variable $menu
51:$theme->setTemplate('index'); 52:$theme->_smarty->assign('menu', $menu); 53: 54://para la tabla 55:$theme->_smarty->assign('data',array(1,2,3,4,5,6,7,8,9)); 56:$theme->_smarty->assign('tr',array('bgcolor="#eeeeee"','bgcolor="#bbbbbb"')); 57: 58://para los radio, checkboxes y el select 59:$theme->_smarty->assign('cust_options', array( 60: 1001 => 'Joe Schmoe', 61: 1002 => 'Jack Smith', 62: 1003 => 'Jane Johnson', 63: 1004 => 'Charlie Brown')); 64://id del dato preseleccionado 65:$theme->_smarty->assign('customer_id', 1003);
Línea 51 establecemos el template a usar
Línea 52 asignamos la variable menu para el template index
Línea 55 asignamos unos datos que usaremos para construir una tabla
Línea 56 asignamos unos parámetros para la tabla
Línea 59 asignamos los datos a usar en los checkboxes, radios y el select
Línea 65 asignamos el dato que estará seleccionado por defecto
en los controles radios, checkbox y select
68:if(!$theme->isCached($_GET['id'].THEME)){ 69: echo "<div style='background-color:cornflowerblue;'><p>tomando desde la base de datos</div>"; 70: //el id de la noticia 71: //funciona tambien para identificar el cache 72: if(isset($_GET['id'])){ 73: $query = "SELECT news_content FROM news WHERE news_id='".$_GET['id']."'"; 74: $result = $db->query($query); 75: $row = $result->fetchRow(); 76: $content = $row['news_content']; 77: 78: if (DB::isError($db)) { 79: die($db->getMessage()); 80: } 81: } 82: $theme->_smarty->assign('content', $content); 83:} 84: 85:$theme->display($_GET['id']);
Línea 68 verifico si la noticia con el id se encuentra en cache, de
ser así lo llamo en la línea 85
Línea 69 Muestra un aviso de que se están tomando los datos de
la base de datos
Lineas 72-81 obtengo los datos de la noticia con el id proporcionado
Línea 82 asigno la variable contenido para el template index
Línea 85 muestro el template ya procesado y con las variables sustituidas
que es finalmente HTML