Want to create custom post types for your website, which is running on WordPress and Genesis Framework?
Yes!
Well, I am telling you, you have landed on the right page. 🙂
In this post, we are going to show you step by step how you can create custom post types while using the Genesis WordPress Theme Framework or any theme.
You’ll also learn in this comprehensive tutorial on how to create the custom archive and single post templates for custom post types in Genesis Framework.
We have two custom post types created on RainaStudio.com, one for ‘Code Snippets’ and another for ‘Case Studies.
We will create one custom post type with custom meta fields for bringing additional information/metadata.
In this tutorial, we are going to create Books as the custom post type. Then we create an archive and single templates for it. Stay tuning.
It is an article about the RainaStudio Map 101 blog series. Throughout the series, you’ll learn custom website development using WordPress and Genesis Framework. You can check previous posts of this blog series are following.
- Setup Localhost for Custom Website Development in PC
- Custom Front Page Template Design for Blog Website in Genesis
- Add Custom Heading Style Formats Without Plugin
Creating Custom Post Types in WordPress with Meta Fields and Custom Templates for Genesis Framework
You see Posts, Pages, Revision, Menus on your WordPress dashboard. Do you know all of them are post_type?
No! Alright, they are the built-in post types of WordPress. Please take a look at WordPress Codex post types; it will help you know the description of terms and what they do.
So, we can say a custom post type is nothing but a regular post_type with a different name in the database.
If you have visited WordPress Codex, you will be noticed there are several post types that are available by default, and they are initially available to use. Our post types will be like as follows:
- Books ( Post Type: ‘book’ )
So, first thing first, we need to map out what may add as metadata for Book post. Simple, author name; edition date; publish date; all are primary concerns for a book so that they will be our custom metadata field.
Registering Books as CPT(Custom Post Types)
Let’s create a file called cpt-book.php, and then go to the theme directory, create a folder called inc. Put the file(cpt-book.php) into the inc folder.
<?php // register cpt book function books_post() { // Array of custom labels for our custom post type backend. $labels = array( 'name' => 'Books', // General name. ie: Posts/Pages 'singular_name' => 'Book', // Name for single object. ie: Post/Page 'menu_name' => 'Books', // Name used in the menu 'name_admin_bar' => 'Book', // String for use in admin menu bar. - New Book 'add_new_item' => 'Add New Book', // Default is 'Add New Post/Add New Page' 'new_item' => 'New Book', // Default is New Post/New Page. 'edit_item' => 'Edit Book', // Default is Edit Post/Edit Page. 'view_item' => 'View Book', // Default is View Post/View Page. 'all_items' => 'All Books', // String for the submenu. ie: All Posts/All Pages. 'search_items' => 'Search Books', // Default is Search Posts/Pages 'parent_item_colon' => 'Parent Books:', // This string is used in hierarchical types. The default is 'Parent Page:'. 'not_found' => 'No books found.', // Default is No posts found/No pages found. 'not_found_in_trash' => 'No books found in Trash.', // Default is No posts found in Trash/No pages found in Trash. 'featured_image' => 'Book Cover', // Default is Featured Image. 'set_featured_image' => 'Set book cover', // Default is Set featured image. 'remove_featured_image' => 'Remove book cover', // Default is Remove featured image. 'use_featured_image' => 'Use as book cover', // Default is Use as featured image. 'items_list' => 'Books list', // String for the table hidden heading. 'items_list_navigation' => 'Books list navigation', // String for the table pagination hidden heading. 'filter_items_list' => 'Filter items list' // String for the table views hidden heading. ); $args = array( // A plural descriptive name for the post type marked for translation. If you don’t declare a custom label, WordPress will use the name of the custom post type by default. 'label' => 'Book', 'labels' => $labels, // Applying our labels array from above. 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'comments', 'custom-fields', ), // Array of features that the CPT will support. 'taxonomies' => array( 'category', 'post_tag' ), // An array of registered taxonomies like category or post_tag. 'hierarchical' => false, // Whether the post type is hierarchical (e.g. page). 'public' => true, // Whether a post type is intended to be used publicly. 'show_ui' => true, // Generates a default UI for managing this CPT in the admin. 'show_in_menu' => true, // Whether the custom post type should be visible in the menu. 'menu_position' => 5, // The position in the menu order in admin. 'menu_icon' => 'dashicons-book-alt', // This declares a custom icon for this CPT in the admin area. 'show_in_admin_bar' => true, // Whether to make this post type available in the WordPress admin bar. 'show_in_nav_menus' => true, //Whether post_type is available for selection in navigation menus. 'has_archive' => true, // Enables post type archives. 'exclude_from_search' => false, // Exclude for search engine. 'publicly_queryable' => true, // Whether queries can be performed on the front end as part of parse_request(). 'capability_type' => 'page', // Type of custom post type we will be dealing with. 'show_in_rest' => true // Whether to expose this post type in the REST API. ); // The register_post_type() is a function that WordPress recognizes as a custom post type generator. In this example it accepts two parameters which are the name of the post type itself and any arguments you would like to call. register_post_type( 'books', $args ); } // This line of code returns or calls our function above. add_action( 'init', 'books_post' );
Call cpt-book.php in Theme’s functions.php File
By calling that file into functions.php file, the code will act on the Dashboard. There are many ways to request a file into functions.php. But we will add the following snippet to functions.php file, and I think it’s another way which will work!
// Include php files from includes folder require_once( get_stylesheet_directory() . '/inc/cpt-book.php');
Or add the following snippet if you have only a standalone/PARENT theme installed.
/** * CPT Book Registration. */ require get_template_directory() . '/inc/cpt-book.php';
Here is the execution-screenshot of CODE SNIPPETS we added for BOOKS post type.
Adding Meta Fields for CPT Books
Alright, we have reached this point. Now, I’ll show you how we will register a couple of meta fields for the following data.
- Author Name
- Topics
- Publish Date
- Last Edition Data
So, let’s begin. Open the file cpt-book.php with your favorite code editor. I am using Atom now. It’s one of my favorites. Get your hand dirty with some codes.
// Create Meta Box 'Author Name' function book_author() { global $post; // Noncename needed to verify where the data originated echo '<input type="hidden" name="books_meta_nonce" id="books_meta_nonce" value="' . wp_create_nonce( plugin_basename(__FILE__) ) . '" />'; // Get the Metabox data if its already been entered $author_name = get_post_meta($post->ID, 'author_name', true); // Echo out the field echo '<label for="author_name" class="components-form-token-field__label">Add Author Name</label> <div class="components-form-token-field__input-container"> <input type="text" name="author_name" value="' . $author_name . '" placeholder="eg. Napoleon Hill" class="components-form-token-field__input"/> </div>'; } /** * Add Custom Metaboxes to Books CPT */ function add_book_metaboxes() { add_meta_box('book_author', 'Author Name', 'book_author', 'books', 'side', 'core'); } add_action( 'add_meta_boxes', 'add_book_metaboxes' );
Adding those lines of code, we just added the “Author Name” meta box to our BOOKS. Let’s look at the editor window of books. Here is the screenshot following.
If you put any data to that input filed and hit Save, it’ll not save to database or with a post. Because we haven’t added the code which saves the value or data into the field yet. Now, add the following code to save it for saving any given data to the meta field.
// Save the Metabox Data function save_books_meta_data($post_id, $post) { // verify this came from the our screen and with proper authorization, // because save_post can be triggered at other times if ( !isset($_POST['books_meta_nonce']) || !wp_verify_nonce( $_POST['books_meta_nonce'], plugin_basename(__FILE__) )) { return $post->ID; } // If this is an autosave, our form has not been submitted, so we don't want to do anything. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Is the user allowed to edit the post or page if ( !current_user_can( 'edit_post', $post->ID )) { return $post->ID; } // OK, we're authenticated: we need to find and save the data // We'll put it into an array to make it easier to loop though. $books_meta['author_name'] = $_POST['author_name']; // Add values of $books_meta as custom fields foreach ($books_meta as $key => $value) { // Cycle through the $books_meta array! if( $post->post_type == 'revision' ) return; // Don't store custom data twice $value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely) if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value update_post_meta($post->ID, $key, $value); } else { // If the custom field doesn't have a value add_post_meta($post->ID, $key, $value); } if(!$value) delete_post_meta($post->ID, $key); // Delete if blank } } add_action('save_post', 'save_books_meta_data', 1, 2); // save the custom fields
If you test saving any value to that filed now, it’ll work and retrieve the value to the field even after the page reloading, that’s mean it’s working perfectly so far.
I guess you have understood how functions are working here. Now, time to merge the code for other meta fields topics, publish date, and last edition date.
Merging Code for Additional Meta Fields
We will not create or write any new function for additional meta fields. We going to replace book_author() function with books_meta_fields() and add markup for additional fields. So, here you go.
// Create Meta Box 'Author Name'; 'Topics'; 'Publish Date'; 'Last Edition Date' function books_meta_fields() { global $post; // Noncename needed to verify where the data originated wp_nonce_field( basename( __FILE__ ), 'books_meta_nonce' ); // Get the Metabox data if its already been entered $author_name = get_post_meta($post->ID, 'author_name', true); $topics = get_post_meta($post->ID, 'topics', true); $publish_date = get_post_meta($post->ID, 'publish_date', true); $last_edition_date = get_post_meta($post->ID, 'last_edition_date', true); // Echo out the field echo '<label for="author_name" class="components-form-token-field__label">Add Author Name</label> <div class="components-form-token-field__input-container"> <input id="book_author_name" type="text" name="author_name" value="' . $author_name . '" placeholder="eg. Napoleon Hill" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; // Echo out the 'topics' field echo '<label for="topics" class="components-form-token-field__label">Add Topics</label> <div class="components-form-token-field__input-container"> <input id="book_topics" type="text" name="topics" value="' . $topics . '" placeholder="eg. Business, Mindset" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; // Echo out the 'topics' field echo '<label for="publish_date" class="components-form-token-field__label">Add Publish Date</label> <div class="components-form-token-field__input-container"> <input id="book_publish_date" type="date" name="publish_date" value="' . $publish_date . '" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; // Echo out the 'last_edition_date' field echo '<label for="last_edition_date" class="components-form-token-field__label">Add Last Edition Date</label> <div class="components-form-token-field__input-container"> <input id="book_last_edition_date" type="date" name="last_edition_date" value="' . $last_edition_date . '" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; } /** * Add Custom Metaboxes to Books CPT */ function add_book_metaboxes() { add_meta_box('books_meta_fields', 'Books Meta Data', 'books_meta_fields', 'books', 'side', 'core'); } add_action( 'add_meta_boxes', 'add_book_metaboxes' );
As we just updated the code above and changed function name, we needed to replace add_meta_box(‘book_author’, ‘Author Name’, ‘book_author’, ‘books’, ‘side’, ‘core’); with add_meta_box(‘books_meta_fields’, ‘Books Meta Data’, ‘books_meta_fields’, ‘books’, ‘side’, ‘core’);. And we have done adding all of those meta fields for ‘Books‘ CPT so far.
Following is a screenshot of how it is looking on my end.
Now, time to update save_books_meta_data() function to save additional metadata. So, let’s do it. We just need to add following snippet under $books_meta[‘author_name’] = $_POST[‘author_name’];.
$books_meta['topics'] = $_POST['topics']; $books_meta['publish_date'] = $_POST['publish_date']; $books_meta['last_edition_date'] = $_POST['last_edition_date'];
We will see all code together from registering post type to creating meta boxes/fields for books. It’s for you like me who loves to see things done altogether.
<?php // register cpt book function books_post() { // Array of custom labels for our custom post type backend. $labels = array( 'name' => 'Books', // General name. ie: Posts/Pages 'singular_name' => 'Book', // Name for single object. ie: Post/Page 'menu_name' => 'Books', // Name used in the menu 'name_admin_bar' => 'Book', // String for use in admin menu bar. - New Book 'add_new_item' => 'Add New Book', // Default is 'Add New Post/Add New Page' 'new_item' => 'New Book', // Default is New Post/New Page. 'edit_item' => 'Edit Book', // Default is Edit Post/Edit Page. 'view_item' => 'View Book', // Default is View Post/View Page. 'all_items' => 'All Books', // String for the submenu. ie: All Posts/All Pages. 'search_items' => 'Search Books', // Default is Search Posts/Pages 'parent_item_colon' => 'Parent Books:', // This string is used in hierarchical types. The default is 'Parent Page:'. 'not_found' => 'No books found.', // Default is No posts found/No pages found. 'not_found_in_trash' => 'No books found in Trash.', // Default is No posts found in Trash/No pages found in Trash. 'featured_image' => 'Book Cover', // Default is Featured Image. 'set_featured_image' => 'Set book cover', // Default is Set featured image. 'remove_featured_image' => 'Remove book cover', // Default is Remove featured image. 'use_featured_image' => 'Use as book cover', // Default is Use as featured image. 'items_list' => 'Books list', // String for the table hidden heading. 'items_list_navigation' => 'Books list navigation', // String for the table pagination hidden heading. 'filter_items_list' => 'Filter items list' // String for the table views hidden heading. ); $args = array( // A plural descriptive name for the post type marked for translation. If you don’t declare a custom label, WordPress will use the name of the custom post type by default. 'label' => 'Book', 'labels' => $labels, // Applying our labels array from above. 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'comments', 'custom-fields', ), // Array of features that the CPT will support. 'taxonomies' => array( 'category', 'post_tag' ), // An array of registered taxonomies like category or post_tag. 'hierarchical' => false, // Whether the post type is hierarchical (e.g. page). 'public' => true, // Whether a post type is intended to be used publicly. 'show_ui' => true, // Generates a default UI for managing this CPT in the admin. 'show_in_menu' => true, // Whether the custom post type should be visible in the menu. 'menu_position' => 5, // The position in the menu order in admin. 'menu_icon' => 'dashicons-book-alt', // This declares a custom icon for this CPT in the admin area. 'show_in_admin_bar' => true, // Whether to make this post type available in the WordPress admin bar. 'show_in_nav_menus' => true, //Whether post_type is available for selection in navigation menus. 'has_archive' => true, // Enables post type archives. 'exclude_from_search' => false, // Exclude for search engine. 'publicly_queryable' => true, // Whether queries can be performed on the front end as part of parse_request(). 'capability_type' => 'page', // Type of custom post type we will be dealing with. 'show_in_rest' => true // Whether to expose this post type in the REST API. ); // The register_post_type() is a function that WordPress recognizes as a custom post type generator. In this example it accepts two parameters which are the name of the post type itself and any arguments you would like to call. register_post_type( 'books', $args ); } // This line of code returns or calls our function above. add_action( 'init', 'books_post' ); // Create Meta Box 'Author Name'; 'Topics'; 'Publish Date'; 'Last Edition Date' function books_meta_fields() { global $post; // Noncename needed to verify where the data originated wp_nonce_field( basename( __FILE__ ), 'books_meta_nonce' ); // Get the Metabox data if its already been entered $author_name = get_post_meta($post->ID, 'author_name', true); $topics = get_post_meta($post->ID, 'topics', true); $publish_date = get_post_meta($post->ID, 'publish_date', true); $last_edition_date = get_post_meta($post->ID, 'last_edition_date', true); // Echo out the field echo '<label for="author_name" class="components-form-token-field__label">Add Author Name</label> <div class="components-form-token-field__input-container"> <input id="book_author_name" type="text" name="author_name" value="' . $author_name . '" placeholder="eg. Napoleon Hill" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; // Echo out the 'topics' field echo '<label for="topics" class="components-form-token-field__label">Add Topics</label> <div class="components-form-token-field__input-container"> <input id="book_topics" type="text" name="topics" value="' . $topics . '" placeholder="eg. Business, Mindset" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; // Echo out the 'topics' field echo '<label for="publish_date" class="components-form-token-field__label">Add Publish Date</label> <div class="components-form-token-field__input-container"> <input id="book_publish_date" type="date" name="publish_date" value="' . $publish_date . '" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; // Echo out the 'last_edition_date' field echo '<label for="last_edition_date" class="components-form-token-field__label">Add Last Edition Date</label> <div class="components-form-token-field__input-container"> <input id="book_last_edition_date" type="date" name="last_edition_date" value="' . $last_edition_date . '" class="widefat components-form-token-field__input" cols="50" rows="5"/> </div>'; } /** * Add Custom Metaboxes to Books CPT */ function add_book_metaboxes() { add_meta_box('books_meta_fields', 'Books Meta Data', 'books_meta_fields', 'books', 'side', 'core'); } add_action( 'add_meta_boxes', 'add_book_metaboxes' ); // Save the Metabox Data function save_books_meta_data($post_id, $post) { // verify this came from the our screen and with proper authorization, // because save_post can be triggered at other times if ( !isset($_POST['books_meta_nonce']) || !wp_verify_nonce( $_POST['books_meta_nonce'], basename(__FILE__) )) { return $post->ID; } // If this is an autosave, our form has not been submitted, so we don't want to do anything. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Is the user allowed to edit the post or page if ( !current_user_can( 'edit_post', $post->ID )) { return $post->ID; } // OK, we're authenticated: we need to find and save the data // We'll put it into an array to make it easier to loop though. $books_meta['author_name'] = $_POST['author_name']; $books_meta['topics'] = $_POST['topics']; $books_meta['publish_date'] = $_POST['publish_date']; $books_meta['last_edition_date'] = $_POST['last_edition_date']; // Add values of $books_meta as custom fields foreach ($books_meta as $key => $value) { // Cycle through the $books_meta array! if( $post->post_type == 'revision' ) return; // Don't store custom data twice $value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely) if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value update_post_meta($post->ID, $key, $value); } else { // If the custom field doesn't have a value add_post_meta($post->ID, $key, $value); } if(!$value) delete_post_meta($post->ID, $key); // Delete if blank } } add_action('save_post', 'save_books_meta_data', 1, 2); // save the custom fields
Creating a Single Post Template for Books
Books CPT will use single.php by default if we don’t create any particular template for it. So, now, let’s create a file in your child theme directory/folder called single-books.php. And put the following snippet into the record.
<?php /* * Template for Books CPT. */ // Add Testimonial 'after-content' add_action( 'genesis_entry_footer', 'books_meta' ); function books_meta() { $author_name = get_post_meta( get_the_ID(), 'author_name', true ); $topics = get_post_meta( get_the_ID(), 'topics', true ); $publish_date = get_post_meta( get_the_ID(), 'publish_date', true ); $last_edition_date = get_post_meta( get_the_ID(), 'last_edition_date', true ); if ( $author_name || $topics || $publish_date || $last_edition_date ) { ?> <ul> <li><strong>Author Name: </strong><?php echo $author_name; ?></li> <li><strong>Topics: </strong><?php echo $topics; ?></li> <li><strong>Publish Date: </strong><?php echo $publish_date; ?></li> <li><strong>Last Edition Date: </strong><?php echo $last_edition_date; ?></li> </ul> <?php } } // Run Genesis loop genesis();
As sooner as the file gets saved, you’ll see the ‘Books‘ CPT using that template. You can use a plugin called “What The File” to see which template is used.
Here is a screenshot of the output of a single book template.
Creating Archive Page Template for Books CPT
You can design and get an HTML markup for the archive template. I am just doing some basic. So, let’s create a file called archive-books.php.
We are going to make the archive page template full width so there will be no sidebar. Also, we will remove all default markup to execute our custom markup. So, put/add the following snippet to the file.
<?php /* Books Archive Page Template */ // Add 'archive_books_template' ID function be_site_inner_attr( $attributes ) { // Add an id of 'archive_books_template' for accessible skip links $attributes['id'] = 'archive_books_template'; // Add the attributes from .entry, since this replaces the main entry $attributes = wp_parse_args( $attributes, genesis_attributes_entry( array() ) ); return $attributes; } add_filter( 'genesis_attr_body', 'be_site_inner_attr' ); // Force full width content add_filter( 'genesis_pre_get_option_site_layout', '__genesis_return_full_width_content' ); // Remove standard post content output. remove_action( 'genesis_entry_content', 'genesis_do_post_content' ); // Remove the entry header markup (requires HTML5 theme support) remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_open', 5 ); remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_close', 15 ); // Add Books to entry add_action( 'genesis_entry_content', 'books_post_loop' ); function books_post_loop() { ?> <div class="books_post_wrapper"> <div class="wrap"> <?php $featured_img = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'single-post-thumbnail' ); ?> <img class="book_cover" src="<?php echo $featured_img[0]; ?>"> <div class="books-intro"> <p class="exceprt"><?php the_excerpt(); ?></p> <span><a href="<?php the_permalink(); ?>" class=" book_url">Read the Book...</a></span> </div> </div> </div><?php } // Run the Genesis loop genesis();
Here is the screenshot of the custom archive page so far. It is not looking good, though.
Conclusion
We are at the end of this tutorial. I hope you have understood the long tutorial of registering custom post types on WordPress. If you have any questions to implement these code to your website, feel free to ask or comment below.
We are more than happy to help you. 🙂
You will love The following tutorials:
- How to Install and Configure Really Simple SSL WordPress Plugin
- How to Add Simple Social Icons Widget Plugin in WordPress
- How to Add Custom JavaScript to WordPress
- How to Add Facebook Customer Chat to Your WordPress Website
- How to Show Related Posts in WordPress
- How To Change Text Color In WordPress Post
- How to Develop Mega Menu in WordPress
- Template Page Attributes for Custom Post Type [WP Support]
- Add Custom CSS to WordPress 5.0 Admin for Post Editor
- Custom Front Page Template Design for Blog Website in Genesis (RainaStudio Map 101)
- Add Custom Heading Style Formats to WP Visual Editor Without Plugin (RainaStudio Map 101)
- Custom Related Posts After Entry Footer in Genesis (RainaStudio Map 101)
- Custom Social Share Buttons with Counter in Genesis (RainaStudio Map 101)