Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I have a couple of custom post types registered. I would like to display all "posts" from each custom post type on its own page, and these pages must be visible in the nav menu.

It would be great to only have one page-custom.php template as to a page template for each custom post type. Is it possible to create something like this?

share|improve this question
add comment

1 Answer 1

up vote 3 down vote accepted

If you have a look at the template hierarchy, custom post types are usually displayed on archive templates. Normal template hierarchy does not make provision for page.php type templates to be used to display custom post types by default.

The problem with archive templates is that they don't automatically get added to the default nav menu, and creating a custom menu to create links is not always the most convenient way to go.

The way to go here is to use WP_Query to create a custom query for the loop to include custom post types. WP_Query have a post_type type parameter which is used to call post types.

So, the following needs to be modified to make this work:

Firstly, create a custom page.php template

To create the custom page.php you need to copy your theme's page.php and rename it something like page-cpt.php. Now open it and change the loop. For the sake of this answer, I've used the default twentyfourteen theme. Delete everything inside the template and replace it with this code

<?php
/**
 * Template Name: Custom Post Type Page
 */
get_header(); ?>

<?php
//See if we have any values
$post_meta=array();
$post_meta = get_post_meta( $post->ID,false );
$posttype = isset( $post_meta['_cpt_post_type'] ) ? $post_meta['_cpt_post_type'][0] : 1;
$orderby = isset( $post_meta['_cpt_order_by'] ) ? $post_meta['_cpt_order_by'][0] : 'date';
$asc = isset( $post_meta['_cpt_asc'] ) ? $post_meta['_cpt_asc'][0] : 'DESC';
$post_count = isset( $post_meta['_cpt_post_count'] ) ? $post_meta['_cpt_post_count'][0] : get_option('posts_per_page');
if(!$post_count || !is_numeric( $post_count )) $post_count = get_option('posts_per_page');
?>  
<div id="main-content" class="main-content">

    <div id="primary" class="content-area">
        <div id="content" class="site-content" role="main">

        <?php the_post(); ?>
        <div class="entry-content">
            <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

            <?php if( $post->post_content ) : ?>

                    <header class="entry-header">
                        <h1 class="entry-title"><?php echo the_title(); ?></h1>
                    </header><!-- .entry-header -->

                <div class="entry-content">
                    <?php the_content(); ?>
                    <?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'pietergoosen' ) . '</span>', 'after' => '</div>' ) ); ?>
                </div><!-- .entry-content -->
                <footer class="entry-meta">

                </footer><!-- .entry-meta -->
            <?php endif; ?>
            </article><!-- #post-<?php the_ID(); ?> -->
        </div>  

            <?php 
            /* Do we have any Custom Post Type */
                global $post;

                // Save posts for later use
                $tmp_post = $post;

                $args = array( 
                    'post_type' => $posttype,
                    'posts_per_page' => $post_count,
                    'paged' => $paged,
                    'order' => $asc,
                    'ignore_sticky_posts' => 1,
                );

                    $wp_query= null;
                    $wp_query = new WP_Query();
                    $wp_query->query( $args );

                // Output
        if ( $wp_query->have_posts() ) :

                // Start the Loop.
                while ( have_posts() ) : the_post();


            get_template_part( 'content', get_post_format() );

            endwhile;

                pietergoosen_pagination();  

                else : 

                    get_template_part( 'content', 'none' );

                endif; ?>

                <?php 
                    // Reset the post to the page post
                    $post = $tmp_post; 
                ?>

            <?php if ( comments_open() || get_comments_number() ) {
                        comments_template();
                    } ?>

    </div><!-- #content -->
    </div><!-- #primary -->

    <?php get_sidebar( 'content' ); ?>

</div><!-- #main-content -->

<?php
get_footer();

The first piece of code is used to call the settings from the db. This will be set via a metabox in the back end when creating a new page in the page editor screen. The important code here is the arguments for WP_Query.

