Как создать структуру постоянных ссылок с пользовательскими таксономиями и типами записей вида base-name/parent-tax/child-tax/custom-post-type-name
Я искал ответ на этом сайте и в Google, но ничего не нашел. По сути, я хочу сделать точно то, о чем спрашивается в этом посте, но мне нужна иерархическая таксономия. Ответ, предложенный в том посте, работает отлично, но только с одноуровневой таксономией. Возможно ли сделать то, что я хочу? Я перепробовал множество вариантов, но ничего не работает, в лучшем случае получаю правильные постоянные ссылки, но они выдают 404.
Для наглядности, вот что я хочу получить:
/basename/ - в идеале страница, но думаю, это вызовет коллизию постоянных ссылок
/basename/top-cat/ - архив родительской пользовательской таксономии
/basename/top-cat/child-cat/ - архив дочерней пользовательской таксономии
/basename/top-cat/child-cat/grandchild-cat/ - архив таксономии внука
/basename/top-cat/child-cat/grandchild-cat/post-name/ - запись моего пользовательского типа записи
Это прекрасно работает со встроенными записями и категориями, как сделать это с пользовательскими таксономиями и типами записей? Я знаю, что нужно использовать 'rewrite' => array( 'slug' => 'tax-name', 'with_front' => true, 'hierarchical' => true ),
для получения иерархических ярлыков, что нормально работает на страницах архивов, но записи пользовательского типа выдают 404. Если убрать часть 'hierarchical' => true
, то записи работают, но теряется иерархия URL (работает только /basename/grandchild-cat/post-name/).
Есть ли решения? Большое спасибо, это сводит меня с ума уже около 3 недель.
После объединения нескольких частей из других ответов у меня получилось заставить это работать! Вот решение для тех, кто тоже борется с этой проблемой:
Эта статья и эта мне очень помогли, так что спасибо авторам.
Примечание: весь этот код, плюс ваш начальный код регистрации произвольного типа записи и таксономии, должен быть помещен в файл functions.php
.
Сначала правильно укажите ярлыки при определении ваших произвольных типов записей и таксономий: для произвольного типа записи это должен быть basename/%taxonomy_name%
, а для таксономии просто basename
. Не забудьте также добавить 'hierarchical' => true
в массив rewrite таксономии, чтобы получить вложенные термины в URL. Также убедитесь, что query_var
установлен в true
в обоих случаях.
Вам нужно добавить новое правило перезаписи, чтобы WordPress понимал структуру вашего URL. В моем случае часть URL произвольного типа записи всегда будет 5-м сегментом URL, поэтому я определил правило соответствия соответствующим образом. Обратите внимание, что вам, возможно, придется изменить это, если вы используете больше или меньше сегментов URL. Если у вас будут различные уровни вложенных терминов, вам нужно будет написать функцию для проверки, является ли последний сегмент URL произвольным типом записи или термином таксономии, чтобы знать, какое правило добавить (спросите меня, если вам нужна помощь в этом).
add_filter('rewrite_rules_array', 'mmp_rewrite_rules');
function mmp_rewrite_rules($rules) {
$newRules = array();
$newRules['basename/(.+)/(.+)/(.+)/(.+)/?$'] = 'index.php?custom_post_type_name=$matches[4]'; // моя пользовательская структура всегда будет иметь имя записи в качестве 5-го сегмента URL
$newRules['basename/(.+)/?$'] = 'index.php?taxonomy_name=$matches[1]';
return array_merge($newRules, $rules);
}
Затем вам нужно добавить этот код, чтобы WordPress понимал, как обрабатывать %taxonomy_name%
в структуре ярлыка перезаписи вашего произвольного типа записи:
function filter_post_type_link($link, $post)
{
if ($post->post_type != 'custom_post_type_name')
return $link;
if ($cats = get_the_terms($post->ID, 'taxonomy_name'))
{
$link = str_replace('%taxonomy_name%', get_taxonomy_parents(array_pop($cats)->term_id, 'taxonomy_name', false, '/', true), $link); // см. пользовательскую функцию, определенную ниже
}
return $link;
}
add_filter('post_type_link', 'filter_post_type_link', 10, 2);
Я создал пользовательскую функцию на основе собственной функции WordPress get_category_parents
:
// моя собственная функция для того, что делает get_category_parents для других таксономий
function get_taxonomy_parents($id, $taxonomy, $link = false, $separator = '/', $nicename = false, $visited = array()) {
$chain = '';
$parent = &get_term($id, $taxonomy);
if (is_wp_error($parent)) {
return $parent;
}
if ($nicename)
$name = $parent -> slug;
else
$name = $parent -> name;
if ($parent -> parent && ($parent -> parent != $parent -> term_id) && !in_array($parent -> parent, $visited)) {
$visited[] = $parent -> parent;
$chain .= get_taxonomy_parents($parent -> parent, $taxonomy, $link, $separator, $nicename, $visited);
}
if ($link) {
// ничего, не могу заставить это работать :(
} else
$chain .= $name . $separator;
return $chain;
}
Затем вам нужно сбросить ваши постоянные ссылки (просто загрузите страницу настроек постоянных ссылок).
Теперь все "должно" работать! Создайте несколько терминов таксономии и правильно вложите их, затем создайте несколько записей произвольного типа и правильно их категоризируйте. Вы также можете создать страницу с ярлыком basename
, и все должно работать так, как я указал в своем вопросе. Возможно, вы захотите создать пользовательские страницы архивов таксономии, чтобы контролировать их внешний вид, и добавить какой-нибудь виджет таксономии для отображения ваших вложенных категорий в боковой панели.
Надеюсь, это поможет вам!

