How to disable the single view for a custom post type?

Given this custom post type:

register_post_type(
    'sample_post_type',
    [
        'labels' => [
            'name' => _x('Sample Posts', 'post type general name'),
            'singular_name' => _x('Sample Post', 'post type singular name'),
        ],
        'public' => true,
        'show_in_nav_menus' => false,
        'exclude_from_search' => true,
    ]
);

How can I disable the single post view for this particular post type? Displaying a simple 404 is fine, or redirecting to the homepage. Since this is a plugin, I can’t create a single-sample_post_type.php file to set up an empty page.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

METHOD 1:

Redirect to a custom URL for single view, archive page is still publicly accessible.

You can use template_redirect hook to redirect for a custom post type, you can use any other URL you want to in place of home_url() and the error code in other argument.

<?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;
  }
}
?>

METHOD 2:

Completely disable Single or Archive page from front-end; Works for a Custom post only.

A alternative approach is to specify value for param publicly_queryable while registering the custom post if you are ok with displaying a 404 page. [ Thanks @gustavo ]

'publicly_queryable'  => false

It hides single as well as archive page for the CPT, basically completely hidden from front-end but can be done for custom posts only.

This second solution is best suitable for the scenario where you need a Custom post for admin/back-end usage only.

Method 2

Just setting the argument

'publicly_queryable'  => false

when you call register_post_type()

Method 3

A simpler way to do that can be passing the following args when registering the Custom Post Type

register_post_type('sample_post_type',array(
'labels' => array(
    'name' => _x('Sample Posts', 'post type general name'),
    'singular_name' => _x('Sample Post', 'post type singular name')
),
'public' => true,
'exclude_from_search' => true,
'show_in_admin_bar'   => false,
'show_in_nav_menus'   => false,
'publicly_queryable'  => false,
'query_var'           => false
));

Method 4

Tested all the ones mentioned above and the actual solution is simpler than any redirects suggested.

In order to have archive be accessible and list the items, and single post not be accessible and auto redirect to 404 set

'query_var' => false

when registering your CPT. If you set publicly_queryable to false your archives will be redirected to home, any other combo wont work. Set the query_var to false and that is it.

Here is full CPT https://gist.github.com/danyj/bfd038d3c8d578548c4d700bd0a7942a

see line 50 https://gist.github.com/danyj/bfd038d3c8d578548c4d700bd0a7942a#file-thz_cpt_items_single_view_redirect-php-L50

as stated here

https://codex.wordpress.org/Function_Reference/register_post_type

Note: If query_var is empty, null, or a boolean FALSE, WordPress will
still attempt to interpret it (4.2.2) and previews/views of your
custom post will return 404s.

Method 5

One. From your functions file.

add_action( 'template_redirect', 'redirect_cpt_singular_posts' );
    function redirect_cpt_singular_posts() {
      if ( is_singular('your-cpt-slug') ) {
        wp_redirect( home_url(), 302 );
        exit;
      }
    }

Two. From your single.cpt.php file:

<?php wp_redirect( home_url() ); exit; ?>

Method 6

Working from of Sven’s really good answer, I rewrote his function to make it easier to add multiple post types using in_array() in the if statement and then redirecting to the archive page instead of the home page.

(by the way, I think the setting query_var and/or publically_queryable to false will disable not only the single views, but also the native archive view, overriding 'has_archive' => true. In that case you can still set up a custom WP_query and create your own archive page, in a template, but the main query won’t do that any more, will it?)

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' );

Method 7

In case you want to completely disable custom post type single view on frontend but be able to display archive page things are getting a little complicated.

Setting publicly_queryable to false or rewrite to false will prevent for displaying both single and archive view. There is no flag in register_post_type function arguments to prevent creating only single view rewrite rules.

https://github.com/WordPress/WordPress/blob/5.2.3/wp-includes/class-wp-post-type.php#L540

However you can remove rewrite tag after registering your post type and this will leave archive view rewrite rules untouched but remove only single view rewrite rules.

/**
 * Register event post type
 */
function wpse_128636_register_event_post_type() {

    $labels = array(
        'name' => __( 'Events' ),
        'singular_name' => __( 'Event' ),
        'add_new' => __( 'Add new' ),
        'add_new_item' => __( 'Add new' ),
        'edit_item' => __( 'Edit' ),
        'new_item' => __( 'New' ),
        'view_item' => __( 'View' ),
        'search_items' => __( 'Search' ),
        'not_found' => __( 'Not found' ),
        'not_found_in_trash' => __( 'Not found Events in trash' ),
        'parent_item_colon' => __( 'Parent' ),
        'menu_name' => __( 'Events' ),

    );

    $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%' ); // This line will remove event rewrite rules for single view
}

add_action( 'init', 'wpse_128636_register_event_post_type' );

Another bonus is that from now on you can create simple WordPress pages using event post type permalink structure (event/simple-page) which can be helpful in complex websites.

Remember to flush rewrite rules after code modification.

Method 8

In WordPress 5.9.0 a new filter was added which allows not only disabling the single but removing any links to it from the admin screens.

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 );

The single won’t load but if you enter the URL manually, the home page will load instead of a 404. To show a 404 instead of the home page, you may remove the rewrite rule after the post type is registered.

remove_rewrite_tag( '%sample_post_type%' );

After making these changes you will need to flush the rewrite rules.

Using these 2 methods will accomplish the goal without extra template, redirects and broken links in the admin.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x