Ask your WordPress questions! Pay money and get answers fast! (more info)

Display Posts sorted by child category grouped by parent category WordPress

  • SOLVED

Trying to achieve this output for my page

- Parent Category 1

-- Child Category
--- Post
--- Post

-- Child Category
--- Post
--- Post

- Parent Category 2

-- Child Category
--- Post
… etc

What I have so far:

<?php
$categories = get_categories('include=15,16,17&orderby=ID');

foreach($categories as $category) :
?>

<div class="categories columns">
<div class="col">
<h3><?php echo $category->name ?></h3>
<p><?php echo $category->description ?></p>
</div>

<div class="col">

<?php
$catid = $category->cat_ID; //Store the category ID as a variable to be used in WP_Query

$args = array(
'cat' => $catid,
'post_type' => 'fabric'
);

$query = new WP_Query($args);

// Start the Loop. You can use your own loop here
while ( $query->have_posts() ) : $query->the_post();
?>
<p>
<?php the_title(); //Display only the title of the posts in the category ?>
</p>
<?php
endwhile;
echo "</div>";
echo "</div>";

endforeach;
?>

Answers (4)

2017-10-26

timDesain Nanang answers:

try the following code, and change the taxonomy first:

<?php
$post_type = 'fabric';
$taxonomy = 'your_taxonomy'; //change this

$term_args = array('parent' => 0, 'orderby'=>'id', 'order'=>'ASC', 'hide_empty'=>false, );
$get_terms = get_terms($taxonomy, $term_args);

foreach($get_terms as $parent) :

?>
<div class="categories columns">

<div class="col">
<h3><?php echo $parent->name ?></h3>
<p><?php echo $parent->description ?></p>
</div>

<div class="col">
<?php
$parent_id = $parent->term_id; //Store the category ID as a variable to be used in WP_Query

$term_children = get_term_children( $parent_id, $taxonomy );

foreach ( $term_children as $child ) {
$term = get_term_by( 'id', $child, $taxonomy );
$child_id = $term->term_id;
echo '<p><a href="' . get_term_link( $child, $taxonomy ) . '">' . $term->name . '</a></p>';

$args = array(
'posts_per_page' => 3,
'post_type' => $post_type,
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'id',
'terms' => $child_id,
),
),
);

$wpq = new WP_Query($args);
if($wpq->have_posts()) {
while ( $wpq->have_posts() ) : $wpq->the_post();
?>
<li>
<?php the_title(); //Display only the title of the posts in the category ?>
</li>
<?php
endwhile;
}//have_posts

}//term_children

?>
</div>
</div>

<?php
endforeach;
?>


Nick comments:

Thank you, that is almost there!

However your code doesn't seem to be able to handle this scenario (when a parent has no child category it should still display posts) eg

- Parent Category 1

-- Child Category
--- Post
--- Post

-- Child Category
--- Post
--- Post

- Parent Category 2 (no child category)

--- Post
--- Post
--- Post
--- …etc


timDesain Nanang comments:

yep. wait a minute, please.


timDesain Nanang comments:

here we go:

<?php
$post_type = 'fabric';
$taxonomy = 'your_taxonomy'; //change this

$term_args = array('parent' => 0, 'orderby'=>'id', 'order'=>'ASC', 'hide_empty'=>false, );
$get_terms = get_terms($taxonomy, $term_args);

foreach($get_terms as $parent) :

?>
<div class="categories columns">

<div class="col">
<h3><?php echo $parent->name ?></h3>
<p><?php echo $parent->description ?></p>
</div>

<div class="col">
<?php
$parent_id = $parent->term_id; //Store the category ID as a variable to be used in WP_Query

$term_children = get_term_children( $parent_id, $taxonomy );

if(count($term_children)>0){

foreach ( $term_children as $child ) {
$term = get_term_by( 'id', $child, $taxonomy );
$child_id = $term->term_id;
echo '<p><a href="' . get_term_link( $child, $taxonomy ) . '">' . $term->name . '</a></p>';

wpq_posts_list($post_type, $taxonomy, $child_id);

}//term_children

}
else{
wpq_posts_list($post_type, $taxonomy, $parent_id);
}

