Настраиваемое правило перезаписи URL для иерархического типа записей в WordPress
Работая над сайтом клиента, я столкнулся со своим злейшим врагом... перезаписью URL в WordPress :(. Я действительно не могу разобраться и после многих дней попыток и поисков у меня ничего не получается.
Что я пытаюсь получить - это постоянные ссылки вида company.com/product/%название-продукта%/
независимо от глубины вложенности /родитель/дочерний/
. Мне нужна функциональность иерархических страниц по другим причинам, но это не должно отображаться в URL.
Вот моя настройка:
- Wordpress 4.4.1
Настройки постоянных ссылок установлены на
Название записи
Я создал иерархический кастомный тип записи
product
с параметром rewrite отключенным.$args = array( 'label' => __( 'Product', 'domain' ), 'description' => __( 'Company products', 'domain' ), 'labels' => $labels, 'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'page-attributes' ), 'taxonomies' => array( 'category', 'post_tag' ), 'hierarchical' => true, 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 5, 'show_in_admin_bar' => true, 'show_in_nav_menus' => false, 'can_export' => true, 'has_archive' => true, 'exclude_from_search' => false, 'publicly_queryable' => true, 'rewrite' => false, 'capability_type' => 'page', ); register_post_type( 'product', $args );
На этом этапе продукты не имеют красивых постоянных ссылок, они выглядят так и оба варианта работают:
- Первый уровень:
company.com/?product=my-first-product
-> ок - Второй уровень:
company.com/?product=my-first-product/child-product
-> ок
После этого я зарегистрировал правило перезаписи и permastruct, затем сбросил правила перезаписи, сохранив изменения на странице настроек постоянных ссылок
function bvt_product_rewrite_rule() {
add_rewrite_rule(
'^product/([^/]+)/?$',
'index.php?post_type=product&pagename=$matches[1]',
'top'
);
add_permastruct( 'product', '/product/%product%/' );
}
add_action( 'init', 'bvt_product_rewrite_rule', 10 );
Теперь постоянные ссылки правильно отображались для страниц первого уровня, но не работали для дочерних страниц. Также оба уровня выдавали ошибку 404.
- Первый уровень:
company.com/product/my-first-product/
-> 404 - Второй уровень:
company.com/product/my-first-product/child-product/
-> 404
Я также попробовал решение, приведенное здесь https://wordpress.stackexchange.com/a/101077/86838, где предлагалось удалить родительский слаг из дочерней постоянной ссылки, но у меня все равно ничего не получилось
function bvt_product_flatten_hierarchies( $post_link, $post ) {
if ( 'product' != $post->post_type ) {
return $post_link;
}
$uri = '';
foreach ( $post->ancestors as $parent ) {
$uri = get_post( $parent )->post_name . "/" . $uri;
}
return str_replace( $uri, '', $post_link );
}
add_filter( 'post_type_link', 'bvt_product_flatten_hierarchies', 10, 2 );
Я установил два плагина debug-bar и monkeyman-rewrite-analyzer для анализа перезаписи и запросов, но все равно не могу заставить это работать.
Моя последняя попытка была с плагином wp-permastructure, который позволяет установить пользовательскую постоянную ссылку для кастомного типа записи. Он включает настройку permastruct
в массиве опций rewrite
функции register_post_type
.
[...]
'publicly_queryable' => true,
'rewrite' => array(
'permastruct' => '/%postname%/',
),
'capability_type' => 'page',
[...]
С этой настройкой и отключенными предыдущими кастомными rewrite/permatruct/flatten_hierarchies, все работало, как указано в описании плагина
- Первый уровень:
company.com/my-first-product/
-> ок - Второй уровень:
company.com/child-product/
-> ок
Но теперь отсутствует уровень /product/
, и если я попытаюсь добавить его в конфигурацию 'permastruct' => '/product/%postname%/'
, это снова приводит к ошибке 404.
Что приводит к моему главному вопросу.
Возможно ли сделать то, что мне нужно, или только то, что я смог получить с помощью последнего плагина?
Заранее спасибо всем, кто уделит время ответить мне и, возможно, спасет меня от моей печальной ситуации. Спасибо
P.S. Я не могу вставить более двух ссылок в тело сообщения, поэтому вот ссылки на упомянутые плагины:
https://wordpress.org/plugins/debug-bar/
https://wordpress.org/plugins/monkeyman-rewrite-analyzer/
https://wordpress.org/plugins/wp-permastructure/
Вы почти у цели. В вашем правиле перезаписи используется неверная переменная запроса - pagename
должно быть просто name
.
Вот версия, которая работает у меня на свежей установке WordPress 4.4.1 с темой Twenty Sixteen:
function bvt_product_init() {
$args = array(
'label' => __( 'Product', 'domain' ), // Название типа записи
'description' => __( 'Company products', 'domain' ), // Описание
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'revisions', 'page-attributes' ), // Поддерживаемые возможности
'taxonomies' => array( 'category', 'post_tag' ), // Таксономии
'hierarchical' => true, // Иерархический тип
'public' => true, // Публичный
'show_ui' => true, // Показывать в админке
'show_in_menu' => true, // Показывать в меню
'menu_position' => 5, // Позиция в меню
'show_in_admin_bar' => true, // Показывать в админ-баре
'show_in_nav_menus' => false, // Показывать в навигационных меню
'can_export' => true, // Можно экспортировать
'has_archive' => true, // Имеет архив
'rewrite' => array( 'slug' => 'product' ), // Перезапись URL
);
register_post_type( 'product', $args );
// Правило перезаписи URL для продуктов
add_rewrite_rule(
'^product/([^/]+)/?$',
'index.php?post_type=product&name=$matches[1]',
'top'
);
}
add_action( 'init', 'bvt_product_init' );
// Функция для уплощения иерархии в URL
function bvt_product_flatten_hierarchies( $post_link, $post ) {
if ( 'product' != $post->post_type ) {
return $post_link;
}
$uri = '';
foreach ( $post->ancestors as $parent ) {
$uri = get_post( $parent )->post_name . "/" . $uri;
}
return str_replace( $uri, '', $post_link );
}
add_filter( 'post_type_link', 'bvt_product_flatten_hierarchies', 10, 2 );
Один потенциальный момент, на который стоит обратить внимание: иерархические типы записей позволяют создавать записи с одинаковыми ярлыками (slug), но разными родителями. Обычно это работает, поскольку они запрашиваются по своим родительским/дочерним путям. Без наличия этой родительской/дочерней связи в структуре URL вы можете создать записи, которые никогда нельзя будет запросить на фронтенде, если их ярлык совпадает с уже существующей записью. Просто имейте это в виду.
