Как отключить страницу одиночного просмотра для произвольного типа записи?
Допустим, у нас есть такой произвольный тип записи:
register_post_type(
'sample_post_type',
[
'labels' => [
'name' => _x('Примеры записей', 'общее название типа записи'),
'singular_name' => _x('Пример записи', 'единичное название типа записи'),
],
'public' => true,
'show_in_nav_menus' => false,
'exclude_from_search' => true,
]
);
Как можно отключить просмотр одиночной записи для этого конкретного типа записи? Показ простой 404 ошибки подойдет, или редирект на главную страницу. Поскольку это плагин, я не могу создать файл single-sample_post_type.php
для настройки пустой страницы.

Это похоже на решение, которое предотвращает создание реальных веб-страниц для вашего CPT, сохраняя при этом все ссылки в базе данных.

Чтобы отключить одиночное отображение (single view) для пользовательского типа записи (CPT), вы можете либо перенаправить пользователей на определенный URL, либо отключить эту возможность при регистрации самого CPT.
МЕТОД 1:
Перенаправление одиночного просмотра CPT на пользовательский URL, при этом архивная страница остается доступной.
Вы можете использовать хук template_redirect
для перенаправления пользователя. Вместо home_url()
можно указать любой нужный URL, а в качестве второго аргумента — желаемый код ошибки.
<?php
add_action( 'template_redirect', 'wpse_128636_redirect_post' );
function wpse_128636_redirect_post() {
if ( is_singular( 'sample_post_type' ) ) :
wp_redirect( home_url(), 301 );
exit;
endif;
}
?>
МЕТОД 2:
Полное отключение одиночных и архивных страниц на фронтенде; Работает только для пользовательских типов записей.
Альтернативный подход — установить параметр publicly_queryable
в значение false
при регистрации пользовательского типа записи.
'publicly_queryable' => false
Это скроет как одиночные, так и архивные страницы для CPT. Данный метод применим только к пользовательским типам записей.
Даже если архив и одиночные страницы скрыты, вы по-прежнему можете добавить шаблон страницы или пользовательский блок для вывода записей, если это необходимо.

Хорошее решение. Я обнаружил, что полезно выполнять этот редирект только для неаутентифицированных пользователей, проверяя, возвращает ли get_current_user_id()
значение 0.

почему бы не использовать просто is_singular('post-type-slug')
в условии if()
вместо двух условий?

предложение использовать remove_rewrite_tag( '%post-type-slug%' ) - хорошее решение

Протестировал все вышеупомянутые варианты, и настоящее решение оказалось проще любых предложенных редиректов.
Чтобы архив был доступен и отображал записи, а отдельная запись не была доступна и автоматически перенаправляла на 404, установите
'query_var' => false
при регистрации вашего CPT. Если вы установите publicly_queryable
в false, ваши архивы будут перенаправлены на главную страницу, никакие другие комбинации не сработают. Просто установите query_var
в false, и всё.
Вот полный код CPT: https://gist.github.com/danyj/bfd038d3c8d578548c4d700bd0a7942a
см. строку 50: https://gist.github.com/danyj/bfd038d3c8d578548c4d700bd0a7942a#file-thz_cpt_items_single_view_redirect-php-L50
Как указано здесь:
https://codex.wordpress.org/Function_Reference/register_post_type
Примечание: Если query_var пуст, null или имеет значение FALSE, WordPress всё равно попытается его интерпретировать (4.2.2), и предпросмотры/просмотры вашего пользовательского типа записи будут возвращать 404 ошибки.

Это выглядит немного как костыль, но, кажется, работает. Хотелось бы, чтобы они просто добавили специальное свойство has_single, аналогично существующему has_archive.

Привет, я попробовал сделать то же самое, но это не сработало. Несмотря на добавление query_var
в false
, я всё ещё могу переходить по ссылкам на одиночные страницы. Можешь помочь?

@iSaumya попробуй сбросить постоянные ссылки (перейди на страницу постоянных ссылок и сохрани снова) или используй команду wp-cli wp rewrite flush && wp cache flush

Больше не работает (август 2022). Заметка о том, что одиночные просмотры удалены, также больше не существует в документации.

@blorf заметка всё ещё в документации - она указана в разделе publicly_queryable

У меня это работает, у меня установлены Elementor с JetEngine, и я настраиваю тип записи с параметрами public
, publicly_queryable
и has_archive
установленными в true, а query_var
установлен в false. Мои архивы работают, а одиночные записи перенаправляются (301) на главную страницу.

Более простой способ сделать это — передать следующие аргументы при регистрации пользовательского типа записи:
register_post_type('sample_post_type',array(
'labels' => array(
'name' => _x('Пример записей', 'общее название типа записи'),
'singular_name' => _x('Пример записи', 'название типа записи в единственном числе')
),
'public' => true,
'exclude_from_search' => true,
'show_in_admin_bar' => false,
'show_in_nav_menus' => false,
'publicly_queryable' => false,
'query_var' => false
));

Для работы архивов типа записи необходимо, чтобы параметр publically_querable
был установлен в true.

У меня это не работает. При попытке перейти в архив происходит перенаправление на главную страницу.

Первый способ. Добавьте в файл functions.php:
add_action( 'template_redirect', 'redirect_cpt_singular_posts' );
function redirect_cpt_singular_posts() {
if ( is_singular('your-cpt-slug') ) { // Замените 'your-cpt-slug' на ярлык вашего типа записи
wp_redirect( home_url(), 302 ); // Перенаправление на главную страницу с кодом 302
exit;
}
}
Второй способ. Добавьте в файл single-cpt.php (замените cpt на ваш тип записи):
<?php wp_redirect( home_url() ); exit; // Перенаправление на главную страницу ?>

