Create dynamic sub posts for a WordPress custom post type

I needed a way to have sub posts (pages) automatically created for my custom post types. Tabbing the content with javascript would have been insufficient and that is not the correct way to deal with a large amount of content. Oddly, there are no plugins that I am aware of equivalent to the Drupal Views and Panels modules. It took me about a while to find a virtual or “fake pages” solution that fit my needs and allowed me to dynamically create pages with custom fields.

My sub-pages content is created from custom custom fields displayed neatly on post edit screens within meta boxes. This tutorial will assume you have an understanding of creating and managing custom fields – or that you have a plugin like the Meta Box plugin installed.

Here is the structure of my project site, using the custom post type “books”:

  • Book: To Kill a Mockingbird (mysite.com/books/to-kill-mockingbird/ – the actual post)
    • Author Biography (mysite.com/books/to-kill-mockingbird/author/) – virtual child page
    • Purchase Book (mysite.com/books/to-kill-mockingbird/purchase/) – virtual child page
    • Reviews (mysite.com/books/to-kill-mockingbird/reviews/) – virtual child page

The method I am using is heavily based upon a post @ BetterWP.net with some exceptions: We are using a custom post type, and the content for each page is not created by using or tags. Every post in your configured custom post type will have the child pages. This is 100% correct for my implementation, and will be exactly what most of you require.

Update: I added a demo and a child theme for Twenty-Eleven. View them here.

Update 2: This method will ONLY work if you have “/%postname%/” for permalinks.

Let’s get started 🙂

Functions.php or plugin file

This code can go into your functions.php but due to the number of lines your best bet is placing this code in /wp-content/plugins/fake-pages/fakes.php.

At each highlighted line, you will need to change “books” to your custom post type name. “Reviews“, “Purchase“, and “Author” should be changed to match your required page names. You can have as many child pages as you like, however, the URL will work for EVERY item created in your custom post type. This tutorial is based off of the “Twenty Eleven/Twenty Ten themes”.

<?php
/*
Plugin Name: Mysite.com Fake Pages
Plugin URI: http://www.mysite.com
Description: Creates fake sub pages for a custom content type.
Version: 1.0
Author: my name
Author URI: http://mysite.com
/* ----------------------------------------------*/

    // Fake pages' permalinks and titles. Change these to your required sub pages.
    $my_fake_pages = array(
        'reviews' => 'Reviews',
        'purchase' => 'Purchase',
        'author' => 'Author Bio'
    );
     
    add_filter('rewrite_rules_array', 'fsp_insertrules');
    add_filter('query_vars', 'fsp_insertqv');
     
    // Adding fake pages' rewrite rules
    function fsp_insertrules($rules)
    {
        global $my_fake_pages;
     
		$newrules = array();
		foreach ($my_fake_pages as $slug => $title)
			$newrules['books/([^/]+)/' . $slug . '/?$'] = 'index.php?books=$matches[1]&fpage=' . $slug;
     
        return $newrules + $rules;
    }
     
    // Tell WordPress to accept our custom query variable
    function fsp_insertqv($vars)
    {
        array_push($vars, 'fpage');
        return $vars;
    }

    // Remove WordPress's default canonical handling function
	
    remove_filter('wp_head', 'rel_canonical');
    add_filter('wp_head', 'fsp_rel_canonical');
    function fsp_rel_canonical()
    {
        global $current_fp, $wp_the_query;
     
        if (!is_singular())
            return;
     
        if (!$id = $wp_the_query->get_queried_object_id())
            return;
     
        $link = trailingslashit(get_permalink($id));
     
        // Make sure fake pages' permalinks are canonical
        if (!empty($current_fp))
            $link .= user_trailingslashit($current_fp);
     
        echo '<link rel="canonical" href="'.$link.'" />';
    }

?>

DO NOT forget to flush your permalinks! Go to Settings > Permalinks > Save to flush


Post type requirements

Your post type must have the following capabilities:

'public' => true,
'publicly_queryable' => true,
'query_var' => true,
'rewrite' => true,
'hierarchical' => true,

More information on registering custom post types: http://codex.wordpress.org/Function_Reference/register_post_type


Yoast WP-SEO Canonical Fix

If your using Yoasts WordPress SEO plugin, you need to remove the canonical link from your custom post type as we set proper canonical tags above. Below I disable canonical tags for “books” posts. Place this code in your functions.php if using the Yoast SEO plugin.

