Sobre los hooks hay que decir que en la 1.6 no hay ningún listado, y la documentación es pobre como ella sola, managing hooks doc.
Existe eso sí un listado “aproximado” de hooks para la 1.5, listado 1.5 doc
Sin esta API de programación los módulos no servirían para nada. Este punto de anclaje viene definido por un nombre único, así que un hook en lenguaje plano es un nombre -> displayHome, displayLeftColumn, actionAddProduct..etc
El tema de hooks en prestashop es un cacao importante y al tratar de responder a la pregunta, pero cuantos hooks ahí? parece que se va liando la cosa..
Tipos de hooks: acción y display
Previamente hay que decir que existen dos tipos de hooks, los de acción y los de display, siendo estos últimos los mas ampliamente utilizados.
En la versión 1.5 se introdujo una nomenclatura consistente en que que el hook empiece por action o display de forma que de un vistazo se distingo entre ambos tipos. Aún así hay compatibilidad con nombres “antiguos” (código legacy) de forma que mediante el uso de alias podemos encontrar hooks que se llaman p.ej payment en lugar de displayPayment y updateproduct en lugar de actionProductUpdate.
Los de display tienen la peculiaridad que sabes que lo que devuelvas sera utilizado de forma visual, por lo que siempre al implementarlo hay devolver html de una forma o otra ya que sino pierden su sentido.
En los de acción se realiza algo y sabemos que ese algo no tiene una repercusión visual, es decir p.ej cuando se añade un cliente el módulo necesita realizar una acción, p.ej comunicarse con un ERP, borrar una cache, pueden servir para añadir js a la pantalla..etc, en algunos casos tienes la oportunidad de modificar algún parámetro que reciba el hook.
Como comentaba la documentación es realmente pobre, es ridículo que no haya una explicación de la API prestahsop para un aspecto tan importante como éste, cuando se ejecuta el hook, que recibe como parámetros, en caso de ser de display donde se muestra..etc.
Registro de hooks
Ningún hook sirve de nada si no hay módulos registrados en él, con la API de sistema de registro de módulos que tiene prestashop que consiste en llamar al modulo->registerHook(‘nombre_del_hook’) (en la instalación del módulo preferiblemente, aunque luego se puede hacer uso del backoffice para registrar, des-registrar, mover..etc), tenemos que además en caso de no existir en la base de datos de la instalación, se creará automáticamente, por lo que a partir de ese momento estará disponible para trastear con él de forma administrativa en el backoffice (module positions doc). Es decir se pueden desenganchar y enganchar, mover..etc
Es importante el orden en que se enganchan los módulos a los hooks, ya que en el caso de hooks de display es evidente que nos interesa que uno salga primero que otro.
public function hookNombreDelHook($params)
Ahora bien, en una instalación por defecto de prestashop 1.6.0.14 cuantos hooks hay?
Mirando las Tablas PS_HOOK y PS_HOOK_ALIAS
Usando esta consulta sql en una instalación 1.6.0.14
1 |
SELECT h.id_hook,h.name,a.alias,h.title,h.description FROM `ps_hook` h LEFT JOIN `ps_hook_alias` a ON (h.name=a.name) WHERE 1 |
salen 161 hooks, muchos son hooks que todavía tienen un alias para poder tratar con módulos legacy (y no tan legacy..), otros son nuevos 1.5/1.6
link a la tabla con el listado
Aunque esta cifra y listado es una buena base, no nos podemos fiar al 100% ya que p.ej displayInvoiceLegalFreeText esta en el listado pero no “existe como hook”. Que no exista como hook quiere decir que Prestashop no lo llama.
Esta tabla ira creciendo a medida que instalemos módulos ya que al registrar un módulo en un hook, si éste no existe se creara. Posteriormente una vez exista en la tabla se podrá hacer uso de él en el panel del backoffice de posiciones de módulo.
Núcleo de Prestashop, Hook::exec
Repetimos, un hook es un punto donde los módulos asocian su código a ciertos eventos.
Estos eventos vienen orquestados por el núcleo de prestashop (controladores y clases de entidad). Técnicamente, el código de prestashop llama vía Hook::exec(‘nombre_del_hook’,parametros,.) en ciertos momentos importantes a todos los módulos que están suscritos a ese nombre_del_hook (siguiendo un orden).
Se trata de una implementación del patrón de diseño Observer.
Se le pasa además unos parámetros. Por defecto los parámetros que recibirán los módulos serán el carrito y la cookie pero algunos privilegiados recibirán mas información. Absolutamente no se describe en ningún sitio de la documentación oficial, que hooks recibirán estos parámetros.
P.ej el hook updateOrderStatus vemos que recibe como parámetros el nuevo estado, y la id del pedido
1 |
Hook::exec('updateOrderStatus', array('newOrderStatus' => $newOS, 'id_order' => (int)($order->id))) |
Y aún hay mas, algunos incluso (pocos) recibirán un parámetro por referencia y podrán cambiar el valor de ese parámetro de forma que pueden afectar de forma muy directa al comportamiento de Prestashop.
P.ej
1 |
Hook::exec('actionCartListOverride', array('summary' => $result, 'json' => &$json)); |
esto lo vemos por el & de la variable
Realizando una búsqueda en el código de Prestashop de llamadas a este método Hook::exec, vemos que se llama 219 veces, repartidos entre hooks de acción y de display. Evidentemente se llama al mismo hook desde distintos puntos y hay algunos casos especiales por lo que no se puede decir en ningún caso que hay 219 hooks. P.ej actionCartSave se llama cuando se guarda por primera vez y cuando se modifica un carrito.
También se observa como se usa muchas veces el nombre legacy del hook en la propia llamada..
captura completa llamadas Hook::exec
tabla resumen llamadas a Hook::exec
Llamadas directas desde la plantilla tpl {hook h=”displayNombreHook”}
Un caso especial consiste en que en el sistema de plantillas smarty (archivos tpl) puede hacer una llamada directa a cualquier hook, de forma que si en nuestro tpl llamamos a {hook h="displayTuHook" }
se llamará al hook displayTuHook, es posible incluso pasarle parámetros. Solo tienen sentido hacerlo para mostrar algo, por lo que solo se encontrarán hooks de display.
Este sistema que esta desde la 1.5 ha añadido una gran flexibilidad a la hora de tunear temas mediante el uso de módulos que interese que su contenido se muestre en determinados puntos que no son los habituales de prestashop. Aunque este sistema se salta por completo el sistema MVC (Modelo Vista Controlador) ya que en la propia vista estamos llamando directamente al Módulo.
Como curiosidad este sistema se ha implementado como una función smarty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
smartyRegisterFunction($smarty, 'function', 'hook', 'smartyHook'); ................... .................. function smartyHook($params, &$smarty) { if (!empty($params['h'])) { $id_module = null; $hook_params = $params; $hook_params['smarty'] = $smarty; if (!empty($params['mod'])) { $module = Module::getInstanceByName($params['mod']); if ($module && $module->id) $id_module = $module->id; unset($hook_params['mod']); } unset($hook_params['h']); return Hook::exec($params['h'], $hook_params, $id_module); } } |
Cabe destacar que el tema default-bootstrap también hace uso de ellos…p,ej {hook h=”displayTopColumn”} ya que una instalación base de prestashop incluye un buen puñado de módulos (homeslider y themeconfigurator) que se entienden con el tema default-bootstrap.
Es fácil ver los hooks de display “normales” en la plantilla porque su nombre es bastante descriptivo y son variables smarty (con el $ delante) -> $HOOK_TOP, $HOOK_FOOTER …etc
P.ej
1 |
{if isset($HOOK_TOP)}{$HOOK_TOP}{/if} |
se llama en header.tpl. Esta variable viene rellenada previamente por el núcleo de prestashop (recordemos, por el controlador, ya que es un hook de display) que ha llamado a todos los módulos registrados vía el método Hook::exec mencionado anteriormente. En este caso el núcleo prestashop quiere decir cualquier controlador del FrontOffice, ya que el hook displayTop se necesita en todas las páginas de la tienda.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public function initContent() { $this->process(); if (!isset($this->context->cart)) $this->context->cart = new Cart(); if (!$this->useMobileTheme()) { // These hooks aren't used for the mobile theme. // Needed hooks are called in the tpl files. $this->context->smarty->assign(array( 'HOOK_HEADER' => Hook::exec('displayHeader'), 'HOOK_TOP' => Hook::exec('displayTop'), 'HOOK_LEFT_COLUMN' => ($this->display_column_left ? Hook::exec('displayLeftColumn') : ''), 'HOOK_RIGHT_COLUMN' => ($this->display_column_right ? Hook::exec('displayRightColumn', array('cart' => $this->context->cart)) : ''), )); } else $this->context->smarty->assign('HOOK_MOBILE_HEADER', Hook::exec('displayMobileHeader')); } |
Estos hooks de display normales tienen que estar en todas las plantillas que sean compatibles con la versión de prestashop.
Mas hooks de los que podemos contar?
Hay situaciones especiales, ya que hay casos en que el nombre del hook se genera de forma dinámica. Por lo que p.ej si analizamos este trozo de código que se llama siempre previamente a que se añada una Entidad (u Objeto) en la base de datos,
1 2 |
Hook::exec('actionObjectAddBefore', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'AddBefore', array('object' => $this)); |
vemos que en el primer caso cualquier método que este suscrito a actionObjectAddBefore
, en el momento de que se cree cualquier entidad Prestashop (Producto, Categoría,..etc) sera informado, y se le pasará ese objeto. luego ya se encargará el de distinguirlo. Pero sin embargo en el segundo caso se hace una llamada dinámica de forma que si estamos creando un Producto el hook se llamará actionObjectProductAddBefore
. De esta forma el método suscrito a este hook esperará recibir un producto y no una entidad genérica (producto, categoría, fabricante,cliente,pedido..etc).
Esta estrategia dinámica se utiliza en algunos puntos mas y hace que el número total de hooks que entiende prestashop en su núcleo sea bastante grande
Resultado final de llamadas a hooks desde el núcleo de Prestashop
Entonces, si cogemos y filtramos el resultado anterior (219 resultados) quedan 177
Ver imagen completa. En amarillo los hooks con nombre dinámico y en naranja los pocos que reciben por referencia alguna variable. He intentado que se vean los parámetros que reciben estos hooks.
Sobre las llamadas dinámicas a nombres de hooks, tenemos p.ej que, en una instalación Prestashop, tenemos 97 entidades (Productos, paginas CMS , etc..) con los 6 hooks que hay antes y después respecto a la creación/edición/borrado de entidades, pasamos que de 6 se han multiplicado a 582.
De la misma forma este sistema de nombres dinámico se usa en los controladores del Backoffice antes y después de realizar alguna acción
P.ej
1 2 3 4 5 6 7 |
Hook::exec('actionAdmin'.ucfirst($this->action).'Before', array('controller' => $this)); Hook::exec('action'.get_class($this).ucfirst($this->action).'Before', array('controller' => $this)); // Call process $return = $this->{'process'.Tools::toCamelCase($this->action)}(); // Hook After Action Hook::exec('actionAdmin'.ucfirst($this->action).'After', array('controller' => $this, 'return' => $return)); Hook::exec('action'.get_class($this).ucfirst($this->action).'After', array('controller' => $this, 'return' => $return)); |
P.ej el hook actionAdminProductControllerProductQuantityBefore
y actionAdminProductControllerProductQuantityAfter
se ejecutarán antes y después de que se llame a ajaxProcessProductQuantity en el controlador de Productos del Backoffice
Como hay un porrón de controladores en el Backoffice (95) y cada uno hace bastantes cosas (acciones) el numero de hooks posibles a usar es realmente muy grande.