Cómo ocultar el título de bloques mediante código

Estoy desarrollando una distribución Drupal. Ahora mismo ando exportando grupos de configuración a features y me encuentro con un problema que he afrontado ya varias veces sin haber encontrado (hasta ahora) una solución satisfactoria.

El problema

Uso el módulo context para gestionar la disposición de los bloques. En algunos bloques necesito que el título no se muestre, y context no proporciona una manera de hacer esto (panels si), por lo que recurro a la interfaz administrativa de bloques y neutralizo el título poniendo <none>. Quizás haya maneras de hacerlo con algún módulo que extienda context, pero por lo que he buscado y preguntado no existe otra opción.

El problema con el que me encuentro es que al exportar la configuración de context a código, el override del título del bloque no se exporta. Algo lógico: la configuración de un bloque no tiene nada que ver con la disposición en context.

Una posible solución a esto es exportar la configuración del bloque con features_extra. No soy muy fan de este módulo, y además exporta mucho más de lo que necesito (todas las settings de cache, visibilidad, etc). No he encontrado otras opciones antes de recurrir a resolverlo en código.

Una solución aparatosa

Hasta ahora he venido resolviendo esta cuestión insertando registros en la tabla {block} para establecer a <none> el título de los bloques en cuestión. Es decir, haciendo db_insert() en el hook_install() del módulo/feature de turno.

Esta solución es un tanto aparatosa y nada elegante, además presenta algunas incomodidades:

  • Para los sucesivos bloques que añada a la distri debo escribir código tanto en hook_install() como en hook_update_N().
  • Debe introducirse un registro en {block} por cada theme activo en el sitio. Típicamente sólo uso un theme, pero el problema está ahí.
  • Siguiendo con el punto anterior, si cambias de theme habría que volver a intervenir en la bbdd.

Una solución basada en Drupal core

Una solución a la que he llegado se basa en hook_block_view_alter(), un hook disponible a partir de Drupal 7, en combinación con la introducción de un hook propio al que he dado en llamar hook_block_hide_title(). Consiste en que cada módulo/feature que quiera ocultar el título de bloques de los que es responsable implemente hook_block_hide_title(), devolviendo los bloques que quiere ocultar. Por otra parte, en el perfil de instalación hago una implementación de hook_block_view_alter() que recolecta los bloques declarados mediante hook_block_hide_title() y si es el caso, se ventila el título.

A continuación, un ejemplo del código en cuestión. He usado para el ejemplo un perfil de instalación que se llama FOO y un módulo que se llama BAR.

Implementación de hook_block_view_alter() en FOO.profile
  1. /**
  2.  * Hide the title of some blocks.
  3.  */
  4. function FOO_block_view_alter(&$data, $block) {
  5.   static $blocks = FALSE;
  6.   if (!$blocks) {
  7.     $blocks = module_invoke_all('block_hide_title');
  8.   }
  9.   if (isset($blocks[$block->module]) && in_array($block->delta, $blocks[$block->module])) {
  10.     $data['subject'] = '';
  11.   }
  12. }
Implementación de hook_block_hide_title() en BAR.module
  1. /**
  2.  * Implements hook_block_hide_title().
  3.  */
  4. function BAR_block_hide_title() {
  5.   return array(
  6.     'menu' => array('menu-footer-1', 'menu-footer-2', 'menu-footer-3'),
  7.     'search' => array('form'),
  8.     'system' => array('main-menu'),
  9.   );
  10. }

Como se ve es una implementación bastante sencilla, a poco que conozcas los hooks de Drupal. Sólo tengo dos cosas que añadir:

Lo primero es aclarar que, dado que un perfil de instalación es equivalente a un módulo ambos hooks pueden definirse tanto en un perfil como en un módulo. Dado que estoy construyendo una distribución, opto por implementar hook_block_view_alter() en el perfil de instalación pues es el lugar más apropiado para ubicar este código del que se benefician todos los módulos/features a medida .

Lo segundo, comentar que al igual que ocurre con hook_form_alter(), el hook utilizado viene acompañado de un hook que sólo se invoca para un bloque determinado: hook_block_view_MODULE_DELTA_alter(),.. pero quien intente utilizarlo se topará con este bug: #1076132: hook_block_view_MODULE_DELTA_alter fails with menu blocks.

Rendimiento

Dado que es una preocupación habitual, toca decir algo sobre cómo afecta esta solución al rendimiento. Aquí van algunas consideraciones a ojo:

  • El código a ejecutar por petición es mínimo, y es compatible con las opciones de cacheo de bloques de Drupal, con lo cual el impacto es prácticamente inexistente.
  • Existe una mejoría de rendimiento con respecto a la solución inicial (lo de poner <none> en el título del bloque), aunque es prácticamente inapreciable: la consulta a la base de datos devolverá menos resultados.

Por lo visto, no afecta ni mejora el rendimiento de una manera considerable pero ya que estamos bordeando el tema del rendimiento y bloques, pero quizás aún se se le puede sacar al asunto un poquito más de jugo... para esto voy a contar primero una pequeña historia, y luego vuelvo al asunto del rendimiento.


¿Sabías que?

Una gran mejora de Drupal 7 que ha pasado de puntillas es que el módulo block no es obligatorio. ¿Sorprendente? ¿Impactante? Pues sí,... no es obligatorio ni necesario, de hecho seguramente llevas tiempo reemplazándolo completamente con panels o context para el posicionamiento en regiones y tal vez con bean o boxes para crear bloques a medida (yo uso y recomiendo bean pero esa es otra historia).

Context es capaz de pintar bloques sin necesidad de que el módulo block esté activo, y por su parte bean es capaz de funcionar sin block activo, aunque por ahora requiere de este parche: #1859450: Drop dependency on block.module.


Volviendo al tema, y para ir terminando.... si desactivas el módulo block te ahorras entre otras cosas (block_page_build()) algunas consultas innecesarias a la base de datos. De hecho te ahorras un pico considerable, que es de largo mucho más costoso que la solución que he descrito en este post.

¿Dudas? ¿Sugerencias? ¿Ideas? ¿Conoces otra manera de resolver el problema inicial? Deja un comentario!