I am looking for a solution that allows me to print the following:
Cat 1 Cat 2 Cat 3
Post 1 Post 1 Post 1
Post 2 Post 2 Post 2
Post 3 Post 3
Post 4
EDIT
I am looking for something that will only require one database query! So if you have a foreach
in your code followed by a new WP_Query
then that is not what I am looking for (I am planning to put this onto my website's homepage).
I am looking for a solution that allows me to print the following:
Cat 1 Cat 2 Cat 3
Post 1 Post 1 Post 1
Post 2 Post 2 Post 2
Post 3 Post 3
Post 4
EDIT
I am looking for something that will only require one database query! So if you have a foreach
in your code followed by a new WP_Query
then that is not what I am looking for (I am planning to put this onto my website's homepage).
- downvoted as your requirement turns this from a WordPress question into a php sorting problem. – Michael Commented Feb 25, 2012 at 11:29
- 3 Please see my answer. Extremely quick and efficient. See the stats I've added in my answer – Pieter Goosen Commented Sep 7, 2014 at 13:51
- How about by this? <?php $erer = wp_list_categories('orderby=name&title_li=&show_count=1'); var_dump($erer); ?> You can get how to use HTML and css then. – Vishwa Commented Oct 16, 2018 at 4:14
8 Answers
Reset to default 20EDIT REVISIT NO 2
I have never touched the Transient API, until today when I saw @MikeSchinkel answer in this post. This inspired me to revisit this post once again. After some testing, I came up with the following:
Time to execute went down from ~0.07 seconds to ~0.002seconds
Database query time went down by about halve
With the transient, only 2 db queries are executed
How the code works (Just going to discuss changes from the original code from REVISIT):
STEP 1
We need to save the value of $q
to a transient, this is the value that holds the category list with post titles.
STEP 2
We first need to check if a transient exists, and if none exists, create the transient. If the transient exists, retrieve its info
STEP 3
This info is now be passed through a foreach
loop to print the list with category names and post titles.
STEP 4
As it stands, the transient will be updated every twelve hours. This can be set to suite your needs. However, the transient will need to be deleted and recreated every time a post's status changes. This might be from draft to publish, a new post being published or a post that's being trashed. To do this, you need to make use of delete_transient
which will be hooked to transition_post_status
which will be triggered each time a post's status changes
Here is the complete code:
In your functions.php
add_action( 'transition_post_status', 'publish_new_post', 10, 3 );
function publish_new_post() {
delete_transient( 'category_list' );
}
In your template where you need to display your list
<?php
if ( false === ( $q = get_transient( 'category_list' ) ) ) {
$args = array(
'posts_per_page' => -1
);
$query = new WP_Query($args);
$q = array();
while ( $query->have_posts() ) {
$query->the_post();
$a = '<a href="'. get_permalink() .'">' . get_the_title() .'</a>';
$categories = get_the_category();
foreach ( $categories as $key=>$category ) {
$b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';
}
$q[$b][] = $a; // Create an array with the category names and post titles
}
/* Restore original Post Data */
wp_reset_postdata();
set_transient( 'category_list', $q, 12 * HOUR_IN_SECONDS );
}
foreach ($q as $key=>$values) {
echo $key;
echo '<ul>';
foreach ($values as $value){
echo '<li>' . $value . '</li>';
}
echo '</ul>';
}
?>
REVISIT
I recently came up with a very lightweight solution that is way much faster than the other possible solutions given. On my test site I get a total generation time of only ~0.07 seconds and only 6 db queries according to Query Monitor while the other methods give me a generation time of ~0.35 seconds and 50 extra db queries.
Here is a breakdown of my method
STEP 1
You first need to create a custom query with WP_Query
to retrieve all published posts
$args = array(
'posts_per_page' => -1
);
$query = new WP_Query($args);
$q = array();
while ( $query->have_posts() ) {
}
/* Restore original Post Data */
wp_reset_postdata();
Step 2
Using get_the_category
, retrieve a list of all categories a post belongs to.
$categories = get_the_category();
foreach ( $categories as $key=>$category ) {
$b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';
}
STEP 3
Assign variables to the post title and the categories of the post
$a = '<a href="'. get_permalink() .'">' . get_the_title() .'</a>';
and
$b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';
STEP 4
Combine these two variables to form a multidimensional array
$q[$b][] = $a;
To see what is happening in the array, simply do a var_dump
?><pre><?php var_dump($q); ?></pre><?php
STEP 5
Using foreach
loops, create your post list sorted by category
foreach ($q as $key=>$values) {
echo $key;
echo '<ul>';
foreach ($values as $value){
echo '<li>' . $value . '</li>';
}
echo '</ul>';
}
ALL TOGETHER NOW!
Here is the complete code
<?php
$args = array(
'posts_per_page' => -1
);
$query = new WP_Query($args);
$q = array();
while ( $query->have_posts() ) {
$query->the_post();
$a = '<a href="'. get_permalink() .'">' . get_the_title() .'</a>';
$categories = get_the_category();
foreach ( $categories as $key=>$category ) {
$b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';
}
$q[$b][] = $a; // Create an array with the category names and post titles
}
/* Restore original Post Data */
wp_reset_postdata();
foreach ($q as $key=>$values) {
echo $key;
echo '<ul>';
foreach ($values as $value){
echo '<li>' . $value . '</li>';
}
echo '</ul>';
}
?>
I just wanted to add my solution that derived from this question I had. This caches the query for the categories and also caches the posts, including the post content, in each category. The first time the cache is filled there are normal database queries, however once filled the cached categories and posts are served, so no more database queries.
// Transients API all categories and all posts
$query_categories = get_transient('cached_categories');
if ( false === $query_categories){
$args_cat = array(
// order by category name ascending
'orderby' => 'name',
'order' => 'ASC',
// get only top level categories
'parent' => 0
);
// Instead of caching a WP Query I cache 'get_categories()'.
$query_categories = get_categories($args_cat);
// var_dump($query_categories);
set_transient('cached_categories', $query_categories, DAY_IN_SECONDS );
}
// Full posts query
// if there are categories filled with posts
if (!empty ($query_categories) && !is_wp_error( $query_categories )) {
foreach ($query_categories as $category) {
// var_dump($category);
$query_category_posts = get_transient('cached_posts_' . $category->slug );
if ( false === $query_category_posts ){
// Query all posts by slug inside each category
$args_category_posts = array(
'post_type' => 'post',
// The category slug and category name we get from the foreach over all categories
'category_name' => $category->slug
);
// Here I cache the WP_Query, though this runs for all categories.
// Because of that the '$category->slug' is used to serve a string and not an object.
$query_category_posts = new WP_Query($args_category_posts);
set_transient( 'cached_posts_' . $category->slug , $query_category_posts, DAY_IN_SECONDS );
}
if ($query_category_posts->have_posts()) {
while ($query_category_posts->have_posts()) {
$query_category_posts->the_post(); ?>
<article class="<?php echo $category->slug ?>-article">
<h2 class="<?php echo $category->slug ?>-article-title">
<a href="<?php echo get_permalink() ?>"><?php echo get_the_title() ?></a>
</h2>
<p class="<?php echo $category->slug ?>-post-info">
<?php the_time('d. m. Y') ?>
</p>
<div <?php post_class() ?> >
<?php the_content(); ?>
</div>
</article> <?php
}
} // end loop
} // end foreach
wp_reset_postdata() ;
} // end if there are categories filled with posts
Here is my solution for getting the categories and the posts within those categories as one result.
My example is based on custom post type taxonomy category document_category
and the custom post type documents
. But i'm sure you will get the idea.
$result = [];
$categories = get_terms( [
'taxonomy' => 'document_category'
] );
foreach ( $categories as $index => $category ) {
$args = [
'post_type' => 'documents',
'posts_per_page' => - 1,
'public' => true,
'tax_query' => [
[
'taxonomy' => 'document_category',
'field' => 'term_id',
'terms' => $category->term_id,
'include_children' => true
]
]
];
$documents = get_posts( $args );
$result[ $index ]['category'] = $category;
$result[ $index ]['documents'] = $documents;
}
return $result;
try now this code
$cat_ids=array();
foreach (get_categories() as $cat)
{
array_push($cat_ids, $cat->cat_ID);
}
$the_query = new WP_Query(array('post_type'=>'post', array('category__and' => $cat_ids) ) );
if( $the_query->have_posts() ):
while ( $the_query->have_posts() ) : $the_query->the_post();
echo the_title();
endwhile;
endif;
wp_reset_query();
I've build something for me that I use quite alot, here is the code, you can use slugs, ids or the term object in the $categories
array, if you want to get all the categories you can use get_terms()
, and feed with a bunch of term objects, but be careful, there is no treatment for hierarchy on this code.
$categories = array( 1, 'slug', 3 );
echo '<ul>';
foreach($categories as $category) {
$term = ( is_numeric($category) || is_object($category) ? get_term( $category, 'category' ) : get_term_by( 'slug', $category, 'category' ) );
$args = array(
'cat' => $term->term_id
// Add any other arguments to fit your needs
);
$q = new WP_Query( $args );
if( $q->have_posts() ) {
echo '<li><a href="' . get_term_link( $term->term_id, 'category' ) . '">' . $term->name . '</a><ul>';
while( $q->have_posts() ) {
$q->the_post();
echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
}
echo '</ul></li>';
} else {
}
}
echo '</ul>';
Untested, yet one of the simplest approaches I would try is the following:
<?php
$category_ids = get_all_category_ids();
foreach ($category_ids as $values) {
$args = array('category' => $value);
$posts_array = get_posts( $args );
foreach ($posts_array as $post) : setup_postdata($post);
?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php
endforeach;
wp_reset_query();
}
?>
You can use this... Set the amount of posts you need...
Also I have put it all inside a div, so you can do the structure and design your looking for.
<?php
$allcats = get_categories('child_of=0');
foreach ($allcats as $cat) :
$args = array(
'posts_per_page' => 3, // set number of post per category here
'category__in' => array($cat->term_id)
);
$customInCatQuery = new WP_Query($args);
if ($customInCatQuery->have_posts()) :
echo '<div>';
echo '<h3>'.$cat->name.'</h3>';
echo '<ul>';
while ($customInCatQuery->have_posts()) : $customInCatQuery->the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php
endwhile;
echo '</ul></div>';
?>
<?php
else :
echo 'No post published in:'.$cat->name;
endif;
wp_reset_query();
endforeach;
?>
Hope this helps.
if you are looking for a plugin, List Category Posts might work for you.
Fore a query, take a look at get_posts