$args = array( 
 'post_type' => $posttype,
 'posts_per_page' => $post_count,
 'paged' => $paged,
 'order' => $asc,
 'ignore_sticky_posts' => 1,

This will decide which custom post types will be displayed, posts per page and the order of posts. All these settings are called from the db, and is set in the custom meta box in the back end

Secondly, create a custom meta box

This metabox will be diplayed in the "Page" screen when a new page is created and "Custom Post Type Page" is selected in the "Page Attributes" meta box.

Add the following in your functions.php or custom functions file

add_action('admin_init', 'pietergoosen_add_cpt_meta_box');

function pietergoosen_add_cpt_meta_box(){   
    $post_id = isset( $_GET['post'] ) ? $_GET['post'] : 0 ;
    if($post_id) { 
        $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
        if ($template_file == 'page-cpt.php') { 
            add_meta_box('cpt_meta_box', __( 'Page of Posts with the same name', 'pietergoosen' ), 'pietergoosen_cpt_meta_options', 'page', 'side', 'core');
        } else {
            $meta = get_post_meta($post_id, '_cpt_post_type', true);
            if( $meta ) {
                pietergoosen_cpt_update_post_meta($post_id, '_cpt_post_type', '');
                pietergoosen_cpt_update_post_meta($post_id, '_cpt_order_by', '');
                pietergoosen_cpt_update_post_meta($post_id, '_cpt_asc', '');
                pietergoosen_cpt_update_post_meta($post_id, '_cpt_post_count', '');
                remove_meta_box( 'cpt_meta_box', 'page', 'side');
            }
        }
    }
    add_action('save_post', 'pietergoosen_cpt_update_post_meta_box');
}

function pietergoosen_cpt_meta_options(){

    $post_id =  !empty($_GET['post']) ? $_GET['post'] : 0;
    if( !$post_id ) return;

    $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
    if ($template_file != 'page-cpt.php') return;

    global $order_list,$post_styles,$sort;
    $categories = get_categories();

    //Check if we have values
    $post_meta=array();
    $post_meta = get_post_meta( $post_id,false );

    $posttype = isset( $post_meta['_cpt_post_type'] ) ? $post_meta['_cpt_post_type'][0] : 1;
    $order_by = isset( $post_meta['_cpt_order_by'] ) ? $post_meta['_cpt_order_by'][0] : 'date';
    $asc = isset( $post_meta['_cpt_asc'] ) ? $post_meta['_cpt_asc'][0] : 'DESC';
    $post_count = isset( $post_meta['_cpt_post_count'] ) ? $post_meta['_cpt_post_count'][0] : get_option('posts_per_page');
    if(!$post_count || !is_numeric( $post_count )) $post_count = get_option('posts_per_page');
    ?>

    <!-- Start the meta boxes -->
    <div class="inside">
    <p><label><strong><?php _e( 'Custom Post Type', 'pietergoosen' ); ?></strong></label></p>
    <select id="_cpt_post_type" name="_cpt_post_type">
<?php 
    //Custom Post Type List
    $args = array(
    'public'   => true,
    '_builtin' => false
    );

    $output = 'names'; // names or objects, note names is the default
    $operator = 'and'; // 'and' or 'or'

    $post_types = get_post_types( $args, $output, $operator ); 

    foreach ( $post_types  as $post_type ) :
        $selected = ( $post_type == $posttype ) ? ' selected = "selected" ' : '';
        $option = '<option '.$selected .'value="'. $post_type;
        $option = $option .'">';
        $option = $option .$post_type;
        $option = $option .'</option>';
        echo $option;
    endforeach;

?>
    </select>

    <p><label><strong><?php _e( 'Order')?><strong></label></p>
    <select id="_cpt_asc" name="_cpt_asc">
<?php 

    $sort = array(
        'DESC' => array( 'value' => 'DESC','label' => 'Descending' ),
        'ASC' => array( 'value' => 'ASC','label' => 'Ascending' ),
    ); 

    foreach ($sort as $output) :
        $selected = ( $output['value'] == $asc ) ? ' selected = "selected" ' : '';
        $option = '<option '.$selected .'value="' . $output['value'];
        $option = $option .'">';
        $option = $option .$output['label'];
        $option = $option .'</option>';
        echo $option;
    endforeach;
?>
    </select>

    <p><strong><label><?php _e( 'Posts per Page', 'pageofposts' ); ?><strong></label></p>
    <input id="_cpt_post_count" name="_cpt_post_count" type="text" value="<?php echo $post_count; ?>" size="3" />

    </div>
    <!-- End page of posts meta box -->
    <?php
}
function pietergoosen_cpt_update_post_meta_box( $post_id ){

    if ( empty( $_POST ) ) {
        return;
    } else {
        $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
        if ($template_file != 'page-cpt.php') return;

        if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
            return $post_id;
        } else {
            if ( $_POST['post_type'] == 'page' ) {
                if ( !current_user_can( 'edit_page', $post_id ) )
                  return $post_id;
            } else {
                if ( !current_user_can( 'edit_post', $post_id ) )
                  return $post_id;
            }
            $meta = isset( $_POST['_cpt_post_type'] ) ? $_POST['_cpt_post_type'] : 1;           
            pietergoosen_cpt_update_post_meta($post_id, '_cpt_post_type', $meta);
            $meta = isset( $_POST['_cpt_order_by'] ) ? $_POST['_cpt_order_by'] : 'date';            
            pietergoosen_cpt_update_post_meta($post_id, '_cpt_order_by', $meta);
            $meta = isset( $_POST['_cpt_asc'] ) ? $_POST['_cpt_asc'] : 'DESC';
            pietergoosen_cpt_update_post_meta($post_id, '_cpt_asc', $meta);
            $meta = isset( $_POST['_cpt_post_count'] ) ? $_POST['_cpt_post_count'] : get_option('posts_per_page');
            pietergoosen_cpt_update_post_meta($post_id, '_cpt_post_count', $meta);
            return;
        }
    }
}

function pietergoosen_cpt_update_post_meta($post_id, $key, $data) {
    $post_meta = get_post_meta($post_id, $key, true);
    if( $data != '' && $post_meta != $data) {
        update_post_meta($post_id, $key, $data);
    } elseif ( $post_meta != '' && $data == '' ) {
        delete_post_meta($post_id, $key);
    }
}

Metabox

What this code do is to register and display the meta box, add the options to the metabox and storing the options to the db for use in the page-cpt.php template.

You can now go and create a new page, and call the page whatever you like. In the "Page Attributes", select "Custom Post Type Page" and "Publish" your page. The metabox for the custom post types options will now appear above the "Publish" metabox, and will display all the current available custom post types. Select and set the options you need to display and click "Update". Your page will now show posts from the custom post type you have selected, and your page will be visible in the nav bar.

You can add more functionality to this, or change the code to display categories or taxonomies in the same way. Hope this help

share|improve this answer
2  
Arguably it's easier and way more robust to make menus support archives than hack pages into pseudo-archives. –  Rarst Apr 3 at 20:25
    
I won't argue that. I think this will depend on person to person. I know this might be over inflated, but it works for me. It is always nice to hear what others think of a solution and how they will do things. Thanks for your comment. Really appreciated –  Pieter Goosen Apr 3 at 20:34
2  
@Rarst you should add that as an alternate answer. I'll upvote it, too, along with this one. –  Chip Bennett Apr 4 at 12:48
    
@ChipBennett there are already answers on that on site and since I'm lazy it would be "search around or use plugin" so I don't feel like it's good answer material :) –  Rarst Apr 4 at 12:50
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.