Creating Page Based on Category

REVISIT: Feb. 2nd 2016

The original code had many issues

  • Data wasn’t sanitized and validated which can lead to serious security issues

  • Some parts were repetitive

  • Bit messy and sometimes hard to read

  • Some sections was only partly working

  • Used globals which is really evil

That is why I revisited this answer and updated the code in order to solve the issues above. The code is now cleaner, safer and easier to read and debug. Make sure to check it out in the ORIGINAL ANSWER section

Before I go to the original ORIGINAL ANSWER section, I want to add an alternative which I think is a bit better to use

ALTERNATIVE WAY

This is a straight forward alternative solution which does not involve custom templates (except maybe a content.php) or modifying any templates. All you need to do is

  • create a new page with any page template you wish

  • create a content.php template part of any such template part if your theme does not have these available by default

  • add the following code and your done

    $query = new PreGetPostsForPages(
        251,       // Page ID we will target
        'content', //Template part which will be used to display posts, name should be without .php extension 
        true,      // Should get_template_part support post formats
        false,     // Should the page object be excluded from the loop
        [          // Array of valid arguments that will be passed to WP_Query/pre_get_posts
            'post_type'      => 'post', 
            'posts_per_page' => 2
        ] 
    );
    $query->init(); 
    

The PreGetPostsForPages class can be found in my answer here and also a detailed explanation on how to use it

ORIGINAL ANSWER

From what I can understand is that you are specifically looking for a page template that you can set under page attributes in the page admin add/edit screen and auto assign a category to it.

Here is a page template that does exactly that. You can create a new page in the back end, assign this template to the page and then set a specific category to it, all from the back end.

Here is how it work

I have adapted this template from a tutorial by digitalraindrops that you can go and check out at the link provided. I have made a couple modifications to it to work on twentyfourteen. I have also removed the original WP_Query as it is used in a way that actually is the same as doing a normal query_posts query, which should never be used.

Note: This function isn’t meant to be used by plugins or themes. As explained later, there are better, more performant options to alter the main query. query_posts() is overly simplistic and problematic way to modify main query of a page by replacing it with new instance of the query. It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination).

It should be noted before I start, you can also just create a proper category.php template, create a custom menu and add the required category to the menu. You should also note that you can use the category name as page slug, but you cannot have any child pages for these pages, this will interfere and break the Template Hierarchy

To accomplish all of what we need, you need to create a custom meta box which will be added to the page add/edit screen when the specific template is selected. This meta box will be used to select which category will be used for the page selected. You will also be able to set custom posts_per_page and sort posts. Here is the code to add the custom meta box. Add these code to your functions.php or any functions related file

<?php
add_action( 'admin_init', function ()
{   
    $post_id = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT );
    if ( $post_id ) {
        // Get the current page template
        $post_meta = get_post_meta( $post_id );

        // Make sure that we only target our desired template
        if (    isset ( $post_meta['_wp_page_template'][0] )
             && 'page-pop.php' === $post_meta['_wp_page_template'][0] 
        ) {
            add_meta_box(
                'pop_meta_box', 
                __( 'Page of Posts with the same name' ), 
                'pop_metabox_options', 
                'page', 
                'side', 
                'core'
            );
        } else {
            if( isset( $meta['_cat_id'][0] ) ) {
                $meta_value_array = [
                    '_cat_id',
                    '_page_title',
                    '_posts_title',
                    '_order_by',
                    '_asc',
                    '_post_count'
                ];
                foreach ( $meta_value_array as $value ) 
                    pop_helper_update_post_meta( $post_id, $value, '' );

                remove_meta_box( 'pop_meta_box', 'page', 'side' );
            }
        }
    }
    add_action( 'save_post',  'pop_update_post_meta_box' );
});

