Hablando llanamente, los hooks son puntos en el código de un programa donde se deja una puerta abierta para realizar un evento relacionado, para insertar código y/o para modificar un comportamiento.

En PrestaShop tenemos 2 tipos básicos de hooks: action (acción) y display (plantilla). Además existen hooks para front-office, para el back-office o generales para en un objeto se realiza un determinado evento.

También existen hooks dinámicos, esto quiere decir que su nombre se genera a partir de un patrón, por ejemplo actionAdminAddAfter, actionAdminDeleteBefore, etc.

No existe una lista fiable de todos los hooks en PrestaShop aunque siempre puedes echar un ojo a la base de datos en la tabla ps_hook para hacerte una idea. En esta entrada iré  indicando alguno de los más utilizados, así que si quereis saber si existe un hook no queda otra que ir al sitio (clase, controlador o plantilla) donde os haga falta y buscar si existe.

Antes de comenzar quiero destacar algo importante acerca de los hooks de display, al ser estos llamados desde la plantilla hay ciertos eventos o funcionalidades que no podreis realizar. ¿Por qué? Piensa en el flujo de ejecución y lo último en procesarse será la plantilla, así que algo tan sencillo como agregar una hoja de estilos CSS (tanto en front-office como en back-office) no podrá hacerse desde un hook tipo display.

Instalación de los hooks

En la anterior entrada vimos como se instala y desinstala un hook en PrestaShop 1.6. En PS 1.5 debeis hacerlo a mano en la base de datos.

Si apenas tienes que instalar un par de hooks, controlarlo es sencillo, pero que pasaría si vuestro módulo utilizara digamos 4 hooks… Y si utilizara 10? Yo desde aquí propongo separar la instalación de hooks en 2 métodos, declaramos un array con los hooks a mantener en nuestro módulo y las funciones usarían bucles para procesarlos. Os presento a registerHooks() y unregisterHooks(). Siéntase libre de usar este pedacito de código, pero recuerda que está bajo licencia beerware 😉

class mimodulo extends Module
{
    private $_hooks = array(
        'displayCustomerAccount',
        'displayFooter',
        'displayHeader',
        'moduleRoutes',
    );

    public function __construct() {
        // ...
    }

    public function install() {
        if (!parent::install()) {
            $this->_errors[] = sprintf($this->l('Could not install %s'), 'module');
            return false;
        }
        
        if (!$this->registerHooks()) {
            $this->_errors[] = sprintf($this->l('Could not install %s'), 'hooks');
            return false;
        }
        
        return true;
    }
    
    public function uninstall() {
        
        if (!$this->unregisterHooks()) {
            $this->_errors[] = sprintf($this->l('Could not uninstall %s'), 'hooks');
            return false;
        }
        
        if (!parent::uninstall()) {
            $this->_errors[] = sprintf($this->l('Could not uninstall %s'), 'module');
            return false;
        }
        return true;
    }
    
    /**
     *
     * @param array $hooks
     * @return bool
     */
    private function registerHooks() {
        $return = true;
        foreach ($this->_hooks as $hook)
            $return &= $this->registerHook($hook);
        return $return;
    }
    
    /**
     *
     * @param array $hooks
     * @return bool
     */
    private function unregisterHooks() {
        $return = true;
        foreach ($this->_hooks as $hook) {
            if (Hook::getIdByName($hook))
                $return &= $this->unregisterHook($hook);
        }
        return $return;
    }
}

Ejecución de un hook

Una vez que tenemos nuestro hook instalado, debemos procesarlo desde nuestro módulo, y es tan sencillo como agregar un método con el patrón hook. Por ejemplo, para el hook displayHeader usaremos la función hookDisplayHeader($parametros).

Los hooks pueden recibir parámetros o no, en cualquier caso de recibirlos será en forma de array donde irán incluidos los datos en forma de objeto o array. Al principio esto es un poco confuso, así que para salir de dudas durante el desarrollo haremos cosas como estas para ver que carajo recibimos:

public function hookDisplayHeader($params) {
    die('<pre>'.print_r($params, 1).'</pre>');
}

También puedes buscar la llamada al hook en el código de PrestaShop y ver que se le envía, ya al gusto. En el caso del ejemplo lo vemos en el archivo
classes/controller/FrontController.php, dentro del método initContent()

y no tiene ningún parámetro.

En el caso de que se quiera modificar el contenido de los parámetros tendremos que declarar el uso de éste por referencia y no por valor, algo muy útil en ciertos hooks como «filterCmsContent» de PS1.7.

public function hookFilterCmsContent(&$params) {
   $params['content']['content'] = preg_replace('/[pattern]/', 'replacement', $params['content']['content']);
}

Algunos hooks a destacar

hook Header, displayBackOfficeTop

Este hook es el propio para agregar estilos y scripts en el front-office y el back-office respectivavemnte.

