[wp-trac] [WordPress Trac] #37376: Make it possible for custom post type to have an archive but no single

WordPress Trac noreply at wordpress.org
Thu May 7 13:55:00 UTC 2026


#37376: Make it possible for custom post type to have an archive but no single
----------------------------------------+------------------------------
 Reporter:  jason_the_adams             |       Owner:  (none)
     Type:  feature request             |      Status:  new
 Priority:  normal                      |   Milestone:  Awaiting Review
Component:  Posts, Post Types           |     Version:  4.6
 Severity:  normal                      |  Resolution:
 Keywords:  needs-unit-tests has-patch  |     Focuses:
----------------------------------------+------------------------------

Comment (by bacoords):

 I've been working on refreshing this patch and wanted to document some
 findings about the broader implications.

 === Core Approach ===

 The patch adds a `has_single` property (boolean, default `true`) to
 `register_post_type()`. When `false`:
 * No rewrite rules are generated for single post views
 * `get_permalink()` / `get_post_permalink()` return `false`
 * `wp_get_shortlink()` returns empty string

 === The `is_post_type_viewable()` Problem ===

 A key decision: '''`is_post_type_viewable()` intentionally does NOT check
 `has_single`'''.

 This function is used for two distinct purposes:
 1. "Can users view this post type on the front-end?" (REST API, Query
 Loop, archives)
 2. "Does this post type have single post views?" (View links, sitemaps,
 permalinks)

 If we made `is_post_type_viewable()` return `false` for `has_single =>
 false` post types, it would break:
 * REST API public access
 * Query Loop blocks
 * Archive pages
 * Any code checking general "publicness"

 Instead, places that specifically care about single views must check
 '''both''' `is_post_type_viewable()` AND `has_single`:

 {{{
 if ( is_post_type_viewable( $post_type ) && $post_type->has_single ) {
     // Show view link, add to sitemap, etc.
 }
 }}}

 === The Permalink Assumption ===

 WordPress core assumes `get_permalink()` always returns a URL string. This
 patch changes that - it can now return `false`. Places generating links
 must check:

 {{{
 $permalink = get_permalink( $post );
 if ( $permalink ) {
     echo '<a href="' . esc_url( $permalink ) . '">View</a>';
 }
 }}}

 === Files Changed in This Patch ===

 '''Post type registration:'''
 * `class-wp-post-type.php` - Adds property, skips single rewrite rules

 '''Permalink functions:'''
 * `link-template.php` - `get_post_permalink()`, `wp_get_shortlink()`,
 `get_preview_post_link()` return false/empty

 '''Admin UI:'''
 * `edit-form-advanced.php` - Hides preview/view links in editor
 * `meta-boxes.php` - Hides Preview button
 * `class-wp-posts-list-table.php` - Hides View/Preview row actions

 '''REST API:'''
 * `class-wp-rest-posts-controller.php` - No alternate link header, no
 `permalink_template`/`generated_slug` fields

 '''Sitemaps:'''
 * `class-wp-sitemaps-posts.php` - Excludes post types with `has_single =>
 false`

 === Gutenberg PR Required ===

 Many core blocks also assume permalinks exist for all post types. These
 blocks need updates in the Gutenberg repo:
 * `post-title` - Link option
 * `post-excerpt` - "Read more" link
 * `post-featured-image` - Link option
 * `post-date` - Link option
 * `read-more` - Entire block purpose is linking
 * `latest-posts` - Post title links
 * `breadcrumbs` - Ancestor links

 The fix is straightforward - check if `get_the_permalink()` returns a
 value before wrapping in `<a>`. I'll open a companion PR in Gutenberg.

 === Testing ===

 Register a test post type:

 {{{
 register_post_type( 'no_single', array(
     'label'       => 'No Single',
     'public'      => true,
     'has_archive' => true,
     'has_single'  => false,
     'show_in_rest' => true,
 ) );
 }}}

 Verify:
 * Archive page works
 * REST API returns posts
 * Query Loop displays posts
 * No "View" links in admin
 * Not in sitemaps
 * Single URLs 404 (no rewrite rules)

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/37376#comment:6>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list