Cargar páginas de una vista dentro un panel con ajax

  • 25 Jun 2019
  • Drupal 7

Para los usuarios de panels resulta seguramente muy habitual el hecho de cargar vistas o view panes dentro un panel. La interoperabilidad entre ambos módulos está muy bien desarrollada. En vistas que representan un detalle de un node o donde se muestra un ranking, por ejemplo de artículos más leidos o votados.

Parte de la interoperabilidad entre Panels y Views

Personalmente suelo aprovechar la bondad de panels y views para presentar resultados de búsqueda, integrando además search-api. En este caso, a medida que las vistas resultan más extensas y sobre todo más complejas el requerimiento de ajax pasa a ser menos opcional. Y acá encontramos un problema. Cuando una vista con ajax es incluida en un panel, los parámetros de configuración realizados en panels no son considerados por ajax. El resultado es que perdiendo los filtros y la posición actual la paginación (además del número de elementos por página) no devuelve los resultados que debería.

El escenario es el siguiente:

  • un tipo de contenido con título, tags, id de autor y fechas.
  • un índice con search-api para este tipo de contenido
  • una vista de tipo search-api con filtros expuestos

Y la página de este tipo de contenido deberá incluir:

  • los datos propios del node (campos)
  • un resultados de los artículos similares (a través de una vista search-api)  
  • y un resultado de artículos con el mismo id de autor

Solución

Una solución para resolver el problema del ajax en las vistas podría ser creando un página para cada uno de los resultados que devuelva el html de la busqueda con sus filtros. Por ejemplo, para la vista de artículos similares, deberíamos tener primero una vista que dado un nid devuelva una lista de los artículos relacionados. Luego, con un poco de PHP creamos un menu_item que responda por ejemplo a la url related-articles/% (donde % vendría a ser el id del node). Lo mismo para la segunda vista, la que mostrará los artículos escritos por el mismo autor. Necesitaremos en este caso una vista que dado un nombre o id de autor devuelva todos sus artículos paginados. Una vez creada la vista, con otro poco de PHP (y también JS) tendremos que crear otro menu_item parecido al primero, por ejemplo, mismo-autor/%.

Resumiendo, la solución se basa en:

  • Crear una vista con botones de paginación personalizados en el pie de página.
  • Agregar la vista al panel del node.
  • Crear una página que devuelva el resultado de la búsqueda dando el id del node y el número de página deseado.
  • Dar vida a los botones personalizados con JS.

Hasta la vista

Hasta en la vista vamos a necesitar un poco de PHP. Pero solo para agregar los botones que correspondan en el pie de página. Se podría también usar la paginación standard y luego adaptarla con JS.. pero hoy la solución es esta: Agregamos un campo de tipo global: text area en el footer con este código:

<?php 

$view = views_get_current_view();

$current_page = isset($view->query->pager->current_page) ? $view->query->pager->current_page : 0;
$next_page = $current_page + 1;
$prev_page = $current_page - 1;

$path = current_path();
$path_params = explode("/", $path);
if (count($path_params) > 1 && is_numeric($path_params[1])) {
  $nid = $path_params[1];
}

print '<div class="text-center">';
if ($current_page > 0) {
  print '<a class="btn btn-lg btn-primary btn-ajax view-ajax-related-content" data-nid="'.$nid.'" data-page="'.$prev_page.'">Prev <span class="glyphicon glyphicon-refresh"></span></a> ';
}
print '<a class="btn btn-lg btn-primary btn-ajax view-ajax-related-content" data-nid="'.$nid.'" data-page="'.$next_page.'">Next <span class="glyphicon glyphicon-refresh"></span></a> ';
print '</div>';

?>

Un poco de PHP

Ya tenemos una vista. Ahora vamos a ver el código del módulo personalizado para devolver el resultado:

function custom_functions_menu() {
  $items['related-content/%/%'] = array(
    'page callback' => 'custom_functions_related_content',
    'page arguments' => array(1,2),
    'type' => MENU_CALLBACK,
    'access arguments' => array('access content'),
  );
  return $items;
}

function custom_functions_related_content($nid, $page) {

  $node = node_load($nid);

  $tids = array();

  for ($n = 0; $n < count ($node->field_image_keywords['und']); $n++ ) {
    $tids[] = $node->field_article_keywords['und'][$n]['tid'];
  }

  $terms = taxonomy_term_load_multiple($tids);
  $terms_name = "";
  foreach ($terms as $term) {
    $terms_name .= $term->name." ";
  }
  $terms_name = trim($terms_name);

  $view_name = "related_content";
  $display = "panel_pane";
  $items_per_page = 12;

  $view = views_get_view($view_name);
  $view->set_display($display);
  $view->set_items_per_page($items_per_page);
  $view->current_page = $page;

  $filter_fulltext = $view->get_item($view->current_display, 'filter', 'search_api_views_fulltext');
  $filter_nid = $view->get_item($view->current_display, 'filter', 'nid');

  $filter_fulltext['value'] = $terms_name;
  $filter_nid['value'] = $nid;

  $view->set_item($view->current_display, 'filter', 'search_api_views_fulltext', $filter_fulltext);
  $view->set_item($view->current_display, 'filter', 'nid', $filter_nid);

  $view->pre_execute();
  $view->execute();

  return $view->render();
}

Cargar la vista dentro Panel con PHP

Una vista dentro panels se puede cargar con algunos clicks, pero también se puede hacer con PHP. El código es muy parecido al que usamos en el módulo personalizado a excepción de las variables cargadas con sus respectivos substitutions.

<?php

  $view_name = "related_content";
  $display = "panel_pane";
  $items_per_page = 12;

  $view = views_get_view($view_name);
  $view->set_display($display);
  $view->set_items_per_page($items_per_page);
  
  $filter_fulltext = $view->get_item($view->current_display, 'filter', 'search_api_views_fulltext');
  $filter_nid = $view->get_item($view->current_display, 'filter', 'nid');
 
  $filter_fulltext['value'] = str_replace(", ", " ", "");
  $filter_nid['value'] = 507;

  $view->set_item($view->current_display, 'filter', 'search_api_views_fulltext', $filter_fulltext);
  $view->set_item($view->current_display, 'filter', 'nid', $filter_nid);

  print $view->preview($display);

?>

Javascript para los botones

A los botones hemos agregado un par de atributos personalizados (data-nid y data-page) que utilizaremos para construir la url que load() se ocupará de pedir.

var viewAjax = $(".view-ajax-related-content");
  if (viewAjax.length > 0) {
    $(document).on('click', ".view-ajax-related-content", function(e){
      e.preventDefault();
      $this = $(this);
      var url = "/related-content/"+$this.data('nid')+"/"+$this.data('page');
      var target = $('.view-related-content.view-display-id-default');
      $(target).load(url + " .view-id-related_content", function(){
        $('html, body').animate({
          scrollTop: $(target).offset().top
        }, 300);
        $this.find('.glyphicon').removeClass('spinning');
      })
    })
  }

Espero no haber olvidado nada...