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.
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:
- single-books-reviews.php
- single-books-author.php
- 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..


Discussion
Previously published comments, retained for reference. New comments are closed.
Jonathon
2012-07-26
Chris, Thank you for posting this, was very handy, and I was hoping you might have some insight on adapting this a bit for a custom condition? Just as your example, I have a Custom Post Type with lots of custom fields in each post. I have the content split across 3 templates now using the fake pages method above. The catch is, I have a second Custom Post Type that needs to load into the above mix, and needs to integrate into the URL scheme somehow. The primary CPT is Sports and displays posts such as Football, Baseball, Soccer, etc. and the info for those posts is segmented into 3 fake pages. I need a 4th fake page in that mix though, still based on any current given sport, that displays team photos from a Team Photos CPT (with registered taxonomies of gender, sport, and year). So, I currently have sitename.com/sport/football/byyear which would show the initial page and query for the team photos, but navigating through the team photos, the links want to swap to sitename.com/teamphotos/nameofpost, when ideally, it would be sitename.com/sport/football/byyear/teamphotos/mens/1999 (or similar). Any thoughts on how to accomplish this with the rewrites? Thank again, JonathonChris Rutherford
2012-08-27
I sent you an email - your comment had been marked as spam and I hadn't noticed it.Tudor
2013-01-03
Hello, First of all many thanks for the writeup, it might be a lifesaver for a new project I'm starting now. I've managed to successfully replicate the functionality, but I'm wondering if there's any way I could remove the rewrite base for the custom post type and still make this work. So in you case it would mean having mysite.com/to-kill-mockingbird/author/ instead of mysite.com/books/to-kill-mockingbird/author/. I've managed to remove the base with a plugin, but this also breaks the fake pages. Any thoughts would be greatly appreciated. Thanks, TudorTudor
2013-01-03
And btw, Happy New Year! :)Chris Rutherford
2013-01-03
I have a site on my development server using this that I'm working on now. I'll take a quick look and see if it's possible. I know we are removing the category base from posts but leaving them for the custom post type due to our structure - shouldn't be too hard to apply to the CPT.Chris Rutherford
2013-01-03
Take a look at the code under "Functions.php or plugin file". On line 28, change: [php]$newrules['books/([^/]+)/' . $slug . '/?$'] = 'index.php?books=$matches[1]&fpage=' . $slug;[/php] to: [php]$newrules['([^/]+)/' . $slug . '/?$'] = 'index.php?books=$matches[1]&fpage=' . $slug;[/php] Then, flush your rewrite rules (just go to Settings > Permalinks and click "save"). That should do the trick.Tudor
2013-01-03
Works! :)Johnny Goebel
2013-01-14
Hi, Great tutorial. I've almost got everything working. It works all the way to the single-post-index.php. However when I try to link the fake sub page it comes back with Page Not Found. I'm using the Atahualpa theme, and WP 3.4. I believe I've followed everything perfectly. I can send you whatever files you'd need to look at. Thanks, JohnnyChris Rutherford
2013-01-15
Send all related files to [email protected]Johnny Goebel
2013-01-15
Sent. Thanks!Johnny Goebel
2013-01-15
My man is legit. Took a look at my code. Problem was I didn't have the plug in activated (duh on me). Works great!Chris
2013-07-16
This is a great solution! I'm trying to add my custom taxonomies to the rewrite rules and hit a roadblock. Could you lend a hand? I want it to work as follows: trips/%taxonomy1%/%taxnomy2%/%postname%/ There might be an instance where only one of the taxonomies is being used or there will be nesting. Any help would be amazing.Steve
2013-08-12
Hello, thanks for providing this code. It works great for a single Custom Post Type. How would I get it to work for multiple Custom Post Types? I am working on a Project that has 17 Custom Post Types. I am trying to add a fake page for a details link. So each Page from each Custom post Type needs to have a Fake Details Page. Any help you could provide would be greatly appreciated. Thanks, SteveWaleed Asender
2014-01-05
Hi, I would like to thank you alot for this hack it saved my life. And just FYI it worked for me even though im using “/%category%/%postname%/” permalinks method. Thanks again,Pablo
2014-07-30
Hello Chris. This post is amazing and almost perfect to my use because i need one more level like: mysite.com/books/to-kill-mockingbird/author-list/the-author. How can I do that?Pablo
2014-08-04
I have another question. How can I use pagination? I'm trying plugin or manually, but no way works.Ray
2014-09-13
I need to find an answer to exactly the same question - how to include taxonomy terms in the slug - @Chris Rutherford, do you know how this might be possible? Great code - thanks! QRay
2014-09-13
The answer is to do something along the lines of: $newrules['books/(.*)/(.*)/([^/]+)/' . $slug . '/?$'] = 'index.php?book=$matches[3]&fpage=' . $slug; The important part is the "$matches[3]" - the index should make the placement of the search term - in our case, we have two taxonomy terms and then the CPT post slug - so we take the 3rd match. QAulia Ardianto
2015-01-07
Hi Steve, I'm facing a similar problem with multiple post types, did you managed to make it work? Thanks.Andy
2015-07-07
Great tutotrial, but I believe there is a little mistake. In single-book.php you used $current_fp but it only worked for me after using $fpage instead.Peter
2015-09-28
Thanks a lot! Can't get this to work with "Polylang"-Plugin - which creates a url structure wit an addtional /en/ in front..... is there a solution fpor this?paul
2016-05-09
Thank you very much for this. I'd been messing around trying to get my own custom rewrite rules to work for ages without success.Luyen
2016-06-24
Hi Chris, I've been wrestling with the same challenge for the last few weeks before I kick off a project. I'll be using custom fields (ACF) to store a lot of data that is really meant to be viewed as "Panels" or "Partials" as you would with Drupal. The approach I was thinking about was to use an Ajax function to load each sub-section, to keep the # of queries down i'd try to reference IDs in the custom fields for images, other posts etc, and avoid embedding images directly into the WYSIWIG. The biggest downside is of course from an SEO and URL stand-point, none of these pages would actually exist. It'd be essentially a JS toggle between sub-sections. I'm thinking I could use a combination of your approach with "actual" fake pages, and ajax functionality so I don't end up loading too much data on the initial page load. What do you think? :-)Mark
2017-05-09
Thanks for the great tutorial. Can an adjustment be made to handle one more level? Something like: Post: mysite.com/books/to-kill-mockingbird/ Subpage #1: mysite.com/books/to-kill-mockingbird/author-list/ Subpage #2: mysite.com/books/to-kill-mockingbird/author-list/author Thanks for your help.Mark
2017-05-09
Hi Pablo. I know it's been a couple years, but I wanted to see if you ever solved the "one more level" custom post type issue? I'm having the same issue, and thought I'd touch base to see if you had a solution. ThanksSonika
2017-06-22
Big-big-big thanks, Chris! Works excellent!!! WP 4.8Zemma
2017-12-19
Hi! I can't change the page title to anyting other than Hello World. I deleted the Hello World post and now 'Page not found' is the title. Can you help me out?Amit
2018-04-03
Hi I’m also looking for same. My website have multiple post types. Kindly guide, How would I get it to work for multiple Custom Post Types Thanks.Prashant Rajput
2019-10-19
Thanks, It works perfect. But i need pagination on the page so how can we add this? eg. 10.7.0.105/company/aurora/articles This i have created by referring your article. Now I want pagination on the article page so how to do that. 10.7.0.105/company/aurora-cannabis/articles/1 10.7.0.105/company/aurora-cannabis/articles/2Create sub page of sub pages using “fake pages” – PHP
2021-02-04
[…] found this tutorial to make so called fake pages, so I could dynamically create pages with custom fields. It works very […]Sohail Ahmed
2021-05-20
Not working with wordpress 5.7.2. Sub pages /author/, /reviews/ .. loads single content only and not single-books-author.Max
2022-09-17
Hi. Great solution, but child pages not in sitemap.xml, so it's no indexable in search. Maybe there is a solution to fix this? Thanks