WordPress и управление rewrite правилами

3 февраля 2012

При переносе одного из сайтов на WordPress, также потребовалось перенести имеющийся каталог продукции. При этом было необходимо не только использовать ЧПУ, но и постараться сохранить прежние адреса элементов каталога.

Каталог двухуровневый и адресация имеет следующий вид:

/тип_продукции/прозвиодитель/имя_продукта

Типов продукции было всего два и в том самописном движке, с которого сайт портировался, каждый тип обслуживался своим скриптом. Если переносить данную модель на WordPress, то логично сделать две страницы, slug имена которых будут соответствовать типам продукции, а страницу производителя и конечного продукта генерировать налету, предварительно не создавая для них отдельных страниц в WP.

У WordPress есть механизм, позволяющий расширять стандартные правила роутинга, основанный на функции add_rewrite_rule(). Более того, в Сети хватает заметок о том, как им пользоваться и что необходимо предпринять, чтобы добиться желаемого результата. Но не одна из них, как оказалось, не описывает проблему полностью. Советы и решения не работают без напильника или описывают неочевидные вещи.

И так, решить задачу можно двумя способами:

  • локально, то есть через используемый шаблон и файл functions.php этого шаблона;
  • разработкой плагина, что несколько сложнее, но идеологически правильно.

Задача решается на WordPress версии 3.3.1

Независимо от того, какой способ будет выбран, принцип работы одинаковый. Описываем в файле плагина или в файле functions.php функцию, например, с именем addRewrite(). В теле функции пишем следующий код:

1
2
3
function addRoutes() {
    add_rewrite_rule('(tyres|discs)/([a-z]+)/?$', 'index.php?pagename=$matches[1]&producer=$matches[2]', 'top');
}

Функция add_rewrite_rule() добавляет правило роутинга или рерайта, если выражаться терминологией, принятой WP. В качестве первого параметра передается регулярное выражение, описывающее допустимое значение uri. Второй параметр задает перенаправление.

В данном конкретном случае, добавляемое правило будет работать для страницы, слаг имя (slug) которой или tyres (каталог шин), или discs (каталог дисков). Второй сегмент, описанный шаблоном ([a-z]+), будет содержать имя производителя. В рамках решения задачи, которую я упоминал вначале, должен быть описан и третий сегмент, где будет передано наименование конкретного товара из каталога. Но я опустил данную секцию, дабы не загромождать регулярное выражение.

Нюансы использования add_rewrite_rule():

  • правило не должно начинаться со слеша. Как вы можете видеть по коду примера, перед (tyres|discs) слеш не стоит;
  • нумерация элементов в массиве $matches начинается с 1 (единицы), а не с нуля;
  • функция может быть вызвана несколько раз, каждый раз добавляя новое правило. Третий параметр задает позицию добавляемого правила в общем стеке правил. В данном примере они добавляются в начало. Значение умолчания – bottom.

Теперь, с помощью add_action() вешаем наш хук на «событие» init:

1
add_action('init', 'addRoutes');

Это пример чистого использования WordPress функции add_rewrite_rule(). В таком виде работать ничего не будет.

Во-первых, список переменных, которые WP готов принять, ограничен и переменная producer, которая используется в переадресации, не будет принята и, как следствие, не будет доступна в плагине или шаблоне.

Для исправления ситуация необходимо задействовать функцию add_rewrite_tag(), которая регистрирует дополнительные переменные, передаваемые скрипту.

1
add_rewrite_tag('%producer%', '([^&]+)');

В первом параметре передается имя переменной, а во втором формат значения, задаваемый регулярным выражением.

Нюансы использования add_rewrite_tag():

  • вызов функции должен произойти до процесса инициализации или во время него. Ситуация, когда функция вызывается внутри хука, повешенного на событие init, наиболее удобна;
  • первый параметр должен принимать имя, которое обернуто знаками % (процент).

Еще одна особенность, о которой почти нигде не говорится, заключается в том, что WordPress кэширует правила переадресации. Кэшируются они в базу данных, в таблицу wp_options. Именем опции является rewrite_rules. В качестве значения хранится сериализованный массив.

