Как изменить структуру url в Wordpress

Сравнительно недавно я пытался найти информацию, как изменить структуру url в BuddyPress. Ничего не нашел, зато сам разобрался. Сразу оговорюсь, что статья не рассчитана на новичков, а скорее на тех, кто более-менее понимает, что в движке к чему, но тем не менее испытывает трудности в данном конкретном вопросе.

В общем, был один сайт на Wordpress, где мне нужно было поменять некоторые структуры URL, т.к. многие из них не совсем устраивали SEO-шника. В частности, на форумах bbPress чтобы вместо site.ru/forum/id/test-forum/ или site.ru/forum/topics/want-a-job на них можно было зайти через site.ru/forum/test-forum и site.ru/forum/test-forum/want-a-job соответственно, или хотя бы что-то похожее.

А еще нужно было убрать из URL участки /page/ и /category/, чтобы на страницы главной можно было зайти по адресам site.ru/2, site.ru/5 и т.п., а категории находились по адресам site.ru/books, site.ru/books/2 и так далее (вместо этого каличного варианта site.ru/category/library/books/page/2), но обо всём по порядку.

Обозначим задачу:
1) Как в Wordpress изменить rewrite rules?
2) Как в bbPress изменить структуру url-ов?
3) Как в BuddyPress поменять url пользователей? (здесь вообще все по-своему, и BP даже не создает собственные rewrite rules)
4) Как в WP убрать /page/ без плагинов, да и вообще удалить слэш в конце?

Изрядно покопавшись в интернете и перелопатив некоторое количество мануалов, я пришел к тому, что нужно просто-напросто добавить свои Rewrite Rules, или вообще тупо заменить всё, что там есть, на ограниченный список только нужных нам. Делается это добавлением фильтров:

add_filter( 'rewrite_rules_array','my_insert_rewrite_rules' );
add_filter( 'query_vars','my_insert_query_vars' );
add_action( 'wp_loaded','my_flush_rules' );

// Сбиваем правила flush_rules(), если наших еще нет в списке (операция ресурсоемкая, поэтому не стоит ее делать каждый раз)
function my_flush_rules(){
 $rules = get_option( 'rewrite_rules' );
 
 // достаточно проверить, есть ли в списке хотя бы одно из наших правил
 if ( ! isset( $rules['meetings/([^/]+)(/[0-9]+)?/?$'] ) ) {
 global $wp_rewrite;
 $wp_rewrite->flush_rules();
 }
}

// добавляем наши правила
function my_insert_rewrite_rules( $rules )
{
 $newrules = array();
 $newrules['forum/([^/]*)/([^/]+)/([0-9]+)/?$'] = 'index.php?topic=$matches[2]&paged=$matches[3]';
 $newrules['forum/([^/]*)/([0-9]+)/?$'] = 'index.php?forum=$matches[1]&paged=$matches[2]';
 $newrules['forum/([^/]*)/(.*)/([0-9]+)/?$'] = 'index.php?topic=$matches[2]&paged=$matches[3]';
 $newrules['forum/([^/]*)/(.*)/?$'] = 'index.php?topic=$matches[2]';
 $newrules['forum/([^/]*)/?$'] = 'index.php?forum=$matches[1]';
 $newrules['(library|books|articles|tools|projects|diagrams)/([0-9]{1,})/?$'] = 'index.php?category_name=$matches[1]&paged=$matches[2]';
 $newrules['(library|books|articles|tools|projects|diagrams)/?$'] = 'index.php?category_name=$matches[1]';
 $newrules['([0-9]{1,})/?$'] = 'index.php?&paged=$matches[1]';
 $newrules['meetings/([^/]+)(/[0-9]+)?/?$'] = 'index.php?name=$matches[1]&page=$matches[2]&post_type=event';
 
 return $newrules + $rules; // так оно добавляет наши правила к уже существующим, а еще мы можем тупо убрать все и вернуть только свой список, сделав return $newrules;
}

// добавляем переменную id, чтобы WP воспринимал ее
function my_insert_query_vars( $vars )
{
 array_push($vars, 'id');
 return $vars;
}

Но в адресах тем у нас же ещё идет сегмент с именем форума. Чтобы сделать это, я использовал плагин WP Permastructure и для топиков задал следующую структуру:
/forum/forum-name/%postname%
и для всех событий, где формировалась ссылка на топик, добавил фильтр для замены части /forum-name/, например вот:

add_filter('bbp_get_topic_permalink', 'forum_permalinks');
function forum_permalinks($permalink) {
 
 global $wpdb;
 
 $str = explode('/', $permalink);
 $str = $str[count($str)-1];
 
 $forum_name = $wpdb->get_var("SELECT f.`post_name` FROM `wp_posts` p LEFT JOIN `wp_posts` f ON (p.`post_parent` = f.`ID`) WHERE p.`post_type` = 'topic' AND p.`post_status` = 'publish' AND p.`post_name` = '{$str}'");
 
 return preg_replace('|/forum-name/|', '/'.$forum_name.'/', $permalink);
}