function wpseo_canonical_exclude( $canonical ) {
		global $post;
		if (is_singular('books')) {
    		$canonical = false;
    }
	return $canonical;
}

add_filter( 'wpseo_canonical', 'wpseo_canonical_exclude' );

Creating template files for parent and sub pages

Now, if you look at the tutorial I’m basing this off of (my previous post), they use “nextpage” to add content. That would be a MESS. Instead, all of my sub-pages are using custom custom fields. If you need to add large blocks of text, simply add an additional WYSIWYG text area to the edit post page.

Required Template files:

You will need to create templates like the ones below:

  • Main: single-books.php
    • Parent Post: single-books-index
    • Author Page: single-books-author
    • Purchase: single-books-author.php
    • Reviews: single-books-reviews.php

single-books.php

Copy your single.php template and rename it to single-[post-type-name].php. Mine is single-books.php

Find:

<?php get_template_part( 'content', 'single' ); ?>

Replace With:

		<?php if (!$current_fp) {
				get_template_part( 'single', 'books-index' );
			} else if ($current_fp == 'reviews') {
				get_template_part( 'single', 'books-reviews' );
			} else if ($current_fp == 'author') {
				get_template_part( 'single', 'books-author' );
			} else if ($current_fp == 'purchase') {
				get_template_part( 'single', 'books-purchase' );
			}; ?>
                

Find:

get_header();

After, add:

$current_fp = get_query_var('fpage');

Creating content template files

Save a copy of content-single.php as single-[post-type-name]-index.php. Since this is the post page, I am using the_content() to show the main content entered on the post edit screen. Mine is single-books-index.php. *note* I already wrote the child theme using “single”, it would be more appropriate to prefix with “content” like the original.

Save copies of single-[post-type-name]-index.php that correspond to your sub-page names:

  1. single-books-reviews.php
  2. single-books-author.php
  3. single-books-purchase.php

Your going to want to modify these pages to fit your needs. I assume you know how to include your custom fields into a page template. Download the example files at the end of the tutorial if you need more guidance.


Page navigation

You could do a loop to setup your navigation. I just hard coded my navigation like this:

<ul id="books-nav">
<li class="menu-item"><a href="<?php echo get_permalink(); ?>">Overview</a></li>
<li class="menu-item"><a href="<?php echo get_permalink() ?>reviews/">Book Reviews</a></li>
<li class="menu-item"><a href="<?php echo get_permalink() ?>author/">Author Bio</a></li>
<li class="menu-item"><a href="<?php echo get_permalink() ?>purchase/">Where to Purchase</a></li>
</ul>

You can check the current menu item using something similar to this:

if ($current_fp == 'reviews') { echo ' current-menu-item'; }

Title Tags

Since we cannot use WordPress SEO or All-in-One SEO pack to set our page titles, we need to set the page titles with a filter. This method will work fine with plugins that modify page titles.

Open your Functions.php or Plugin file, and add this to the end:

// Modify page titles
   add_filter('wp_title', 'fsp_titletag', 11, 3);
	
	function fsp_titletag($orig_title)
    {
			global $wp_the_query;
		    $current_fp = get_query_var('fpage');
			
			if (!$current_fp) {
				// If this is not a new page, use the existing title
				$title = $orig_title;
			} 
			else if ($current_fp == 'reviews') { 
			      $title = the_title() .' - Reader Reviews and Ratings - Yoursite.com';
			}		
			else if ($current_fp == 'purchase') { 
			      $title = 'Buy' . the_title() .' - Find the best price online';
			}
			else if ($current_fp == 'author') { 
			      $title = 'About the Author -'. the_title();
			};		
			return $title;  
    }

The titles won’t be very flexible like this, but work just fine for my implementation. You could look into using a custom field here to set a page title for each page.


H1 Page Titles

Again, my titles are hard coded within each template file. I use:

<h1 class="post-title"><?php the_title(); ?> Reviews</h1>

Download Twenty-Eleven Child Theme

I created a Twenty-Eleven child theme with a working example based upon books. Use these files as an example if you had trouble with any of the above, or need help with adding custom fields and bringing it all together 🙂

Download the child theme: Twenty Eleven Child Theme

View a live example: Demo Server – Down for maintenance..

This entry was posted in Wordpress. Bookmark the permalink.

28 Responses to Create dynamic sub posts for a WordPress custom post type

              Leave a Reply

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