Чистые ссылки для пагинации в Drupal 7

Чистые ссылки лучше воспринимаются роботами да и смотрятся красивше. В друпале есть встроенный модуль,
позволяющий преобразовать внешний вид ссылок, но его действие почему-то не распространяется на пагинацию.
Т.е. и после включения модуля у нас остаются ссылки вида 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] у меня тот самый нужный путь.
если вместо:
$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. Большое спасибо за Ваш код, пока не нашЁл его очень сильно расстраивался из-за отсутствия адекватного модуля с подобной функциональностью.

Оставить комментарий

Ваш e-mail не будет опубликован