Jeremy Felt

Exploring Intention When Registering Custom Post Types In WordPress

I’ve often found myself confused when trying to determine the intent of the optional arguments available to the WordPress function register_post_type(). While many are fairly obvious, there were a few that needed some better definition. I set off this weekend to update the register_post_type() Codex article as well as the associated inline docs in post.php.

As part of that refresh, I’m hoping to use this write up as a way to further explore what’s actually going on behind the scenes.

To serve as a very brief guide, here is an example use of register_post_type(). After, I’ll dive into a few of the arguments to expand on the intent involved with each.:

[gist id=2625099]


Implies overall intent for the post type.

The word public in this case should not be confused with front-end. There is no hard definition, and a post type can be public while remaining hidden from any front-end view. The setting for public is used for admin, front-end and XML-RPC requests and should not be underestimated.

If no additional arguments are set, the value for public also helps to trigger values for publicly_queryable, exclude_from_search, show_ui, and show_in_nav_menus as such:

[gist id=2625156]

Any easy assumption to make here is that public only triggers other arguments. That is not correct. While other arguments can inherit its value, public is very much an argument on its own.

How ‘public’ is used in WordPress (as of 3.4)

  • Checked for as ‘true’ during redirect_guess_404_permalink() in canonical.php if the ‘name’ query var exists without a request for ‘post_type’.
  • Checked for as ‘true’ during wp_link_query() in class-wp-editor.php to provide the list of possible internal links when using WP Editor.
  • Checked for as ‘true’ during _get_last_post_time() in post.php to query for the last posted post object.
  • Checked for as ‘false’ when looking for lost attachments in upload.php to help exclude post types (in addition to attachments) that attachments cannont be attached to.
  • Checked for as ‘true’ during find_posts_div() in template.php to help find posts that attachments can be attached to.
  • Depended on when using _prepare_post_type() and wp_getPostTypes() in class-wp-xmlrpc-server.php to provide the post type’s ‘public’ status to XML-RPC requests.
  • Checked for as true during wp_admin_bar_edit_menu() in wp-includes/admin-bar.php to display the ‘View Item’ link in the admin bar.
    • The show_ui argument is used for this on the front end.
  • Checked for as true during redirect_canonical() in canonical.php when trying to redirect based on a valid page, post or attachment ID passed via query_var.
  • Checked for as false during _update_blog_date_on_post_publish()  and _update_blog_date_on_post_delete() in ms-blogs.php to help decide whether or noth to update a site’s last updated time in a multisite environment.
  • Used during edit-form-advanced.php when determining whether to show a sample permalink and the associated Edit, View Post and Get Shortlink buttons.
  • Used when displaying single rows in class-wp-posts-list-table.php to determine if preview/view links should be shown .
  • list still in progress, feel free to chip in…


Should the post type be queried for on the front-end via parse_request()?

This is very much related to what can be seen by a user on the front-end of a WordPress installation. Most URLs associated with a post type will cause a related query_var to be available during parse_request() in order to handle the direction of the page load.

In fact, the only use of publicly_queryable in core code outside of register_post_type() is in class-wp.php as part of parse_request() when the post_type is set as a query var.

  • If $query_var[‘post_type’] is a string and matches a post_type that is publicly_queryable, it continues.
  • If $query_var[‘post_type’] is a string and does not match a publicly_queryable post_type, the $query_var[‘post_type’] is unset entirely.
  • If $query_var[‘post_type’] is an array, it is matched with an array of post types that have publicly_queryable set to true.

If not explicitly defined, publicly_queryable inherits the public setting, which is false by default.


Should the post type be available to front-end search requests?

This argument is checked for during get_posts() in query.php only if the post_type of the query is set to ‘any’. A check is done then for all post types that have ‘exclude_from_search’ set to false.

For any of the other conditions, whether multiple post types or single post types are specified, no check is done to see if should be exluded from search. This results in slightly unexpected search behavior, as seen in this document of test results.

If not explicitly defined, exclude_from_search inherits the opposite of the public setting, which results in ‘true’ by default.


What should be used as the query_var key in parse_request()?

This helps control the query_var used when a URL is loaded to be processed by parse_request().

  • If true, a post loaded at site/?{post_type_key}={post-slug} will be successful, as the query_var is set to the post_type key.
  • If false, a post loaded at site/?{post_type_key}={post-slug} will not be successful, as the query_var is not registered for use.
  • If a string, such as ‘my-cpt-slug’, is set, a post loaded at site/?my-cpt-slug={post-slug} will be successful.

By default, query_var is set to true and the key used to register the post type becomes the query_var string.

In Progress….

This is very much a work in progress, especially with the settings for public, as its value is used far more in WordPress core than I’ve been able to find so far. I’ll be updating this document as I find more examples, and would be happy to include any input. While exploring, I’ve compiled a horribly formatted document (saved as a Gist here) that attempts to provide examples for each combination of some relative arguments.

Fire away!

Responses and reactions


Alexander Perez replied on 

Thanks for this, the fact that 'publicly_queryable' should be set to true was what I was looking for. It should be the top result in google for "custom post type using index.php".

Leave a Reply

Your email address will not be published. Required fields are marked *

The only requirement for your mention to be recognized is a link to this post in your post's content. You can update or delete your post and then re-submit the URL in the form to update or remove your response from this page.

Learn more about Webmentions.