There are many different types of content in WordPress.
Internally, all of the Post Types are stored in the same place — in the wp_posts database table — but are
differentiated by a database column called post_type.
Different Post Types are displayed by different Template files.
Default Post Types
There are several default Post Types readily available to users or internally used by the WordPress installation. The most common are:
- Post (Post Type:
post) — the most used type. Posts support tags and comments and appear in the blog listing and in the RSS feed. - Page (Post Type:
page) — static pages. They are excluded from the blog listing and the feed, and their templates normally don’t render tags or comments. - Attachment (Post Type:
attachment) — holds the information about every file uploaded through the standard uploader. - Revision (Post Type:
revision) — backup copies of posts. - Navigation menu (Post Type:
nav_menu_item) — menu items are stored as posts too. - Block templates (Post Type:
wp_template) - Template parts (Post Type:
wp_template_part)
Creating a post type
Once a custom post type is registered, it gets a new top-level administrative screen that can be used to manage and create posts of that type.
To register a new post type, you use the open_in_new register_post_type() function.
function register_my_custom_post_type() {
register_post_type( 'product',
array(
'labels' => array(
'name' => __( 'Products', 'textdomain' ),
'singular_name' => __( 'Product', 'textdomain' ),
),
'public' => true,
'has_archive' => true,
)
);
}
add_action( 'init', 'register_my_custom_post_type' );
Custom slug
To set a custom slug for the slug of your custom post type all you need to do is add a key => value pair to the rewrite key in the register_post_type() arguments array.
function wporg_custom_post_type() {
register_post_type('wporg_product',
array(
'labels' => array(
'name' => __( 'Products', 'textdomain' ),
'singular_name' => __( 'Product', 'textdomain' ),
),
'public' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => 'products' ), // my custom slug
)
);
}
add_action('init', 'wporg_custom_post_type');
Note that register_post_type() must be called inside the init hook — calling it earlier or later
will not work.
Labels
labels controls every piece of text WordPress shows for the type in the admin area. A full set looks
like this:
$labels = [
'name' => 'Clients',
'singular_name' => 'Client',
'add_new' => 'Add New',
'add_new_item' => 'Add New Client',
'edit_item' => 'Edit Client',
'new_item' => 'New Client',
'all_items' => 'All Clients',
'view_item' => 'View Client',
'search_items' => 'Search Clients',
'not_found' => 'No clients found.',
'not_found_in_trash' => 'No clients found in Trash.',
'menu_name' => 'Clients', // the link in the admin menu
];
Admin menu and supported features
$args = [
'labels' => $labels,
'public' => true,
'show_ui' => true, // show the interface in the admin area
'has_archive' => true,
'menu_icon' => 'dashicons-groups',
'menu_position' => 20, // the position in the admin menu
'supports' => [ 'title', 'editor', 'thumbnail' ],
];
register_post_type( 'client', $args );
menu_icon accepts a open_in_new Dashicons
name, or a URL to
your own image:
'menu_icon' => get_stylesheet_directory_uri() . '/img/function_icon.png',
supports decides which meta boxes appear on the editing screen — title, editor, comments,
author, thumbnail, excerpt, custom-fields, revisions and so on.
Enabling the block editor
A custom post type gets the classic editor unless you expose it to the REST API:
$args = [
'labels' => $labels,
'public' => true,
'show_ui' => true,
'has_archive' => true,
// ...
'show_in_rest' => true, // Here it is.
];
Attaching taxonomies
A custom post type can reuse the built-in categories:
'taxonomies' => [ 'category' ],
Be aware that the categories will then be shared with regular posts, which is not always what you want — in that case register a custom taxonomy instead.
Once the taxonomy is attached, both types can be queried together:
query_posts( [
'category_name' => $category->slug,
'post_type' => [ 'post', 'success-story' ],
] );
Flushing the rewrite rules
Permalinks for a new post type will return 404 until the rewrite rules are rebuilt. It’s enough to do it once, normally from the plugin activation hook:
flush_rewrite_rules( false );
Naming
Use hyphens in the post type name (success-story rather than success_story), because the name ends up
in the URL.
Using in a template
The following templates can display Custom post types:
single-{post-type}archive-{post-type}searchindex
Add a new record
$post_data = array(
'post_title' => 'New Post Title',
'post_content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'post_status' => 'publish',
'post_type' => 'custom_post_type', // Replace with your custom post type slug
);
$post_id = wp_insert_post($post_data);
if (!is_wp_error($post_id)) {
// Post inserted successfully, you can perform additional actions here
echo 'New post added with ID: ' . $post_id;
} else {
// Handle the error
echo 'Error adding new post: ' . $post_id->get_error_message();
}
Queries
Use open_in_new WP_Query class for such operations.
$query = new WP_Query( [
'title' => $name,
'post_type' => 'employer',
] );
if ( $query->have_posts() ) {
return true;
}
To render the results, run a secondary loop over the query object and reset the global post data afterwards:
<?php
$clients = new WP_Query( [
'post_type' => 'client',
'posts_per_page' => -1,
] );
while ( $clients->have_posts() ) : $clients->the_post(); ?>
...
<?php endwhile; wp_reset_postdata(); ?>
Show the existing post types
You can use this request to see the existing post types:
SELECT post_type, COUNT(*) AS total_posts
FROM wp_posts
GROUP BY post_type
ORDER BY total_posts DESC;
If you want to sort post types (post_type) by the date of their first appearance (i.e., by the minimum ID), here’s the appropriate SQL query:
SELECT post_type, COUNT(*) AS total_posts, MIN(ID) AS first_post_id
FROM wp_posts
GROUP BY post_type
ORDER BY first_post_id ASC;
Andrew Dorokhov