Custom Post Types to Genesis WordPress Theme Framework

Custom Post Types to Genesis WordPress Theme Framework (RainaStudio Map 101)

wprocket

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.

wprocket-728x98_Black

wprocket-728x98_Black

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.

Custom Post Type Books in You Dashboard

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.

  1. Author Name
  2. Topics
  3. Publish Date
  4. 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.

Adding Author Name Meta Field
Adding Author Name Meta Field

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.

Merging Code for Additional Meta Fields

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.

Books Single Post Template Output

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.

Custom Archive Template for Books
Custom Archive Template for Books

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:

GenesisPro728x90

Facebook
Twitter
LinkedIn
Pinterest
Tumblr
Anwer Ashif

Anwer Ashif

Founder of RainaStudio. Help businesses and individuals to create and outstand their online presence. Our success rate is measurable. Our blog served all around the world and counting.

Leave a Reply

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