public function hookHeader() {
    $this->context->controller->addJqueryUI('ui.autocomplete');
    $this->context->controller->addJS($this->_path.'views/js/mimodulo.js');
    $this->context->controller->addCSS($this->_path.'mimodulo.css');
}

public function hookDisplayBackOfficeTop() {
    $this->context->controller->addCSS($this->_path.'views/css/displayBackOfficeTop.css');
}

hooks filterCategoryContent, filterCmsCategoryContent, filterCmsContent, filterHtmlContent, filterManufacturerContent, filterProductContent, filterSupplierContent

Esto hooks son nuevos en la versión 1.7, para versiones anteriores es necesario hacer algún override, etc. En estos hooks podemos procesar el contenido de texto enriquecido de ciertos elementos. A los que acostumbreis a trabajar con WordPress™ el uso de shortcodes será normal. En PrestaShop es una grata novedad. Podeis encontrar un breve ejemplo un par de puntos atrás.

hook moduleRoutes

Este hook es la madre del cordero y uno de mis favoritos. Permite dirigir las URI que deseemos a controladores, quedando una FriendlyURL para nuestras nuevas funcionalidades.

Lo malo es que si obtenemos el enlace hacia el controlador usando la clase de PS Link no nos va a devolver la URI amigable, si no la típica ?module=mimodule&controller=mifrontcontrolller… Pero esa es otra historia!

Para explicar cómo funciona, que mejor que con un ejemplo, no sin antes añadir que se pueden declarar tantos enrutamientos como nos hagan falta simplemente añadiendo elementos al array.

public function hookModuleRoutes() {
    return array(
        'module-mimodulo' => array(
            'controller' => 'myFrontController',
            'rule' => 'urlamigable/{categoria}/{id_elemento}-{link_rewrite}.html',
            'keywords' => array(
                'categoria' => array(
                    'regexp' => '[_a-zA-Z0-9_-]+',
                    'param' => 'categoria',
                ),
                'id_elemento' => array(
                    'regexp' => '[_0-9_]+',
                    'param' => 'id_elemento',
                ),
               'link_rewrite' => array(
                    'regexp' => '[_a-zA-Z0-9_-]+',
                    'param' => 'link_rewrite',
                ), 
            ),
            'params' => array(
                'fc' => 'module',
                'module' => 'mimodulo'
            )
        ),
    );
}

El key del array es al gusto, lo de poner una cadena que identifique el origen tiene 2 propósitos: no sobreescribir un enrutamiento ya existente y por si tenemos que depurar.

Los parámetros enviados en cada elemento del array serán los siguientes:

controller: el nombre de nuestro controlador del FrontOffice. Es decir, si el controlador está declarado como class MiModuloMiControladorFrontController extends ModuleFrontController, aqui pondríamos MiControlador.

rule: esto es expresión regular de la URI para la que PS responderá con nuestro controlador. Los parámetros que se enviarán mediante GET los pondremos entre llaves { } con un nombre que asignaremos al gusto.

keywords: esto es un array donde estableceremos la validación de los datos a procesar. Los keys del array debe coincidir con los declarados en el elemento rule. Cada uno llevará su expresión regular para validar el dato en regexp y el nombre de la variable GET a la que va a ser traducido en param.

params: aqui tuneamos un poco cómo se va a manejar la petición, casi siempre usaremos fc (Front Controller) con el valor «module» y en module el nombre del propio módulo, pero se podrían definir rutas para controladores nativos de PS o de otros módulos.

Crear hooks

Nada nos impide añadir a nuestra tienda un hook en un módulo, en un controlador, en una clase o en una plantilla. Para controladores o clases nativos se recomienda el uso de overrides, o se perderá al actualizar PS a una versión más reciente.

Para crearlos no hay más que ir al script donde deseemos tenerlo. Os dejo un par de ejemplos:

hook en script PHP

$result = Hook::exec('miHook', array('param1' => $this, 'param2' => $myparam));

hook en plantilla Smarty

{hook h='miHook' param1=$miparam1 param2=$miparam2}

Problemas con hooks: instalación a mano

A veces pasa que PS no instala algún hook de nuestro módulo o simplemente no queremos resetear el módulo que estamos confeccionando ni usar un upgrade ya que aún no hemos publicado este, etc. En esos casos podemos irnos a la base de datos de nuestro PS y mirar el ID de nuestro módulo en la tabla ps_module, y el ID del hook en la tabla ps_hook. Si este último no está en la tabla, lo añadimos, sin miedo. Una vez tenemos los ID de ambos elementos, insertamos un registro con la relación en la tabla ps_hook_module y listos, tenemos control del hook en el módulo.

Etiquetas: ,

1 comentario. Dejar nuevo

Muy interesante el tema de los módulos para Prestashop. He encontrado otros artículos similares, pero están tan bien explicados. Seguiremos atentos a las nuevas entregas. Un fuerte abrazo.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.