function get_pop_order_by_list()
{   
    // Set the sort order
    $sort = [
        [
            'DESC' => [
                    'value' => 'DESC',
                    'label' => 'Descending'
                ],
            'ASC'  => [
                    'value' => 'ASC',
                    'label' => 'Ascending'
                ],
        ]
    ];      

    // Create an array of values to order the posts by
    $order_list = [
        [
            'none'          => [
                    'value' => 'none',
                    'label' => 'None'
                ],
            'id'            => [
                    'value' => 'ID',
                    'label' => 'Post ID'
                ],
            'author'        => [
                    'value' => 'author',
                    'label' => 'Author'
                ],
            'title'         => [
                    'value' => 'title',
                    'label' => 'Post Title'
                ],
            'date'          => [
                    'value' => 'date', 
                    'label' => 'Post Date'
                ],
            'modified'      => [
                    'value' => 'modified',
                    'label' => 'Modified Date'
                ],
            'parent'        => [
                    'value' => 'parent',
                    'label' => 'Parent Post'
                ],
            'rand'          => [
                    'value' => 'rand',
                    'label' => 'Random'
                ],
            'comment_count' => [
                    'value' => 'comment_count',
                    'label' => 'Comment Count'
                ],
            'menu_order'    => [
                    'value' => 'menu_order',
                    'label' => 'Menu Order'
                ],
        ]
    ];

    return $list = array_merge( $sort, $order_list );
}

function pop_metabox_options()
{
    $post_id = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT );
    if ( !$post_id )
        return;

    // Make sure the current user have the edit_page ability
    if ( !current_user_can( 'edit_post', $post_id ) )
        return;

    // Get the current page template
    $template_file = get_post_meta( $post_id, '_wp_page_template', true );

    // Make sure that we only target our desired template
    if ( 'page-pop.php' !== $template_file ) 
        return;

    // Get all the post meta values and sanitize/validate them
    $post_meta = get_post_meta( $post_id );

    $filters = [
        '_cat_id'      => [
            'filter'   => FILTER_VALIDATE_INT,
            'default'  => 1
        ],
        '_page_title'  => [
            'filter'   => FILTER_SANITIZE_STRING,
            'default'  => ''
        ],
        '_posts_title' => [
            'filter'   => FILTER_SANITIZE_STRING,
            'default'  => ''
        ],
        '_order_by'    => [
            'filter'   => FILTER_SANITIZE_STRING,
            'default'  => 'ID'
        ],
        '_asc'       => [
            'filter'   => FILTER_SANITIZE_STRING,
            'default'  => 'DESC'
        ],
        '_post_count'  =>  [
            'filter'   => FILTER_VALIDATE_INT,
            'default'  => get_option( 'posts_per_page' )
        ],
    ];  

    foreach ( $filters as $key=>$value ) {
        if ( !array_key_exists( $key, $post_meta  ) ) {
            $post_meta[$key][0] = $value['default'];
        } else {
            $post_meta[$key][0] = filter_var( $post_meta[$key][0], $value['filter'], $value['default'] );
        }
    }

    ?>
        <!-- Sart the meta boxes -->
    <div class="inside">
        <p>
            <label>
                <strong><?php _e( 'Page Title' ); ?></strong>
            </label>
        </p>    
        <input id="_posts_title" name="_posts_title" type="text" style="width: 98%;" value="<?php echo $post_meta['_page_title'][0]; ?>"/>  

        <p>
            <label>
                <strong><?php _e( 'Post Title' ); ?></strong>
            </label>
        </p>    
        <input id="_page_title" name="_page_title" type="text" style="width: 98%;" value="<?php echo $post_meta['_posts_title'][0]; ?>"/>

        <p>
            <label>
                <strong><?php _e( 'Category', 'pietergoosen' ); ?></strong>
            </label>
        </p>
        <select id="_cat_id" name="_cat_id">
            <?php 
            // Get all the categories
            $categories = get_categories();
            foreach ( $categories as $cat ) {
                $selected = ( $cat->cat_ID == $post_meta['_cat_id'][0] ) ? ' selected = "selected" ' : '';

                $option = '<option '.$selected .'value="' . $cat->cat_ID;
                $option = $option .'">';
                $option = $option .$cat->cat_name;
                $option = $option .'</option>';
                echo $option;
            } //endforeach
            ?>
        </select>

        <?php 
        if ( function_exists( 'get_pop_order_by_list' ) ) {
            $list = get_pop_order_by_list();
            ?>

            <p>
                <label>
                    <strong><?php _e( 'Order' )?><strong>
                </label>
            </p>
            <select id="_asc" name="_asc">
                <?php 
                foreach ( $list[0] as $output ) {
                    $selected = ( $output['value'] == $post_meta['_asc'][0] ) ? ' selected = "selected" ' : '';

                    $option = '<option '.$selected .'value="' . $output['value'];
                    $option = $option .'">';
                    $option = $option .$output['label'];
                    $option = $option .'</option>';
                    echo $option;
                } //endforeach
                unset ( $output );
                ?>
            </select>

            <p>
                <label>
                    <strong><?php _e( 'Sort by' )?></strong>
                </label>
            </p>
            <select id="_order_by" name="_order_by">
                <?php 
                foreach ( $list[1] as $output ) {
                    $selected = ( $output['value'] == $post_meta['_order_by'][0] ) ? ' selected = "selected" ' : '';

                    $option = '<option '.$selected .'value="' . $output['value'];
                    $option = $option .'">';
                    $option = $option .$output['label'];
                    $option = $option .'</option>';
                    echo $option;
                } //endforeach
                unset ( $output );
                ?>
            </select>       

            <?php
        }
        ?>

        <p>
            <label>
                <strong><?php _e( 'Posts per Page' ); ?><strong>
            </label>
        </p>
        <input id="_post_count" name="_post_count" type="text" value="<?php echo $post_meta['_post_count'][0]; ?>" size="3" />

    </div>
    <!-- End page of posts meta box -->
    <?php
}

