Drupal views : Glossaire sur un bloc

Portrait de titouille

Le module "Views" de drupal permet de faire beaucoup de choses, mais dans certains cas, la solution s'avère compliquée à trouver et à mettre en oeuvre.

Hier, j'ai travaillé sur le mode "glossary". Le but était de mettre en place un bloc affichant une liste d'éléments de la manière suivante :

  1. Afficher X éléments par page, avec système de pagination standard récupérant les informations via ajax
  2. Au sommet de cette liste, afficher un système de pagination de type "glossaire" alphanumérique

En gros : au sommet de mon bloc, une liste affichant les éléments ALL, 0, 1, ..., 9, A, B, C, ..., X, Y, Z. En dessous, la liste des éléments récupérés, par lot de 20 items, avec un pager standard permettant de voir les 20 éléments précédents / suivants.

Si je cliques sur un lien du glossaire, par exemple la lettre A, la liste est rechargée (via ajax) avec les éléments commençant par le numéro ou la lettre cliquée, dans mon exemple, tous les éléments dont le nom commence par 'a'. Si il existe plus de 20 éléments dans la liste récupérée, un pager standard apparait et me permet de naviguer entre les différentes pages.

Le mode glossaire est assez spécifique. J'ai tout d'abord tenté de partir de la vue qui contenait mon bloc pour y rajouter un visuel de type "attachment" et de faire le nécessaire (en m'inspirant de la vue par défaut "glossary"). Malheureusement, bien que le principe fonctionnait lors de la prévisualisation depuis le mode édition de ma vue, il cessait de fonctionner dès que je plaçait mon bloc sur une page. Les liens du glossaire pointaient tous sur la racine web "/" alors qu'ils devaient pointer sur "/{$letter}" (/all, /0, /1, ..., /9, /a, /b, /c, ..., /x, /y, /z).

Je sais que les vues sont dès fois assez complexes à mettre en place, et leur fonctionnement n'est pas toujours simple. J'avais déjà été confronté à un problème au niveau d'une vue de type "calendar". Si on essaye de créer une vue calendrier à partir d'une vue vierge, ça ne fonctionne pas... La vue calendrier embarque certaines fonctionnalités qui ne sont pas mises d'office dans une vue vierge. Il est nécessaire de cloner la vue calendrier par défaut, puis d'y appliquer les modifications nécessaires pour que ça fonctionne correctement.

Connaissant cette astuce, j'ai alors tenté de cloner la vue glossary pour faire des tests, et d'y créer un nouveau display de type "bloc", puis de la modifier pour qu'elle affiche les données nécessaires. Et là, bien entendu, ça fonctionnait. Je me suis donc dis que la solution se situait dans le clonage. Comme je n'avais pas besoin de display "page", j'ai alors supprimé celui-ci dans ma vue de test. C'est la que la véritable solution a pointé le bout de son nez. Après avoir supprimé le visuel de type "page", mon bloc a cessé de fonctionner correctement, ce qui m'a mis la puce à l'oreille.

Il n'est donc pas nécessaire de cloner la vue "glossary". En réalité, le glossaire a besoin d'un visuel "page" pour aller récupérer les informations nécessaires. Sans ça, il est incapable de recréer les liens proprement car il n'a pas de "cible" pour faire la récupération des données. Le visuel "attachment" doit être attaché ET à la page, ET au bloc (dans la partie "Attachment settings") et ne pas hériter des arguments (toujours dans la même section).

Au final, le glossaire fonctionne très bien. Le seul problème restant était de pouvoir afficher un lien "ALL", tous les numéros et tous les caractères alphabétiques. En l'état, mon glossaire n'affichait que les numéros et lettres dont il trouvait des correspondances.
Là encore, la solution n'était pas forcément simple. Dans l'attachment, vu que l'argument est en mode "glossaire", il propose un "style" pour l'affichage. Le problème, c'est que ce style prend le dessus sur les templates proposés par Views dans la partie "Theme:Informations" de la section "Basic settings". Donc impossible de surdéfinir le template "Display style" pour y intégrer des modifications.

La seule solution que j'ai trouvée est de surdéfinir le template "Display output" pour y rajouter le code nécessaire pour arriver à mes fins.

J'ai remplacé le simple

print $rows;

par

// get current selected letter, set it to "all" if empty
$current_letter = arg(1);
if( empty( $current_letter ) ) $current_letter = "all";
 
// set the url to retrieve data via ajax
$ajax_url = "path-of-my-page-display";
 
// set the list of all elements to display as links
$letters = array( "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" );
 
// get list of existing numbers / letters by querying the database
$res = db_query( "SELECT UCASE( SUBSTR(n.node, 1, 1)) AS letter FROM {node} n WHERE n.type = "blog" GROUP BY letter ORDER BY letter" );
 
// put all numbers / letters in an array
$existing_letters = array();
if( db_affected_rows( $res ) > 0 ) {
  while( $result = db_fetch_object( $res ) ) {
    $existing_letters[] = $result->letter;
  }
}
 
// start printing the alphanumerical list
print '<ul class="pager alpha-pager">';
 
// first, add the "ALL" link
$nav[] = '<li class="pager-item pager-all'.($current_letter == "all" ? " current-pager-item" : "").'"><a href=' . url( $ajax_url . "/all", array( "absolute" => TRUE ) ) . '>ALL</a></li>';
 
// print each number / letter, as link if it exists in the $existing_letters array,
// as simple text if it doesn't exists in the $existing_letters array
foreach( $letters as $letter ) {
  if( in_array( $letter, $existing_letters ) ) {
    $url = url( $ajax_url . "/" . strtolower( $letter ), array( "absolute" => TRUE ) );
    $output = '<li class="pager-item'.($current_letter == strtolower( $letter ) ? " current-pager-item" : "").'">';
    $output .= '<a href=' . $url . '>' . $letter . '</a>';
    $output .= '</li>';
  }
  else {
    $output = '<li class="pager-item no-result">' . $letter . '</li>';
  }
  // add each result in nav array
  $nav[] = $output;
}
 
// print nav array by imploding it with separator
print implode('', $nav);
 
// close listing
print '</ul>';

Et le tour était joué.
Le must aurait été de pouvoir afficher un glossaire du type ALL, 0-9, A, B, C, ..., X, Y, Z. Malheureusement, à partir du moment ou on passe en mode glossaire, il n'est plus possible de spécifier dans l'argument qu'on voudrai autoriser de multiples arguments. Si ça avait été possible, on aurait pu supprimer le listing numérique dans le tableau $letters et mettre un lien du type

$nav[] = '<li class="pager-item pager-all'.($current_letter == "0,1,2,3,4,5,6,7,8,9" ? " current-pager-item" : "").'"><a href=' . url( $ajax_url . "/0,1,2,3,4,5,6,7,8,9", array( "absolute" => TRUE ) ) . '>0-9</a></li>';

juste après l'ajout du lien ALL pour spécifier les arguments multiples, mais je n'y suis malheureusement pas arrivé.

Néanmoins, je pense que l'astuce "un display de type 'page' est nécessaire" est déjà une bonne chose en soi Wink