Изменение структуры постоянных ссылок для произвольного типа записей
У меня есть 2 произвольных типа записей: software
и hardware
. По соображениям SEO я хотел бы иметь постоянные ссылки для отдельных страниц software
и hardware
в виде:
https://domain.com/custom-post-name/
Но по умолчанию WordPress добавляет слаг типа записи следующим образом:
https://domain.com/post-type-slug/custom-post-name/
Я удалил слаг с помощью следующего скрипта, который выполняет замену строки. Теперь отдельная страница доступна по обоим описанным выше URL, и Google Search Console находит 2 страницы с абсолютно одинаковым содержимым, что плохо для SEO. Возможно ли изменить полную структуру постоянной ссылки и избавиться от слага произвольного типа записи?
register_post_type( 'hardware',
array (
'labels' => $labels,
'has_archive' => true,
'public' => true,
'supports' => array( 'title', 'editor', 'excerpt', 'custom-fields', 'thumbnail' ),
'taxonomies' => array( 'hardware-post_tag', 'hardware-category' ),
'exclude_from_search' => false,
'capability_type' => 'post',
'rewrite' => array( 'slug' => 'hardware' ),
)
);
Обратите внимание на один важный момент в приведенном выше ответе:
Хотя на первый взгляд это будет работать нормально, это может вызвать проблемы с производительностью.
Весь этот код будет вызываться через хук init
, поэтому при каждой загрузке страницы он будет выполняться, а функция flush_rules()
весьма ресурсоемкая.
Поэтому рекомендуется вызывать сброс правил перезаписи только при активации темы или плагина. Также можно использовать функции add_permastruct
без необходимости обращаться к global $wp_rewrite
.
Окончательное улучшенное решение будет выглядеть так:
add_action('init', 'my_custom_rewrite');
function my_custom_rewrite() {
add_permastruct('hardware', '/%customname%/', false);
add_permastruct('produkt', '/%customname%/', false);
}
add_filter( 'post_type_link', 'my_custom_permalinks', 10, 2 );
function my_custom_permalinks( $permalink, $post ) {
return str_replace( '%customname%/', $post->post_name, $permalink );
}
/* для плагина */
register_activation_hook(__FILE__,'my_custom_plugin_activate');
function my_custom_plugin_activate() {
flush_rewrite_rules();
}
/* для пользовательской темы в Functions.php */
add_action('after_switch_theme', 'mytheme_setup');
function mytheme_setup () {
flush_rewrite_rules();
}

Мне удалось решить это с помощью {$field_no_prefix}_save_pre
и post_name
.
/**
* Настройка постоянных ссылок.
*
* @param string $post_name
*
* @return string
* Возвращает комбинацию названия и артикула для товаров, если все компоненты доступны.
*/
function my_custom_permalinks( $post_name ) {
if (
($_POST['post_type'] !== 'product')
|| ($_POST['post_status'] === 'auto-draft')
) {
return $post_name;
}
$post_name = sanitize_title_with_dashes(
{ модифицируйте $_POST['post_title'] по своему усмотрению }
);
return $post_name;
}
add_filter('name_save_pre', 'my_custom_permalinks', 1, 1);

Если посмотреть на аргумент rewrite
для функции register_post_type
, там есть опция with_front
, которая позволяет отключить базовый префикс, добавляемый по умолчанию к постоянным ссылкам произвольного типа записи. Если установить значение false, то /blog/products/
, например, превратится в /products/
.
https://developer.wordpress.org/reference/functions/register_post_type/#rewrite
'rewrite' => array(
'slug' => 'products',
'with_front' => false
)

РЕШЕНО
Используя $wp_rewrite
, вы можете добавить новую структуру постоянных ссылок
add_action('init', 'my_custom_rewrite');
function my_custom_rewrite() {
global $wp_rewrite;
$wp_rewrite->add_permastruct('hardware', '/%customname%/', false);
$wp_rewrite->add_permastruct('produkt', '/%customname%/', false);
$wp_rewrite->flush_rules();
}
Затем вы заменяете свой пользовательский тег с помощью str_replace
при фильтрации URL ссылки
add_filter( 'post_type_link', 'my_custom_permalinks', 10, 2 );
function my_custom_permalinks( $permalink, $post ) {
return str_replace( '%customname%/', $post->post_name, $permalink );
}
ПРИМЕЧАНИЕ: если вы используете эту функцию так, как написано (удаляя слаг типа записи из постоянной ссылки), могут возникнуть серьезные проблемы, так как нет контроля, если вы создадите пользовательскую постоянную ссылку такой же, как у страницы или стандартной записи

Если кто-то столкнулся с этой проблемой и обнаружил, что ссылка на "следующий скрипт" из вопроса больше не работает, вот этот скрипт сохраненный в Wayback Machine, с исправлением ошибки, которая, как я полагаю, была в коде:
/**
* Удаляет slug из постоянных ссылок опубликованных записей. Работает только для нашего CPT.
*/
function vipx_remove_cpt_slug( $post_link, $post, $leavename ) {
if ( ! in_array( $post->post_type, array( 'your_post_type' ) ) || 'publish' != $post->post_status )
return $post_link;
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
add_filter( 'post_type_link', 'vipx_remove_cpt_slug', 10, 3 );
function vipx_parse_request_tricksy( $query ) {
// Обрабатываем только основной запрос
if ( ! $query->is_main_query() )
return;
// Обрабатываем только строго определённое правило перезаписи
if ( 2 != count( $query->query )
|| ! isset( $query->query['page'] ) )
return;
// 'name' будет установлен, если постоянные ссылки состоят только из post_name, иначе сработает правило для страниц
if ( ! empty( $query->query['name'] ) )
$query->set( 'post_type', array( 'post', 'your_post_type', 'page' ) );
}
add_action( 'pre_get_posts', 'vipx_parse_request_tricksy' );
В дополнение к пересмотренному решению 3ky от Михаила, если вам, как и мне, не сразу стало понятно, я обнаружил, что необходимо реализовать функцию vipx_parse_request_tricksy()
и соответствующее действие. Однако функция vipx_remove_cpt_slug()
дублирует функционал my_custom_permalinks()
, поэтому обе не нужны — выберите одну?
