При переносе одного из сайтов на 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().