?>
</div>
</div>

<?php
endforeach;

function wpq_posts_list($post_type, $taxonomy, $term_id){
$args = array(
'posts_per_page' => 3,
'post_type' => $post_type,
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'id',
'terms' => $term_id,
),
),
);

$wpq = new WP_Query($args);
if($wpq->have_posts()) {
while ( $wpq->have_posts() ) : $wpq->the_post();
?>
<li>
<?php the_title(); //Display only the title of the posts in the category ?>
</li>
<?php
endwhile;
}//have_posts
}
?>


Nick comments:

Great thank you that looks like it is working as expected

2017-10-27

Pearl Themes answers:

Hi,

I have spent a good amount of time to prepare a proper solution for your problem:

Here you go:


<?php

$taxonomy_name = 'Taxonomy_Name';
$post_type = 'Post_Type_Name';

$parent_terms = get_terms(
array(
'taxonomy' => $taxonomy_name,
'hide_empty' => false,
'parent' => 0
)
);


foreach ( $parent_terms as $parent_term ) {
echo 'Parent Category: ' . $parent_term->name . '<br><br>';

$child_terms = get_terms( $taxonomy_name, array( 'parent' => $parent_term->term_id,
'orderby' => 'slug',
'hide_empty' => false
) );
if ( ! empty( $child_terms ) ) {

foreach ( $child_terms as $child_term ) {

echo 'Child Category: ' . $child_term->name . '<br>';

$args = array(
'post_type' => $post_type,
'tax_query' => array(
array(
'taxonomy' => $taxonomy_name,
'field' => 'slug',
'terms' => $child_term->slug
)
)

);

$_posts = get_posts( $args );

foreach ( $_posts as $_post ) {
$postID = $_post->ID;
echo '<li>';
echo '<a href="' . get_permalink( $postID ) . '">';
echo get_the_title( $postID );
echo '</a></li>';
}

}
echo '<br><br>';
}
}


What you need to do is only set the "$taxonomy_name" and "$post_type" in very top variable and that's it!

Just let me know if you face any issue in the given solution. I am waiting for your response.

Thanks,

Farid


Nick comments:

Hi this code fails as it does not show posts under a parent category that do not have a child category.


Pearl Themes comments:

Hi there,

I have updated the code as per your need. Now it will display the parent category posts also.

<?php

$taxonomy_name = 'Taxonomy_Name';
$post_type = 'Post_Type_Name';

$parent_terms = get_terms(
array(
'taxonomy' => $taxonomy_name,
'hide_empty' => false,
'parent' => 0
)
);


foreach ( $parent_terms as $parent_term ) {
echo 'Parent Category: ' . $parent_term->name . '<br><br>';

$args = array(
'post_type' => $post_type,
'tax_query' => array(
array(
'taxonomy' => $taxonomy_name,
'field' => 'slug',
'terms' => $parent_term->slug
)
)

);

$_posts = get_posts( $args );

foreach ( $_posts as $_post ) {
$postID = $_post->ID;
echo '<li>';
echo '<a href="' . get_permalink( $postID ) . '">';
echo get_the_title( $postID );
echo '</a></li>';
}

$child_terms = get_terms( $taxonomy_name, array( 'parent' => $parent_term->term_id,
'orderby' => 'slug',
'hide_empty' => false
) );
if ( ! empty( $child_terms ) ) {

foreach ( $child_terms as $child_term ) {

echo 'Child Category: ' . $child_term->name . '<br>';

$args = array(
'post_type' => $post_type,
'tax_query' => array(
array(
'taxonomy' => $taxonomy_name,
'field' => 'slug',
'terms' => $child_term->slug
)
)

);

$_posts = get_posts( $args );

foreach ( $_posts as $_post ) {
$postID = $_post->ID;
echo '<li>';
echo '<a href="' . get_permalink( $postID ) . '">';
echo get_the_title( $postID );
echo '</a></li>';
}

}
echo '<br><br>';
}
}
?>


Just let me know if there is still something you think is not correct.

Thanks

2017-10-27

Mohamed Ahmed answers:

