In WordPress default installation, we’re given “Post” and “Page” – 2 types of “post type”.

What if we need something other than just “post” and “page”? Maybe “book” or “project”?

Yes, we can create as many custom post type as we wish by using register_post_type function. Let’s use “Book” as an example.

Creating Custom Post Type

First, we hook init action to our new callback function. Place the following code in functions.php:

add_action( 'init', 'custom_register_my_cpts' );

Next, we create callback function custom_register_my_cpts in functions.php. This function will create the custom post type:

add_action( 'init', 'custom_register_my_cpts' );

function custom_register_my_cpts() {

	/**
	 * Post Type: Book
	 */

	$labels = array(
		"name" => __( "Books", "twentynineteen" ),
		"singular_name" => __( "Book", "twentynineteen" ),
		"add_new_item" => "Add New Book"
	);

	$args = array(
		"label" => __( "Books", "twentynineteen" ),
		"labels" => $labels,
		"description" => "",
		"public" => true,
		"publicly_queryable" => true,
		"show_ui" => true,
		"delete_with_user" => false,
		"show_in_rest" => true,
		"rest_base" => "",
		"rest_controller_class" => "WP_REST_Posts_Controller",
		"has_archive" => false,
		"show_in_menu" => true,
		"show_in_nav_menus" => true,
		"exclude_from_search" => false,
		"capability_type" => "post",
		"hierarchical" => false,
		"rewrite" => array( "slug" => "book", "with_front" => true ),
		"query_var" => true,
		"supports" => array( "title" ),
	);

	register_post_type( "book", $args );

}

Now you should be able to see “Book” admin menu in the dashboard.

About Custom Post Type’s Capability

Pay attention to the argument “capability_type” => “post” above – What does it mean?

With capability_type, we can specify the capability “identifier” for a post type. When an “identifier” is specified, WordPress automatically uses the “identifier” to generate the following capabilities:

edit_(identifier)
read_(identifier)
delete_(identifier)
edit_(identifier in plural)
edit_others_(identifier in plural)
publish_(identifier in plural)
read_private_(identifier in plural)

“What are these “capabilities” anyway? How are they being used?”

In general, WordPress uses these “capabilities” to decide whether your account can view/edit/delete or publish a specific post type. Your role will need to be assigned the specific post type capability if you want to be able to do something with that post type. For example, if you want to be able to edit “XXX” post type, your role needs to be assigned the “edit_xxx” capability.

In our codes above, we set “capability_type” => “post” and so the capabilities generated for “Book” post type will be:

edit_post
read_post
delete_post
edit_posts
edit_others_posts
publish_posts
read_private_posts

“But, we have not assign any of capability to my role, have we? Why I can see “Book” in the admin menu?”

That’s because by default, WordPress has already assigned these “_post” and “_posts” capabilities to your role due to the default “post” post type.

Let’s try changing “post” to “book” in “capability_type” => “book” and then save functions.php. You’ll notice the “Book” menu item disappears immediately from the admin menu as “capability_type” => “book” generates the following capabilities:

edit_book
read_book
delete_book
edit_books
edit_others_books
publish_books
read_private_books

You can’t see “Book” post type now because your role has not been assigned with any “_book” or “books” capability.

(* What’s the difference between singular edit_book and plural edit_books? That’s for another article.)

Assigning Capability to User Role

To assign new capability to user role, we need the add_cap function. Let’s try adding the capabilities to Administrator and Editor role. Add the following codes to functions.php:

add_action( 'admin_init', 'add_book_meta_caps');

function add_book_meta_caps() {
    
    // gets specific role(s)
    $roles = array( get_role('editor'), get_role('administrator') );
	
	// Loop through the roles and add_cap
	foreach($roles as $role) {
		if($role) {
		    $role->add_cap('edit_book');
		    $role->add_cap('read_book');
		    $role->add_cap('delete_book');
		    $role->add_cap('edit_books');
		    $role->add_cap('edit_others_books');
		    $role->add_cap('publish_books');
		    $role->add_cap('read_private_books');
		}
	}
}

Save functions.php and then refresh the dashboard. Voila! If you’d like to limit the capabilities assigned to the role, skip those add_cap lines with the unwanted capabilities.

Do note that these capabilities added to roles are all saved in the database. You’ll need to use remove_cap function to correctly remove previously assigned capabilities. Example below:

add_action( 'admin_init', 'remove_book_meta_caps');

function remove_book_meta_caps() {
    
    // gets specific role(s)
    $roles = array( get_role('editor'), get_role('administrator') );
	
	// Loop through the roles and remove_cap
	foreach($roles as $role) {
		if($role) {
		    $role->remove_cap('publish_books');
		}
	}
}

Full Code

Here’s the full working codes:

add_action( 'init', 'custom_register_my_cpts' );

function custom_register_my_cpts() {

	/**
	 * Post Type: Book
	 */

	$labels = array(
		"name" => __( "Books", "twentynineteen" ),
		"singular_name" => __( "Book", "twentynineteen" ),
		"add_new_item" => "Add New Book"
	);

	$args = array(
		"label" => __( "Books", "twentynineteen" ),
		"labels" => $labels,
		"description" => "",
		"public" => true,
		"publicly_queryable" => true,
		"show_ui" => true,
		"delete_with_user" => false,
		"show_in_rest" => true,
		"rest_base" => "",
		"rest_controller_class" => "WP_REST_Posts_Controller",
		"has_archive" => false,
		"show_in_menu" => true,
		"show_in_nav_menus" => true,
		"exclude_from_search" => false,
		"capability_type" => "book",
		"hierarchical" => false,
		"rewrite" => array( "slug" => "book", "with_front" => true ),
		"query_var" => true,
		"supports" => array( "title" ),
	);

	register_post_type( "book", $args );

}

add_action( 'admin_init', 'add_book_meta_caps');

function add_book_meta_caps() {
    
    // gets specific role(s)
    $roles = array( get_role('editor'), get_role('administrator') );
	
	// Loop through the roles and add_cap
	foreach($roles as $role) {
		if($role) {
		    $role->add_cap('edit_book');
		    $role->add_cap('read_book');
		    $role->add_cap('delete_book');
		    $role->add_cap('edit_books');
		    $role->add_cap('edit_others_books');
		    $role->add_cap('publish_books');
		    $role->add_cap('read_private_books');
		}
	}
}