Piège d’un render_array contenant #markup

Arg, Drupal m’a bien eu ! Je viens de passer 2 heures à comprendre pourquoi mon admin_menu n’apparaissait plus sur mon site, bien que connecté.

Et j’ai découvert qu’il fallait faire gaffe aux render Arrays contenant une clé « #markup ».

Si le tableau contient une clé #markup, il aura beau avoir d’autres clés contenant des fils (clés non préfixées par #, qui sont donc considérées comme des « children »), les fils ne seront pas « rendus ».

 

Pourquoi ?

#markup entraine l’ajout d’un pre_render, qui définit la clé #children avec le contenu HTML.

Si la clé #children n’est pas vide, les fils ne seront pas rendus, CQFD.

 

Solution

Ajouter une sous-clé avant le #markup :

Exemple :

Ne pas écrire :

$vars['page']['page_bottom']['#markup'] = 'mon code HTML';

Mais

$vars['page']['page_bottom']['sous_cle']['#markup'] = 'mon code HTML';

Explication en détails

Cf common.inc : fonction drupal_render

if (isset($elements['#markup']) && !isset($elements['#type'])) {
    $elements['#type'] = 'markup';
}

d’abord le tableau $elements est enrichi d’un #type=’markup’

if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
    $elements += element_info($elements['#type']);
  }

Ensuite, l’invocation des hook_element_info ajoutera #pre_render au tableau $elements.

function drupal_pre_render_markup($elements) {
  $elements['#children'] = $elements['#markup'];
  return $elements;
}

Enfin, la clé #children est remplie par le pre_render_markup.
Si on regarde un peu plus bas dans la fonction drupal_render, on remarque que si #children est vide et seulement si il est vide, alors on fait le rendu des fils.

if ($elements['#children'] == '') {
    foreach ($children as $key) {
      $elements['#children'] .= drupal_render($elements[$key]);
    }
  }