It's very easy task, Here you are the code
and we can do any preview to posts or categories as you want.


<?php
//Categories arguments;
$cats = get_categories( array(
'orderby' => 'name',
'order' => 'ASC'
));

foreach ($cats as $cat){
$args = array(
'cat' => $cat->term_id,
'post_type' => 'post',
'posts_per_page' => '1',
);
$query = new WP_Query( $args );
// Check if category has children categories.
$children = get_term_children($cat->term_id, 'category');
// If you need to show categories that only has a sub-categories.
// If you need all categories you can remove next line.
if($children):
echo "-- $cat->name <p>";
$categories = get_categories(['parent' => $cat->term_id]);
else:
echo "- $cat->name <p>";
$categories = get_categories();
endif;

?>
<?php if(!$children) if ($query->have_posts()) : ?>
<ol>
<?php while ($query->have_posts()) : $query->the_post(); ?>
<li><?php the_title(); ?></li>
<?php endwhile;?>
<p></ol>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
<?php } ?>


Mohamed Ahmed comments:

Is this do what you want or you have more requests?

Regards

2017-10-29

Dario Ferrer answers:

Hello Nick, I wrote this code and hope you find it useful for you.

Features

• Unlimited levels to show, all of them correctly ordered and parsed. This was possible thanks to recursive function.
• Displays both term and posts in the same level if they are children of their parent term.
• Displays posts even when a parent has no child category.
• Works with any post/taxonomy/term types.
• You can publish any list you want into any part of your site without have conflicts.
• You can add your own functions/html through some actions/filter availables, in order to modify de behavior/structure of your list.
• You can build/configure you lists easily and fast using a simple tag: nick_list('parameters').

Available Parameters:

'posts_mode' (str):
Controls the posts/categories position when they are togheter into the same parent category.
• If set to "first" displays the posts before categories on list.
• If set to "last" displays the posts after categories on list.
Default: last,

'output' (str):
Controls the results output. There are 3 ways:
• If set to "html" parses the html.
• If set to "array" returns the structured array (tree mode, in the same logic order than the normal list).
• If set to "flat" returns a raw, monodimensional and plain array.
Default: html.

'terms' (int):
Defines the term to point.
Default: (empty).

'taxonomy' (int):
Defines the taxonomy.
Default: category.

'post_type' (str):
Defines the post_type.
Default: post.

'posts_per_page' (int):
Defines how many posts you wan to show.
Default: -1 (all posts in the category).

Available Actions and filters:

Actions:
'nick_before_html': triggers before first 'ul' element.
'nick_after_html': triggers after last 'ul' element.
'nick_start_item': triggers right next to '<li>' opening tag.
'nick_end_item': triggers right before to '</li>' closing tag.

Filters:
'nick_query_posts': This filter is made to replace the default post query. You can perform a complex query with this.
'nick_query_terms': Same as above, but with terms.
'nick_array_articles': modifies the posts array.
'nick_array_categories': modifies the categories array.

Additional:
• If you want some improvement out of you did asked, tell me and I do it for you without extra cost.
• Also tell me if you need a hand with the css part, no extra cost.

Usage example:


nick_list( 'posts_per_page=3&post_mode=first' );

nick_list(
'posts_per_page' => 3,
'post_mode' => 'first'
);


The code:


function nick_constructor( $array ) {

$html = do_action( 'nick_before_html' );
$children = '';

foreach( $array as $node ) {
if ( isset( $node['children'] ) ) {
$html .= '<ul class="has-children">';
} else {
$html .= '<ul>';
}

$html .= "\n" . '<li>';
$html .= do_action( 'nick_before_item' );

$link = array_key_exists( 'ID' , $node ) ? get_permalink( $node['ID'] ) : get_term_link( $node['term_id'] );
$html .= "\n" . '<a href="'. esc_url( $link ) .'" title="Link to '. $node['name'] .'">'. $node['name'] .'</a>';

if( isset( $node['children'] ) ) {
$html .= "\n" . nick_constructor( $node['children'] );
}
$html .= do_action( 'nick_after_item' );

$html .= "\n" . '</li>' . "\n" . '</ul>' . "\n";
}

$html .= do_action( 'nick_after_html' );

return $html;
}