function pop_update_post_meta_box( $post_id )
{
    // Make sure we have a valid $_POST method
    if ( !$_POST )
        return;

    // Make sure the current user have the edit_page ability
    if ( !current_user_can( 'edit_page', $post_id ) )
        return;

    // Get the current page template
    $template_file = get_post_meta( $post_id, '_wp_page_template', true );

    // Make sure that we only target our desired template
    if ( 'page-pop.php' !== $template_file ) 
        return;

    // Do nothing on auto save, just bail
    if (    defined( 'DOING_AUTOSAVE' ) 
         && DOING_AUTOSAVE 
    )
        return;

    $args = [
        '_cat_id'       => [
                               'filter' => FILTER_VALIDATE_INT,
                               'default' => 1
                           ],   
        '_page_title'   => [
                               'filter' => FILTER_SANITIZE_STRING,
                               'default' => ''
                           ],
        '_posts_title'  => [
                               'filter' => FILTER_SANITIZE_STRING,
                               'default' => ''
                           ],
        '_order_by'     => [
                               'filter'  => FILTER_SANITIZE_STRING,
                               'default' => 'date'
                           ],
        '_asc'        => [
                               'filter'  => FILTER_SANITIZE_STRING,
                               'default' => 'DESC'
                           ],
        '_post_count'   => [
                               'filter'  => FILTER_VALIDATE_INT,
                               'default' => get_option( 'posts_per_page' )
                           ],  
    ];  

    $meta = filter_input_array( INPUT_POST, $args );

    if ( !$meta )
        return;

    // Loop throught the array and update meta values
    foreach ( $meta as $k=>$v ) 
        pop_helper_update_post_meta( $post_id, $k, $v );
}   

