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