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;
?>
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
Farid 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.
Farid 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
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
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;
}