Это, пожалуй, то еще ad hoc решение, но главное, что работает, как надо, хотя если вы хотите сделать у себя всё красиво и универсально, то в вордпрессе есть такое понятие, как rewrite tags (они работают по тому же принципу), и вам стоит поковыряться со всеми этими %postname%, %category% и подобными (например, можно добавить свое %forumname% какое-нибудь), но это уже без меня. Могу разве что ссылку подкинуть для освоения:
http://wp.tutsplus.com/tutorials/creative-coding/the-rewrite-api-the-basics/
читать под заголовком "Add rewrite tag"

В BuddyPress так и вообще шляпа, как оказалось, он не создает рерайтов и сам отлавливает обращения.
Покопаться можно в /wp-content/plugins/buddypress/bp-core/bp-core-catchuri.php
Там обрабатываются и перехватываются пути.
Красивее всего, конечно же, использовать фильтр, конкретно здесь будет bp_uri. Пример:

add_filter('bp_uri', 'my_uri_filter');
function my_uri_filter($value) {
 
 // поскольку я хочу, чтобы в пагинации вместо /members/?upage=2 было приятное на вид /members/2, то делаем следующее
 if ( preg_match('#^/members/([0-9]+)$#', $value, $matches) ) {
 $_REQUEST['upage'] = $matches[1];
 $value = '/members';
 }
 
 // здесь я хочу сократить ссылки вида /members/vasiliy/forums/topics до вида /members/vasiliy/topics и т.п.; если вам это не нужно, просто не используйте
 if ( !preg_match('#/forums/(topics|replies|favorites|subscriptions)/?$#', $value) ) {
 $value = preg_replace( array('|/topics$|', '|/replies$|', '|/favorites$|', '|/subscriptions$|'), array('/forums/topics', '/forums/replies', '/forums/favorites', '/forums/subscriptions'), $value);
 }
 
 // если используется пагинация, добавим переменную paged
 if ( preg_match('#/(topics|replies|favorites|subscriptions)/([0-9]+)$#', $value, $matches) ) {
 $_GET['paged'] = $matches[2];
 $value = str_replace('/'.$matches[1].'/'.$matches[2], '/forums/'.$matches[1].'/page/'.$matches[2], $value);
 }
 
 return $value;
}

Как в Wordpress убрать родительскую категорию из url? Тут я все решил грубой силой ;)
В /wp-includes/category-template.php в функции get_category_parents() тупо написал return '';
Та-да!

Чтобы убрать /category/ из URL, я использовал следующий фильтр (каюсь, украл из какого-то плагина и модифицировал под свои нужды, т.к. не хотел устанавливать 200 плагинов, а наоборот - сделать всё в одном):

add_filter("category_link", "filter_category_link");
function filter_category_link ($termlink)
{
 if (preg_match ("/\?cat=/", $termlink))
 return $termlink;
 
 $str = explode("/", $termlink);
 
 $myslug = $str[count($str)-1];
 
 // если есть сегмент category
 preg_match ("/category.*?".$myslug."/", $termlink, $result);
 
 $str = explode("/", $result[0]);
 
 $termlink = preg_replace ("/category.*?".$myslug."/", $myslug, $termlink);
 
 return $termlink; 
}

Еще одной проблемой стала пагинация. Когда я вводил site.ru/2, меня перекидывало на какой-то пост, имя которого начинается с двойки: site.ru/2011-04-17blablabla. Мне, конечно, такое поведение нафиг не сдалось, и я, не сильно заморачиваясь, отрубил redirect_canonical() следующим странным образом:

в /wp-includes/canonical.php в функции redirect_canonical() в нужном месте дописал

$do_redirect = false;

Да, чуть не забыл, чтобы убрать /page/ из url, как вы уже поняли, я снова занимаюсь фильтрацией. Не нужно бездумно копировать код, это просто пример:

add_filter('bbp_get_reply_url', 'remove_page_base');
function remove_page_base($permalink) {
 
 if ( preg_match('|/page/([0-9]+)/?(#post\-[0-9]+)?$|', $permalink, $matches) ) {
 
 $permalink = str_replace('/page/'.$matches[1], '/'.$matches[1], $permalink);
 }
 
 return $permalink;
}

Если есть поправки, советы, вопросы, да мало ли что, смело пишите в комментариях! Не обещаю, что смогу потратить время на решение ваших проблем, но возможно смогу что-то подсказать, и как минимум объяснить вышенаписанное, если оно вами недопонято! Удачи вам)

 Жду с нетерпением
ваших комментариев!
 

Подписаться на RSS

Вы можете нажать "подписаться", чтобы следить за моими новостями!
Так вы всегда будете в курсе появления новостей на сайте =)
О том, что такое RSS можно прочитать здесь.

Подписаться

Подписаться на Twitter

Я специально зарегистрировался в Твиттере, чтобы вы могли следить за обновлениями на сайте =)

Подписаться

Envato marketplace А эти люди занимаются прокатом карнавальных костюмов и масок в Минске. К слову, я им делал сайт.