WordPress.org

Ready to get started?Download WordPress

Forums

Custom Taxonomy - Related Posts Query (28 posts)

  1. jloft
    Member
    Posted 4 years ago #

    I just started experimenting with Custom Taxonomies in WordPress 3.0.1.

    I understand how to query posts associated with my custom taxonomy when explicitly stating a term, but I can't figure out how to create a dynamic query for something like related posts.

    I expected that if my custom taxonomy functioned as a replacement for the default categories for a custom post type that I could simply use the typical category related tags like get_the_category(); to interact with the custom taxonomy, but that does not seem to be the case.

    For example, in a single.php template, I would typically add the following code to the sidebar to display the titles of other posts in the same category as the main post.

    <?php
    $categories = get_the_category($post->ID);
    if ($categories) {
    	$category_ids = array();
    	foreach($categories as $individual_category) $category_ids[] = $individual_category->term_id;
    
    	$args=array(
    		'category__in' => $category_ids,
    		'post__not_in' => array($post->ID),
    		'showposts'=>5, // Number of related posts that will be shown.
    		'caller_get_posts'=>1
    	);
    	$my_query = new wp_query($args);
    	if( $my_query->have_posts() ) {
    		echo '<h3>Related Posts</h3><ul>';
    		while ($my_query->have_posts()) {
    			$my_query->the_post();
    		?>
    			<li><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></li>
    		<?php
    		}
    		echo '</ul>';
    	}
    }
    ?>

    Does anyone know how the same result is achieved when using a custom taxonomy? In essence, I'm looking for the the alternative to the_get_category(); function for custom taxonomies.

    http://codex.wordpress.org/Function_Reference/get_the_category

    I haven't tried anything similar when the custom taxonomy functions like tags, but I suppose I'd run into similar problems. So down the road, I'll likely be looking for an alternative to the get_the_tags(); function as well. Thus, even though I have a specific question, I also have a broader question about the functions for custom taxonomies that replace their category and tag counterparts. I may be missing it in the codex, but it doesn't seem like this is clearly addressed anywhere.

  2. jloft
    Member
    Posted 4 years ago #

    I'd like to note that I originally got the code for displaying related posts by category from Binny VA's blog. I've implemented it on other WordPress sites, so I know it works.

    http://www.bin-co.com/blog/2009/04/show-related-post-in-wordpress-without-a-plugin/

  3. sevenspark
    Member
    Posted 3 years ago #

    I ended up rolling my own function for this task based on the get_the_category() function. Basically, it uses the wp_get_object_terms() function and adds some caching. I haven't tested it extensively, but it's been working for me so far.

    function get_the_taxonomy( $taxonomy, $id = false ) {
    	global $post;
    
    	$id = (int) $id;
    	if ( !$id )
    		$id = (int) $post->ID;
    
    	$categories = get_object_term_cache( $id, $taxonomy );
    	if ( false === $categories ) {
    		$categories = wp_get_object_terms( $id, $taxonomy );
    		wp_cache_add($id, $categories, $taxonomy.'_relationships');
    	}
    
    	if ( !empty( $categories ) )
    		usort( $categories, '_usort_terms_by_name' );
    	else
    		$categories = array();
    
    	foreach ( (array) array_keys( $categories ) as $key ) {
    		_make_cat_compat( $categories[$key] );
    	}
    
    	return $categories;
    }

    Hope that helps you out!

    Chris

  4. JoeyBottle
    Member
    Posted 3 years ago #

    The code from chris seems to belong in the function.php, right ? So it's just there to make the "get_the_taxonomy" working, right?

    If so, how is the code which I supposed to put in the taxonomy.php or single.php or whatever to get the posts of a category of a custom-taxonomy???

  5. Walton
    Member
    Posted 3 years ago #

    When I tried that out in twenty ten, I got this error message:
    Warning: Missing argument 1 for get_the_taxonomy(), called in /home1/englisi9/public_html/testblog/wp-content/themes/twentyten/getthetax.php on line 8 and defined in /home1/englisi9/public_html/testblog/wp-content/themes/twentyten/functions.php on line 485

    Warning: array_keys() [function.array-keys]: The first argument should be an array in /home1/englisi9/public_html/testblog/wp-content/themes/twentyten/functions.php on line 503

    Would love to see it work.

  6. Ameisenman
    Member
    Posted 3 years ago #

    I've tried it, it produces a nice array object of the given taxonomy but not hierarchical...

  7. fcarthy
    Member
    Posted 3 years ago #

    Hi,

    I'm using this code to retrieve related posts based on my custom taxonomy. It works great, but the only problem is that I'm trying to retrieve a total of 4 related posts. This retrieves up to 4 posts per term. What am I doing wrong?

    <?php
    global $post;
    $terms = get_the_terms( $post->ID , 'badge', 'string');
    $do_not_duplicate[] = $post->ID;
    $postnum = 0;
    
    if(!empty($terms)){
        foreach ($terms as $term) {
            query_posts( array(
            'badge' => $term->slug,
            'showposts' => 3,
            'caller_get_posts' => 1,
            'post__not_in' => $do_not_duplicate ) );
            if(have_posts()){ ?>
    
                <?php while ( have_posts() ) : the_post(); $do_not_duplicate[] = $post->ID; ?>
    		<?php $postnum++; ?>
    	<div class="single-small-posts-<?php echo $postnum; ?>">
          			<?php
    			$clipping_image = get_post_meta( $post->ID, 'clipping_image', true );
    			$terms_of_post = get_the_term_list( $post->ID, 'type', '','', '', '' );?>
    
    			<a href="<?php the_permalink() ?>" rel="bookmark" title="<?php the_title();?>"><img src="<?php bloginfo('template_directory'); ?>/scripts/timthumb.php?src=<?php echo $clipping_image ?>&h=140&w=140&zc=1"></a>
    				<p class="single-small-post-cat"><?php if( $terms_of_post ) echo $terms_of_post; ?></p><p class="single-small-post-number">#<?php echo get_post_meta($post->ID,'incr_number',true); ?></p>
    
    		</div>
            		<?php $found_none = '';
         			endwhile; wp_reset_query();
    ?>
    
        <?php }
      }
    }
    if ($found_none) {
    echo $found_none;
    }
    $post = $backup;  // copy it back
    wp_reset_query(); // to use the original query again
    ?>
  8. Richard Vencu
    Member
    Posted 3 years ago #

    maybe increase this number to more than 3?
    'showposts' => 3,

  9. mistercyril
    Member
    Posted 3 years ago #

    Hello,

    I tried fcarthy's code but it throws back a bunch of posts even if they aren't using taxonomy terms...

    I only have one post that uses one of my "Brands" taxonomy terms, so this script should only return one post. I don't understand how its fetching the results.

    I am trying to display related "posts" on a custom post type's single.php template, based on wether or not the posts are associated with at least one of the same terms (in my Brands taxonomy) as the current custom post.

    Does someone know how to do this? I just can't get this to work properly.
    Thanks,
    C.

  10. fcarthy
    Member
    Posted 3 years ago #

    @mistercyril

    Here is the code I have thats functioning. It looks for your specified custom taxonomy, and outputs up to 4 posts that have the same tags. The only problem with this code is that it will find 4 posts for EACH term found. I haven't been able to make it a total of 4 posts.

    <?php
    global $post;
    $terms = get_the_terms( $post->ID , 'badge', 'string'); //change badge to your taxonomy, these are my tags
    $do_not_duplicate[] = $post->ID;
    $postnum = 0;
    
    if(!empty($terms)){
        foreach ($terms as $term) {
            query_posts( array(
            'badge' => $term->slug, //custom taxonomy, these are my tags
            'showposts' => 4,
    	'orderby' => 'rand', //orders results randomly
            'caller_get_posts' => 1,
            'post__not_in' => $do_not_duplicate ) );
            if(have_posts()){ ?>
    
                <?php while ( have_posts() ) : the_post(); $do_not_duplicate[] = $post->ID; ?>
    		<?php $postnum++; //generates a unique number for each post and applies it to its class to have theming control ?>
    	<div class="post-<?php echo $postnum; ?>">
          			<?php
    			$clipping_image = get_post_meta( $post->ID, 'clipping_image', true );
    			$terms_of_post = get_the_term_list( $post->ID, 'type', '','', '', '' );//change type to your post type ?>
    
    				<?php if( $terms_of_post ) echo $terms_of_post; ?>
    
    		</div>
            		<?php $found_none = '';
         			endwhile; wp_reset_query();
    ?>
    
        <?php }
      }
    }
    if ($found_none) {
    echo $found_none;
    }
    $post = $backup;  // copy it back
    wp_reset_query(); // to use the original query again
    ?>

    *Place code in loop

    I've commented some of my code to help you out. I may have left some divs or p tags around from my own code by accident. Please check before using. Let me know if that works.

  11. sevenspark
    Member
    Posted 3 years ago #

    @fcarthy, @mistercyril

    Just taking a quick look at that code, if you want it to just print the first 4 posts that it finds, I think you can simply add this to the beginning of the foreach loop:

    if($postnum >= 4) break;

    Not tested or anything, but I hope that helps.

  12. fcarthy
    Member
    Posted 3 years ago #

    I forgot to mention to place my code in the loop.

    @sevenspark My problem has always been that I want it to randomize how it finds the posts. If i get what you're saying this is what would happen:

    Say we have a post with 2 tags. If the first tag has 4 or more posts associated with it, it would display those 4 posts, but disregard the second tag.

  13. sevenspark
    Member
    Posted 3 years ago #

    @fcarthy, yes that is correct.

    If you want be "fair" with the tag distribution, you'd want to collect 4 potential posts from each tag in arrays and then loop through each until you have enough. You might do something like this:

    $potentials = array();
    $c = 0;
    $totalFound = 0;
    //Gather your posts for each term
    foreach($terms as $term_name){
       $q = array(
    	'my_custom_tax' =>	$term_name,
    	'numberposts' => 4
       );
       $posts = get_posts($q);
       $totalFound+= count($posts);
       $potentials[$c++] = array_reverse($posts);
    }
    $count = 0;  //The number of good posts we've found
    $index = 0;  //Number of posts we've tried
    $max = $totalFound > 4 ? 4 : $totalFound;
    $posts = array();
    //Now pick one post from each term until we reach our quota, or have checked them all
    while($count < $max){
      //Pop off a post to use
      $post = array_pop($potentials[$index++]);
      //only works if there is a post left in this array
      if($post){
        //don't take duplicates
        if($empty($posts[$post->ID])){
          $posts[$post->ID] = $post;
          $count++;
        }
      }
      $index = ($index % 4);
    }
    foreach($posts as $post){
      //print your posts
    }
    

    This is off the top of my head and untested, but it's something to play with at least :)

  14. mistercyril
    Member
    Posted 3 years ago #

    @sevenspark Thanks for your input.

    I just tried it out and am getting : Warning: Invalid argument supplied for foreach()

    I replaced 'my_custom_tax' with 'Brands' (the taxonomy i want to use). Is this where I should be doing this?

    Also, how can I limit the search to a specific post-type (i.e. posts)?

    Here's what i have so far:

    <?php
    $potentials = array();
    $c = 0;
    $totalFound = 0;
    //Gather your posts for each term
    foreach($terms as $term_name){
       $q = array(
    	'Brands' => $term_name,
    	'post_type' => 'post',
    	'numberposts' => 4
       );
       $posts = get_posts($q);
       $totalFound+= count($posts);
       $potentials[$c++] = array_reverse($posts);
    }
    $count = 0;  //The number of good posts we've found
    $index = 0;  //Number of posts we've tried
    $max = $totalFound > 4 ? 4 : $totalFound;
    $posts = array();
    //Now pick one post from each term until we reach our quota, or have checked them all
    while($count < $max){
      //Pop off a post to use
      $post = array_pop($potentials[$index++]);
      //only works if there is a post left in this array
      if($post){
        //don't take duplicates
        if($empty($posts[$post->ID])){
          $posts[$post->ID] = $post;
          $count++;
        }
      }
      $index = ($index % 4);
    }
    foreach($posts as $post){
      //print your posts
    }
    ?>
  15. sevenspark
    Member
    Posted 3 years ago #

    Yeah, the code isn't complete. It's assumed you already have $terms in an array to loop through. You could test it by setting:

    $terms = array( 'term1', 'term2', 'term3' );

    at the top.

    my_custom_tax should get replaced with the slug of the custom taxonomy (most likely 'brands' not 'Brands')

    To limit the post type just set 'post_type' => 'post' in the $q array.

  16. mistercyril
    Member
    Posted 3 years ago #

    ah but that means terms need to be hard coded right? I think fcarthy and I need to get the terms from the custom post type being viewed and search for corresponding posts (using the same taxonomy terms).

    Am I completely off track?

  17. sevenspark
    Member
    Posted 3 years ago #

    I was just suggesting hardcoding for testing. The code assumes an array - how that array is populated (statically or dynamically) doesn't matter. To get them dynamically you could use one of the strategies above, for example:

    $terms = get_the_terms( $post->ID , 'badge', 'string');

    (in that case, you would use this as the top of the foreach function:

    foreach($terms as $term){
    $term_name = $term->slug;
    ....
    

    How you set up the $terms array beforehand is totally up to you. Good luck :)

  18. mistercyril
    Member
    Posted 3 years ago #

    I'm sorry but I think the fatigue has taken a toll on my comprehension skills... I can't wrap my head around this. Would you mind posting an example of the entire loop?

    I know its a lot to ask but i'm loosing my head over this :-(

  19. sevenspark
    Member
    Posted 3 years ago #

    It's untested, so there may well be typos/errors/other issues, but this is the main idea. The presumption is that we're working with a $post and that we're finding other posts in the same custom taxonomy tag set.

    $global $post;
    
    //Get the terms for the current post
    $terms = get_the_terms( $post->ID , 'badge', 'string');
    
    $potentials = array();
    $c = 0;
    $totalFound = 0;
    
    //Gather your posts for each term
    foreach($terms as $term_name){
       $q = array(
    	'my_custom_tax' => $term_name, //term to retrieve from custom taxonomy
    	'numberposts' => 4,  //limit to 4 posts
    	'post_type' => 'post', //get only posts
    	'exclude' => $post->ID //exclude current post
       );
       $posts = get_posts($q);
       $totalFound+= count($posts);
       $potentials[$c++] = array_reverse($posts); //reverse the array so 'pop' grabs the 'first' element
    }
    
    $count = 0;  //The number of good posts we've found
    $index = 0;  //Number of posts we've tried
    $max = $totalFound > 4 ? 4 : $totalFound;  //The max we can find
    $posts = array();
    
    //Now pick one post from each term until we reach our quota,
    //or have checked them all
    while($count < $max){
    
      //Pop off a post to use
      $rpost = array_pop($potentials[$index++]);
    
      //if we got a post (if there was one left)
      if($rpost){
        //don't take duplicates
        if(!isset($posts[$rpost->ID])){
          $posts[$rpost->ID] = $rpost;
          $count++;
        }
      }
      $index = ($index % 4); //rotate through the 4 term arrays
    }
    foreach($posts as $rpost){
      //print your related posts
      echo '<h3>'.$rpost->post_title.'</h3>';
      echo '<div>'.$rpost->post_excerpt.'</h3>';
    }
    
  20. mistercyril
    Member
    Posted 3 years ago #

    Ok,

    This generates a few errors : preg_split() expects parameter 2 to be string, array given

    and then lists the 4 latest posts.

  21. sevenspark
    Member
    Posted 3 years ago #

    Forgot to integrate the term term_name issue

    $global $post;
    
    //Get the terms for the current post
    $terms = get_the_terms( $post->ID , 'badge', 'string');
    
    $potentials = array();
    $c = 0;
    $totalFound = 0;
    
    //Gather your posts for each term
    foreach($terms as $term){     //changed to $term
       $term_name = $term->slug;  //new
       $q = array(
    	'my_custom_tax' => $term_name, //term to retrieve from custom taxonomy
    	'numberposts' => 4,  //limit to 4 posts
    	'post_type' => 'post', //get only posts
    	'exclude' => $post->ID //exclude current post
       );
       $posts = get_posts($q);
       $totalFound+= count($posts);
       $potentials[$c++] = array_reverse($posts); //reverse the array so 'pop' grabs the 'first' element
    }
    
    $count = 0;  //The number of good posts we've found
    $index = 0;  //Number of posts we've tried
    $max = $totalFound > 4 ? 4 : $totalFound;  //The max we can find
    $posts = array();
    
    //Now pick one post from each term until we reach our quota,
    //or have checked them all
    while($count < $max){
    
      //Pop off a post to use
      $rpost = array_pop($potentials[$index++]);
    
      //if we got a post (if there was one left)
      if($rpost){
        //don't take duplicates
        if(!isset($posts[$rpost->ID])){
          $posts[$rpost->ID] = $rpost;
          $count++;
        }
      }
      $index = ($index % 4); //rotate through the 4 term arrays
    }
    foreach($posts as $rpost){
      //print your related posts
      echo '<h3>'.$rpost->post_title.'</h3>';
      echo '<div>'.$rpost->post_excerpt.'</h3>';
    }
    
  22. mistercyril
    Member
    Posted 3 years ago #

    So this is exactly what i put into my page, and all I get is the last 4 posts published on the blog.

    [Code moderated as per the Forum Rules. Please use the pastebin]

    If I replace 'Brands' with 'brands' I get :
    Warning: array_pop() [function.array-pop]: The argument should be an array in

    This error message displays once for every term associated to the current post (in my case, 6 times) and is then followed by the last 4 posts published on the blog.

    Does this make any sense?

  23. sevenspark
    Member
    Posted 3 years ago #

    1. the code you posted still uses 'my_custom_tax' - it should be "brands" or "Brands" or whatnot (that's where I assume the lowercase version would be better, but I don't know, it depends on your code. Usually slugs are lower-case).

    2. Yeah, the code assumes there are only 4 terms applied to the current post. If your current post has 6 terms, this code will fail the way it currently works. For the purposes of this example, you can truncate the array like this:

    //Get the terms for the current post
    $terms = get_the_terms( $post->ID , 'Brands', 'string');
    $terms = array_splice($terms, 4);

    To make it work for all cases, instead of truncating the array I *think* all you'd need to change is:

    $index = ($index % 4); //rotate through the 4 term arrays
    to

    $index = ($index % count($terms)); //rotate through the n term arrays

    [Please post code snippets between backticks or use the code button.]

  24. mistercyril
    Member
    Posted 3 years ago #

    OMG Your a GENIUS!

    THANK YOU THANK YOU THANK YOU THANK YOU!!!!!

    I'm going to relay the info to all the others who have been desperately looking for this kind of solution.

    Thanks again ;-)

  25. sevenspark
    Member
    Posted 3 years ago #

    Haha glad we got it sorted :)

  26. mistercyril
    Member
    Posted 3 years ago #

    Ok so i've got one last one, but this shouldn't be too hard... considering ;-)

    I'd like to display the post's thumbnails using the_post_thumbnail();

    But after testing my method it just displays the current post's thumbnail 4 times instead of the related post's thumbs...

  27. sevenspark
    Member
    Posted 3 years ago #

    How about

    echo get_the_post_thumbnail( $rpost->ID );
    The the_post_thumbnail function uses the global post variable. In the loop we can pass the ID of the individual posts to the get version.

    [Please post code snippets between backticks or use the code button.]

  28. mistercyril
    Member
    Posted 3 years ago #

    Ah right! Perfect!

    So I've been testing this script and its working as it should but there are a few cases where I don't understand what is happening.

    Ignored terms
    Some terms are ignored all together. For instance the 'Brand' (taxonomy) term with the most associated posts never gets pulled into the results.

    No results displayed
    If my custom post type has several terms associated with it, but only one of these terms is associated to posts, then nothing shows up.

    I noticed that if I changed $terms = array_splice($terms, 4); to $terms = array_splice($terms, 1); for example, it displays posts but only from one brand...

    Posts from a single brand only
    An other problem I noticed is that posts from a single brand are displayed (in some cases) even if there are several terms associated with it and each of these terms have posts as well.

    Now this is weird behaviour, I don't get why its doing that.

    Any ideas?

Topic Closed

This topic has been closed to new replies.

About this Topic