Привет, Джефф, спасибо за ответ! Я почти разобрался, после 4 часов борьбы с этим. Единственная проблема — у меня получается двойной слеш перед названием записи (вот так: portfolio/diseno-grafico-2/logo//alquimia-sonora/). Можешь помочь?

@Cmorales, Трудно сказать без кода, но поищи место, где ты вручную добавляешь слеш перед названием записи, возможно в регистрации slug для CPT или в функции filter_post_type? Если не удается найти, откуда берется лишний слеш, можно просто удалить последний символ из возвращаемого значения функции get_taxonomy_parents, вызываемой в filter_post_type_link, потому что она оставляет слеш в конце, который становится первым из двойных. Удачи.

Я создал постоянные ссылки по вашему руководству, но стандартная ссылка в WordPress для пользовательского типа записи: domain.com/my-post-type/custom-post-type-name (domain.com/place/lotus-hotel) Я просто протестировал ваш код в function.php, но не могу найти способ добавить пользовательскую таксономию к этой ссылке (domain.com/place/tokyo/lotus-hotel) — tokyo это моя пользовательская таксономия.

Исправлено - см. вопрос. Джефф, спасибо за объяснение! Но не могли бы вы опубликовать код создания пользовательского типа записей / таксономии? Чтобы я мог увидеть, что я делаю не так? Был бы очень благодарен!

Огромное спасибо за это. Есть одна небольшая проблема: похоже, это засоряет правила перезаписи кучей записей %taxonomy_name% или в моем случае: apps/%primary_tag%/([^/]+)/page/?([0-9]{1,})/?$] => index.php?%primary_tag%$matches[1]&apps-post=$matches[2]&paged=$matches[3]
Правила, которые я добавляю вручную, имеют приоритет и вроде работают, но эти записи подразумевают, что что-то не совсем правильно. Есть идеи?

"Если у вас будут различные уровни вложенных терминов, то вам понадобится написать функцию для проверки, является ли последний сегмент URI пользовательским типом записи или термином таксономии, чтобы знать, какое правило добавлять (спросите меня, если вам нужна помощь в этом)..." Ну, не могли бы вы мне помочь, пожалуйста? :)

^ это именно то, что мне нужно. Кто-нибудь знает, как это сделать? Смотрите мой вопрос здесь: http://wordpress.stackexchange.com/questions/102246/wordpress-returns-404-on-custom-rewrite-rules

Это сработало хорошо, но по какой-то причине оно выводит только родительскую категорию и ни одной дочерней. Я скопировал в точности, как вы опубликовали, и изменил только названия своего типа записей и таксономий, где указано. Есть идеи?

Я знаю, что это немного устарело, но есть ли у вас идеи, как сохранить '/basename' указывающим на самые свежие записи этого типа, независимо от таксономии?

После 3 часов поиска в Google, вот ответ. Для тех, кто использует формат /post_type/term/slug/ (без иерархии), вы можете удалить функцию get_taxonomy_parents() и заменить её на $cats[0]->slug, так как там уже есть проверка на наличие терминов. Эта функция также добавляет лишний слеш.

Ознакомьтесь с этим плагином (теперь на github). Он предоставляет иерархические URL для категорий, но вы можете легко адаптировать его для любой таксономии.
Создание правил следует рекурсивной функции.

Для работы с различными уровнями вложенности,
/basename/top-cat/ -> Архив Top Cat
/basename/top-cat/post-name/ -> Запись в Top Cat
/basename/top-cat/child-cat/ -> Архив Child Cat
/basename/top-cat/child-cat/post-name/ -> Запись в Child Cat
В итоге я использовал решение Джеффа без фильтра rewrite_rules_array
. Вместо этого я применил фильтр request
, чтобы проверить, является ли последняя часть URL допустимым именем записи, и добавил её в query_vars.
Например:
function vk_query_vars($qvars){
if(is_admin()) return $qvars;
$custom_taxonomy = 'product_category';
if(array_key_exists($custom_taxonomy, $qvars)){
$custom_post_type = 'product';
$pathParts = explode('/', $qvars[$custom_taxonomy]);
$numParts = sizeof($pathParts);
$lastPart = array_pop($pathParts);
$post = get_page_by_path($lastPart, OBJECT, $custom_post_type);
if( $post && !is_wp_error($post) ){
$qvars['p'] = $post->ID;
$qvars['post_type'] = $custom_post_type;
}
}
return $qvars;
}
add_filter('request', 'vk_query_vars');

Вот мой вариант решения Джеффа. rewrite_rules_array
может не понадобиться в зависимости от вашего приложения. Это упрощает функцию filter_post_type_link
и не требует дополнительных функций. Используется встроенная функция get_term_parents_list()
function PLUGIN_filter_post_type_link($post_link, $post)
{
$postTypes = ['CUSTOM-POST-TYPE'];
$taxonomy = 'TAXONOMY';
/* если тип записи совпадает */
if (in_array($post->post_type, $postTypes, $strict=true)) {
/* если таксономия назначена */
if ($terms = get_the_terms($post->ID, $taxonomy)
) {
$args = [
'format' => 'slug',
'separator' => '/',
'link' => false,
'inclusive' => true,
];
return str_replace(
"%{$taxonomy}%",
rtrim(
get_term_parents_list($terms[0]->term_id, $taxonomy, $args),
"/"
),
$post_link);
}
/* удалить таксономию и завершающий слэш из post_link */
return str_replace("%{$taxonomy}%/", '', $post_link);
}
return $post_link;
}
