Чистые ссылки лучше воспринимаются роботами да и смотрятся красивше. В друпале есть встроенный модуль,
позволяющий преобразовать внешний вид ссылок, но его действие почему-то не распространяется на пагинацию.
Т.е. и после включения модуля у нас остаются ссылки вида http://www.site.ru/alias/?page=1 вместо http://www.site.ru/alias/1.
Итак, как сделать чистые ссылки в пагинации (drupal 7)?
К счастью, в друпале есть две замечательные функции, которые позволяют генерировать "красивые" адреса для динамических страниц: это хуки hook_url_inbound_alter() и hook_url_outbound_alter().
ВАЖНО: хук hook_url_inbound_alter() вызывается в функции drupal_get_normal_path - там идет перебор модулей. Поэтому, чтобы воспользовоться этим хуком, нужно написать небольшой модуль, в файле template.php он работать не будет.
Для начала преобразуес ссылки на страницах сайта alias/?page=1 к виду alias/1. Это делается с помощью хука
hook_url_outbound_alter(). Хук вызывается при генерации текущей страницы и меняет адреса ссылок, которые генерируются вызовом функции url().
function mymodule_url_outbound_alter(&$path, &&$options, $original_path) {
if(isset($options['query']['page'])) {
$path = drupal_get_path_alias($path).'/'.($options['query']['page']+1);
$options['alias'] = $path;
$options['query'] = array();
}
}
Надо отметить, что нумерация страниц в друпале довольно странная: когда пользователь нажимает на страницу 2,
он переходит по адресу alias/?page=1, что, на мой взгляд, как-то не логично. Поэтому в скрипте выше к номеру страницы добавлено +1: $options['query']['page']+1.
Итак, ссылки на страницы пагинации у нас стали красивые, но, если перейти по такой ссылке, у нас отобразится 404-ая страница. Чтобы по адресу alias/1 отображался контент страницы alias/?page=1, нужно задействовать второй хук:
function mymodule_url_inbound_alter(&$path, $original_path, $path_language) {
global $_GET;
$parts = explode('/', $original_path);
if( isset($parts[1]) && ctype_digit($parts[1]) && $parts[0] != 'node' ) {
$path_info = path_load(array('alias' => $parts[0]));
$path = $path_info['source'];
$_GET['page'] = $parts[1] - 1;
}
}
Теперь всё работает. Но страницы вида alias/?page=1 по-прежнему существуют, что не очень хорошо с точки зрения СЕО. Лучше сделать переадресацию:
if( isset($_GET['page']) ) {
$goto_path = $original_path.'/'.($_GET['page']+1);
drupal_goto($goto_path,array(),301);
}
Или же можно добавить в .htaccess:
RewriteCond %{QUERY_STRING} ^page=([0-9]+) [NC]
RewriteRule .* %{REQUEST_URI}/%1/? [R=301,L]
Всё вместе (вариант с переадресацией с помощью drupal_goto):
function mymodule_url_inbound_alter(&$path, $original_path, $path_language) {
global $_GET;
$parts = explode('/', $original_path);
if( isset($_GET['page']) ) {
$goto_path = $original_path.'/'.($_GET['page']+1);
drupal_goto($goto_path,array(),301);
} else {
if( isset($parts[1]) && ctype_digit($parts[1]) && $parts[0] != 'node' ) {
$path_info = path_load(array('alias' => $parts[0]));
$path = $path_info['source'];
$_GET['page'] = $parts[1] - 1;
}
}
}
function mymodule_url_outbound_alter(&$path, &&$options, $original_path) {
if(isset($options['query']['page'])) {
$path = drupal_get_path_alias($path).'/'.($options['query']['page']+1);
$options['alias'] = $path;
$options['query'] = array();
}
}
ВАЖНО! Если у Вас включены какие-то дополнительные модули, например, Search API, то этот простейший вариант нужно немного скорректировать. В админке такой модуль пагинацию тоже поломает. Моей задачей было сделать чистые ссылки в пагинации на страницах терминов таксономий. Поэтому область применения модуля ограничим функцией:
function paginated_pages() {
$default_list = array();
$vids = db_query("SELECT vid FROM {taxonomy_vocabulary}")->fetchCol();
foreach ($vids as $vid) {
if($vid!=1){ // 1-ый словарь - это словарь тегов, его не учитываем
$tids = db_query("SELECT tid FROM {taxonomy_term_data} WHERE vid = :vid", array(':vid' => $vid))->fetchCol();
foreach ($tids as $tid) {
$alias = drupal_get_path_alias('taxonomy/term/'.$tid);
$default_list[]= $alias;
}
}
}
return $default_list;
}
Эта функция получает массив алиасов всех терминов. Наши преобразования будем запускать, только если текущий адрес соответствует какому-либо элементу массива:
function mymodule_url_inbound_alter(&$path, $original_path, $path_language) {
global $_GET;
$parts = explode('/', $original_path);
if( isset($_GET['page']) && in_array($original_path, paginated_pages()) ) {
$goto_path = $original_path.'/'.($_GET['page']+1);
drupal_goto($goto_path,array(),301);
} else {
if( isset($parts[1]) && ctype_digit($parts[1]) && in_array($parts[0], paginated_pages()) ) {
$path_info = path_load(array('alias' => $parts[0]));
$path = $path_info['source'];
$_GET['page'] = $parts[1] - 1;
}
}
}
function mymodule_url_outbound_alter(&$path, &&$options, $original_path) {
if( isset($options['query']['page']) && in_array(drupal_get_path_alias($path), paginated_pages()) ) {
$path = drupal_get_path_alias($path).'/'.($options['query']['page']+1);
$options['alias'] = $path;
$options['query'] = array();
}
}
вот с этой строчкой явно что-то не так… не понимаю, почему не работает…
$path_info = path_load(array(‘alias’ => $parts[0]));
А в $parts[0] у Вас что?
В $parts[0] у меня тот самый нужный путь.
если вместо:
$path_info = path_load(array(‘alias’ => $parts[0]));
$path = $path_info[‘source’];
использую:
$path = $parts[0];
то всЁ работает.
я что-то делаю не так?
Что не так — трудно сказать. У меня работало то, что написано.
Может, какие-то настройки не те или версия друпала.
Кстати, после использования этого кода, во views пропадает возможность использовать token’ы для изменения title’а(это для того, чтобы в тайтле добавлялся номер страницы пэйджера).
Обсуждение здесь:
https://www.drupal.org/node/224262
Решается применением патча из сообщения #48.
p.s. Большое спасибо за Ваш код, пока не нашЁл его очень сильно расстраивался из-за отсутствия адекватного модуля с подобной функциональностью.