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.
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:
Y la página de este tipo de contenido deberá incluir:
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:
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>';
?>
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();
}
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);
?>
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...