Мне понравился второй вариант. Но мне нужно знать, есть ли какие-то недостатки у этого второго варианта.

@user2584538 Если у вас нет кастомного файла single-cpt_name.php
, вы не сможете этого сделать. Если поместите функцию в простой файл плагина, вы сможете активировать/деактивировать его без редактирования каких-либо файлов.

Основываясь на отличном ответе Свена, я переписал его функцию, чтобы упростить добавление нескольких типов записей с использованием in_array()
в условии if, а затем перенаправлять на страницу архива вместо главной страницы.
(кстати, я думаю, что установка query_var
и/или publically_queryable
в false отключит не только одиночные просмотры, но и нативное представление архива, переопределяя 'has_archive' => true
. В таком случае вы всё ещё можете настроить custom WP_query и создать свою собственную страницу архива в шаблоне, но основной запрос уже не будет этого делать, верно?)
function fq_disable_single_cpt_views() {
$queried_post_type = get_query_var('post_type');
$cpts_without_single_views = array( 'my-post-type', 'my-other-post-type' );
if ( is_single() && in_array( $queried_post_type, $cpts_without_single_views ) ) {
wp_redirect( home_url( '/' . $queried_post_type . '/' ), 301 );
exit;
}
}
add_action( 'template_redirect', 'fq_disable_single_cpt_views' );

это была хорошая идея, но не работает, если изменить редирект для CPT (пользовательских типов записей)

Я изменил вашу строку wp_redirect на wp_redirect( get_post_type_archive_link( $queried_post_type ), 301 );
работает отлично!

Если вы хотите полностью отключить одиночное представление (single view) пользовательского типа записи на фронтенде, но при этом сохранить возможность отображения архивной страницы, ситуация становится немного сложнее.
Установка параметра publicly_queryable
в false
или rewrite
в false
предотвратит отображение как одиночного, так и архивного представления. В аргументах функции register_post_type
нет флага, который бы предотвращал создание правил перезаписи только для одиночного представления.
https://github.com/WordPress/WordPress/blob/5.2.3/wp-includes/class-wp-post-type.php#L540
Однако вы можете удалить тег перезаписи после регистрации вашего типа записи, и это оставит правила перезаписи для архивного представления без изменений, но удалит только правила для одиночного представления.
/**
* Регистрация типа записи "Событие"
*/
function wpse_128636_register_event_post_type() {
$labels = array(
'name' => __( 'События' ),
'singular_name' => __( 'Событие' ),
'add_new' => __( 'Добавить новое' ),
'add_new_item' => __( 'Добавить новое' ),
'edit_item' => __( 'Редактировать' ),
'new_item' => __( 'Новое' ),
'view_item' => __( 'Просмотр' ),
'search_items' => __( 'Поиск' ),
'not_found' => __( 'Не найдено' ),
'not_found_in_trash' => __( 'События не найдены в корзине' ),
'parent_item_colon' => __( 'Родительский' ),
'menu_name' => __( 'События' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'supports' => array( 'title', 'page-attributes' ),
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => true,
'publicly_queryable' => true,
'exclude_from_search' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'event'),
'capability_type' => 'post',
);
register_post_type( 'event', $args );
remove_rewrite_tag( '%event%' ); // Эта строка удалит правила перезаписи для одиночного представления событий
}
add_action( 'init', 'wpse_128636_register_event_post_type' );
Дополнительным бонусом является то, что теперь вы можете создавать простые страницы WordPress, используя структуру постоянных ссылок типа записи event (event/prostaya-stranica
), что может быть полезно в сложных веб-сайтах.
Не забудьте сбросить правила перезаписи после изменения кода.

В WordPress 5.9.0
был добавлен новый фильтр, который позволяет не только отключать одиночную запись (single), но и удалять все ссылки на неё в административной панели.
add_filter( 'is_post_type_viewable', function( $is_viewable, $post_type ) {
if ( 'sample_post_type' === $post_type->name ) {
return false;
}
return $is_viewable;
}, 10, 2 );
Одиночная запись не будет загружаться, но если ввести URL вручную, вместо 404 ошибки будет загружаться главная страница. Чтобы вместо главной страницы отображалась 404 ошибка, вы можете удалить правило перезаписи после регистрации типа записи.
remove_rewrite_tag( '%sample_post_type%' );
После внесения этих изменений необходимо сбросить правила перезаписи.
Использование этих двух методов позволит достичь цели без дополнительных шаблонов, редиректов и битых ссылок в админке.

Моё текущее решение (в основном "решение с редиректом") с небольшим отличием в реализации.
Это позволит оставить архивные страницы включенными (has_archive = TRUE
)
Но даст возможность включать или отключать одиночные страницы при использовании register_post_type()
с параметром x_has_single = TRUE
(параметр передается вместе со свойствами объекта типа записи).
add_action( 'init', function() {
$args = [
... // другие переменные
'public' => FALSE,
'publicly_queryable' => TRUE,
'show_ui' => TRUE,
'has_archive' => 'customers',
'rewrite' => [ 'slug' => 'customers', 'with_front' => FALSE ],
'x_has_single' => FALSE,
];
register_post_type( 'customers', $args );
} );
add_action( 'template_redirect', function() {
$post_type = get_post_type() ?: FALSE;
$post_type_obj = get_post_type_object( $post_type );
$has_single = $post_type_obj->x_has_single ?? TRUE;
if ( FALSE === $has_single && $post_type && is_singular( $post_type ) ) {
wp_redirect( get_post_type_archive_link( $post_type ) ?: '/', 301 );
exit;
}
} );