function pop_helper_update_post_meta( $post_id = '', $key = '', $data="" ) 
{
    // Make sure we have valid values, if not, return false
    if ( !$post_id
         || !$key
    )
        return false;

    // Sanitize and validate values
    $post_id = filter_var( $post_id, FILTER_VALIDATE_INT    );
    $key     = filter_var( $key,     FILTER_SANITIZE_STRING );
    $data    = filter_var( $data,    FILTER_SANITIZE_STRING );

    // Get the  post meta values
    $post_meta = get_post_meta( $post_id, $key, true );

    if(    $data
        && $post_meta != $data
    ) {
        update_post_meta( $post_id, $key, $data );
    } 

    if (    $post_meta 
         && !$data
    ) {
        delete_post_meta( $post_id, $key );
    }
}

Now for the page template. Create a file in your root directory and call it page-pop.php. You have to call your template this. If you need to rename it, check in the code above and change all instances of page-pop.php to the template name of your choice

The first section will call back all the saved values from the meta box. These values will be fed back into your custom WP_Query to call back all the posts according to category.

The first loop is your normal page template loop which you can use to display content added to the WYSIWYG editor in the page add/edit screen. The second loop/query is the query that will return your posts. You should note that all markup here is for the default twenty fourteen theme, you will need to modify it to suite your theme.

Here is the code that should go into your page-pop.php template

<?php
/**
 * Template Name: Page of Posts
 */
get_header(); ?>

<?php
    //See if we have any values and set defaults in case
    $post_meta   = get_post_meta( get_queried_object_id() );

    $catid       = isset( $post_meta['_cat_id'] )      ? $post_meta['_cat_id'][0]      : 1;
    $page_title  = isset( $post_meta['_page_title'] )  ? $post_meta['_page_title'][0]  : '';
    $posts_title = isset( $post_meta['_posts_title'] ) ? $post_meta['_posts_title'][0] : '';
    $orderby     = isset( $post_meta['_order_by'] )    ? $post_meta['_order_by'][0]    : 'date';
    $asc         = isset( $post_meta['_asc'] )         ? $post_meta['_asc'][0]         : 'DESC';
    $post_count  = isset( $post_meta['_post_count'] )  ? $post_meta['_post_count'][0]  : 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">

    <!-- Page Title -->
    <?php if( $page_title ) { ?>
        <article id="posts-title">
            <header class="entry-header">
                <h2 class="entry-title"><?php echo $page_title; ?></h2>
            </header><!-- .entry-header -->
        </article><!-- #posts-title -->
    <?php } ?>

        <?php the_post(); ?>
        <?php global $post;
        if( $post->post_content || $page_title ) : ?>
        <div class="entry-content">
            <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
                <?php if( $posts_title ) : ?>
                    <header class="entry-header">
                        <h1 class="entry-title"><?php echo $posts_title; ?></h1>
                    </header><!-- .entry-header -->

                <?php endif; ?>
            <?php if( $post->post_content ) : ?>    
                <div class="entry-content">
                    <?php the_content(); ?>
                    <?php wp_link_pages( ['before' => '<div class="page-link"><span>' . __( 'Pages:' ) . '</span>', 'after' => '</div>'] ); ?>
                </div><!-- .entry-content -->
                <footer class="entry-meta">

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

<?php 

/**-----------------------------------------------------------------------------
 *
 *  Start our custom query to display category posts
 *
*------------------------------------------------------------------------------*/   

        $args = [
            'cat'                   => $catid,
            'posts_per_page'        => $post_count,
            'paged'                 => $paged,
            'orderby'               => $orderby,
            'order'                 => $asc,
            'ignore_sticky_posts'   => 1,
        ];

        $cat_query = new WP_Query($args);

        // Output
        if ( $cat_query->have_posts() ) {
            // Start the Loop.
            while ( $cat_query->have_posts() ) { 
                $cat_query->the_post(); 

                    get_template_part( 'content', get_post_format() );

            }

            if ( function_exists( 'pietergoosen_pagination' ) )
                pietergoosen_pagination();  

            wp_reset_postdata();

        } else { 

                get_template_part( 'content', 'none' );

        } 
        ?>

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

    <?php get_sidebar(); ?>

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

<?php
get_footer();

I hope I understood you correctly and hope this is what you need

Leave a Comment