Если кэш не сбросить, то ваше новое правило работать не будет. Сбросить кэш можно тремя способами:

  • создать новую страницу или изменить существующую в панели WordPress;
  • в панели WP, на странице «постоянные ссылки» пересохранить правила формирования ЧПУ;
  • вызвать функцию flush_rewrite_rules().

Функция flush_rewrite_rules() должна вызываться после того, как вы добавили свои правила.

И так, рабочий пример кода выглядит следующим образом:

1
2
3
4
5
6
7
8
function addRoutes() {
    add_rewrite_tag('%producer%', '([^&]+)');
    add_rewrite_rule('(tyres|discs)/([a-z]+)/?$', 'index.php?pagename=$matches[1]&producer=$matches[2]', 'top');
 
    flush_rewrite_rules();
}
 
add_action('init', 'addRoutes');

Если этот код засунуть в файл functions.php, то функция flush_rewrite_rules() будет вызываться при каждом обновлении страницы. Это совершенно неправильно, конечно. В рабочих проектах так делать недопустимо.

Для получения доступа к значению зарегистрированной переменной необходимо использовать объект $wp_query и его свойство query_vars.

Не пытайтесь общаться с данным объектом из файла functions.php вашей темы, так как массив $wp_query->query_vars будет пустым и свою переменную вы там не найдете. Но в теле шаблона все будет так, как и ожидается:

1
echo $wp_query->query_vars['producer'];

Выведет значение переданной переменной &producer.

Дальше, например, можно подставить это значение в SQL запрос и получить список товаров для требуемого производителя. Так делать некрасиво. Работать с моделью из представления – совсем дурной тон, но что уж поделаешь, коль в WordPress такой способ хорошо прижился и многими практикуется. В общем, это на вашей совести.

WordPress rewrite в плагине с использованием Shortcode API

Идеологически верным является написание небольшого плагина, который сделает все тоже самое, но не будет вынуждать нас писать логику в шаблоны. Самый простой вариант – реализация через WordPress Shortcode API.

В тело страницы или записи блога вставляем код shortcode, например такой [my_catalog]. В файле плагина пишем тот же самый код, что и в случае с шаблонами и добавляем к нему регистрацию нового шорткода:

1
2
3
4
5
6
7
8
9
10
11
12
13
function addRoutes() {
    add_rewrite_tag('%producer%', '([^&]+)');
    add_rewrite_rule('(tyres|discs)/([a-z]+)/?$', 'index.php?pagename=$matches[1]&producer=$matches[2]', 'top');
 
    flush_rewrite_rules();
}
 
add_action('init', 'addRoutes');
 
function catalogShortCode() {
    echo get_query_var('producer');
}
add_shortcode('my_catalog', 'catalogShortCode');

Внутри функции catalogShortCode() пишем остальную логику, где уже работаем с БД и прочее.

В целом, точно также, через shortcode, можно работать и в файле functions.php, но вариант с плагином более изящен и практичен. Если вы примете решение сменить тему оформления, вам не придется копировать код из одного шаблона в другой. А если у вас мультидоменная установка WP, то без плагина не обойтись в принципе.

Использование flush_rewrite_rules() по уму.

В документации WordPress дается правильный пример сброса кэша правил роутинга. Предлагается вот такой вариант:

1
2
3
4
5
6
7
8
9
10
function my_flush_rules(){
    // Получаем текущее состояне кэша
	$rules = get_option( 'rewrite_rules' );
 
	// Если наше правило в кэше отсутствует, сбрасываем кэш
	if ( ! isset( $rules['(project)/(\d*)$'] ) ) {
		global $wp_rewrite;
	   	$wp_rewrite->flush_rules();
	}
}

Как вы могли заметить, массив $rules является ассоциативный, где ключами служат регулярные выражения, передаваемые в качестве значения первого параметра функции add_rewrite_rule().

Категория: WordPress

Мурашов Олег

Программист и фрилансер из Санкт-Петербурга. Помимо веб-программирования, занимаюсь разработкой мобильных приложений для платформы Android. Играю на бас гитаре. Занимаюсь спортом.

Добавить комментарий