class NickTreeBuilder {

private $data = array();
public $rendered;

public function __construct(&$Input)
{
foreach($Input as $Item)
{
$Item= (array) $Item;
$this->data['items'][$Item['term_id']] = $Item;
$this->data['parents'][$Item['parent']][] = $Item['term_id'];
if(!isset($this->top_level) || $this->top_level > $Item['parent'])
{
$this->top_level = $Item['parent'];
}
}
return $this;
}

public function build($id)
{
$return{$id} = array();
foreach($this->data['parents'][$id] as $child)
{
$build = $this->data['items'][$child];
if(isset($this->data['parents'][$child]))
{
$build['has_children'] = true;
$build['children'] = $this->build($child);
}
else
{
$build['has_children'] = false;
}
$return{$id}[] = $build;
}
return (array) $return{$id};
}

public function render()
{
if(!isset($this->rendered) || !is_array($this->rendered))
{
$this->rendered = $this->build($this->top_level);
}
return $this->rendered;
}
}

function nick_array_depth(array $array) {
$max_depth = 1;

foreach ($array as $value) {
if (is_array($value)) {
$depth = nick_array_depth($value) + 1;

if ($depth > $max_depth) {
$max_depth = $depth;
}
}
}

return $max_depth;
}

function nick_list( $args = '' ) {

$params = array(
'post_type' => 'post',
'posts_per_page' => -1,
'terms' => '',
'taxonomy' => 'category',
'parent' => '',
'post_mode' => 'last',
'output' => 'html',
);

$r = wp_parse_args($args , $params);
extract( $r , EXTR_SKIP );

$a = $c = $branch = array();
$cat = $result = null;

$terms = $terms ? explode( ',' , $terms ) : '';

$args_articles = array(
'post_type' => $post_type,
'posts_per_page' => $posts_per_page,
);

if( $parent ) {
if( $post_type == 'post' ) {
$args_articles['category__in'] = $parent;
} else {
$args_articles['parent'] = $parent;
}
}

$args_articles = apply_filters( 'nick_query_posts' , $args_articles );

$articles = get_posts( $args_articles );

foreach( $articles as $key => $article ) {

$cat[] = get_the_terms( $article->ID , $taxonomy );
$id = $cat[$key][0]->term_id;

$a[$key] = array(
'ID' => $article->ID,
'parent' => $id,
'taxonomy' => $taxonomy,
'term_id' => '9999999' . $article->ID,
'name' => $article->post_title,
'slug' => $article->post_name,
'nick_type' => 'article',
);

}

$args_terms = array(
'hide_empty' => false,
'term_taxonomy_id' => $terms,
'parent' => $parent,
);

if( $post_type == 'post' ) {
$categories = get_categories( $args_terms );
} else {
$categories = get_terms( $args_terms );
}



$categories = apply_filters( 'nick_query_terms' , $categories );

foreach( $categories as $key => $cats ) {

$c_children = array();

$cat_children = get_posts( 'category__in=' . $cats->term_id );

foreach( $cat_children as $cat_c ) {
$c_children[] = $cat_c->ID;
}

$c[$key] = array(
'parent' => $cats->parent,
'taxonomy' => $cats->taxonomy,
'term_id' => $cats->term_id,
'name' => $cats->name,
'slug' => $cats->slug,
'nick_type' => 'cat',
'children' => $c_children,
);

}

$a = apply_filters( 'nick_array_articles' , $a );
$c = apply_filters( 'nick_array_categories' , $c );

$elements = array_merge( $c , $a );

switch( $post_mode ) {

case 'last':
$elements = array_merge( $c , $a );
break;

case 'first':
$elements = array_merge( $a , $c );
break;

default:
$elements = array_merge( $c , $a );
break;

}

$builder = new NickTreeBuilder( $elements );

switch( $output ) {

case 'html':
$result = nick_constructor( $builder->render() );
break;

case 'array':
$result = $builder->render();
break;

case 'flat':
$result = $elements;
break;

default:
$result = nick_constructor( $builder->render() );
break;

}

return $result;
}