Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

Related posts within a taxonomy. WordPress

  • SOLVED

I would like someone to help me with a snippet of code that displays a defined number of related posts within the displayed posts taxonomy.

As an example, here is what I am working on.

http://www.nolinlakerealestate.com/mls/102-scenic-dr-mammoth-cave-ky-42259-mls-1310786

[[LINK href="http://www.nolinlakerealestate.com/mls/102-scenic-dr-mammoth-cave-ky-42259-mls-1310786"]][[/LINK]]

As you can see at the bottom I am listing related properties. There is a snippet of code that is showing a random selection of posts within the custom post type of "mls".

I would also like to show a list of subdivisions ( a taxonomy ) that are in the same city taxonomy as the current post.

Logic is something like this.

Find out what city taxonomy the current post is in.
Query database for other posts in same city taxonomy.
Put together a list of subdivision taxonomies that exist on those posts.
Output list with links to the subdivision taxonomy pages, and the subdivision names.
Eg : http://www.nolinlakerealestate.com/subdivision/the-point-at-nolin

Answers (3)

2012-09-26

John Cotton answers:

You need to use WP_Query. Something like this:


global $post;

// this gets the term(s) for the subdivison tax for the current post
if( $terms = wp_get_object_terms($post->ID, 'subdivision') ) {

// Use the first one (you could use more) to get other posts:
$args = array(
'post__not_in' => $post->ID, // we don't want the current post!!
'tax_query' => array(
array(
'taxonomy' => 'subdivision',
'field' => 'slug',
'terms' => $terms[0]->slug
)
)
);
$query = new WP_Query( $args );

while($query->have_posts()):
$the_query->the_post();

// Do whatever layout you want.
echo '<li>';
echo the_title();
echo '</li>';
endwhile;
}


Scott Hack comments:

John,

After reading through your code and the comments, I want to add this. To be sure that things are clear.

I am viewing a post within the custom post type of mls.

The post is located in both the city1 taxonomy and the subdivision1 taxonomy.

I have queried the database and I have found that city1 shows up on 20 other posts. Those 20 other posts have a mixture of subdivision1, subdivision2, subdivision3, subdivision4, and subdvision5.

I then output a list of the 5 subdivision taxonomy links.

Attempt to implement your code has generated the following error messages/warnings.

Warning: array_map() [function.array-map]: Argument #2 should be an array in wp-includes/query.php on line 2162

Warning: implode() [function.implode]: Invalid arguments passed in /wp-includes/query.php on line 2162

-Scott


John Cotton comments:

Bryan is right, it will only display other listings within the current subdivision - sorry, I thought that's what you wanted.

So you want a list of the subdivison terms that are used by posts in the same city taxonomy - is that right?

if so:


global $post;

// this gets the term(s) for the city tax for the current post
if( $terms = wp_get_object_terms($post->ID, 'city', array('fields' => 'ids') ) ) {
// Then do the reverse for the subdivsiion tax
$sd = get_objects_in_term( $terms, 'subdivision' );

foreach( $sd as $term ) {
// Loop through and output
}
}



John Cotton comments:

Sorry, ignore last, I wasn't paying attention.


John Cotton comments:

Let's start again. Keep the code I had in my first response, but change the taxonomy query to city1 (or whatever it is called).

That will give you a list of posts in that city.

But change the $query loop at the bottom to this:


$post_ids = [];

while($query->have_posts()):
$the_query->the_post();

// make an array of post ids from the city
$post_ids[] = get_the_ID();
endwhile;

//Now use that array of city posts to get the terms
$terms = wp_get_object_terms($post_ids, 'subdivision');

//Finally, loop through to output
foreach( $terms as $term ) {
//etc.
}


There are several queries in this and Bryan's custom SQL may well give better performance. But I tend to think the benefits of using the WordPress API outweighs performance in many cases (you are protected from database design changes for a start).


Scott Hack comments:

Using the code below, and I'm getting an error message around the line with "$post_ids = [];" -- says Parse error: syntax error, unexpected '['


<?php
global $post;

// this gets the term(s) for the subdivison tax for the current post
if( $terms = wp_get_object_terms($post->ID, 'city') ) {
// Use the first one (you could use more) to get other posts:
$args = array(
'post__not_in' => $post->ID, // we don't want the current post!!
'tax_query' => array(
array(
'taxonomy' => 'subdivision',
'field' => 'slug',
'terms' => $terms[0]->slug
)
)
);
$post_ids = [];

while($query->have_posts()):
$the_query->the_post();
// make an array of post ids from the city
$post_ids[] = get_the_ID();
endwhile;

//Now use that array of city posts to get the terms
$terms = wp_get_object_terms($post_ids, 'subdivision');

//Finally, loop through to output

foreach( $terms as $term ) {
echo '<li>';
echo the_title();
echo '</li>';
} ?>


John Cotton comments:

Sorry, that line should be


$post_ids = array();


Scott Hack comments:

See any reason why I would get this error?

PHP Parse Error: syntax error, unexpected $end


John Cotton comments:

Perhaps because I've given you lots of bits and you've not quite pasted them together...:)

Here it all is:


global $post;

// this gets the term(s) for the subdivison tax for the current post
$terms = wp_get_object_terms($post->ID, 'city');

if( !is_wp_error($terms) ) {
// Use the first one (you could use more) to get other posts:
$args = array(
'post_type' => 'post', // you need to change this if you're using a custom post type
'post__not_in' => $post->ID, // we don't want the current post!!
'tax_query' => array(
array(
'taxonomy' => 'city',
'field' => 'slug',
'terms' => $terms[0]->slug
)));
$query = new WP_Query( $args );

$post_ids = array();

while($query->have_posts()) {
$the_query->the_post();
$post_ids[] = get_the_ID();
}

// Now use that array of city posts to get the terms
$terms = wp_get_object_terms($post_ids, 'subdivision');

// Finally, loop through to output
foreach( $terms as $term ) {
//etc.
}
}


Scott Hack comments:

I think we are closer, but I am getting an error still.

Warning: array_map() [function.array-map]: Argument #2 should be an array in /wp-includes/query.php on line 2162

Warning: implode() [function.implode]: Invalid arguments passed in /wp-includes/query.php on line 2162


John Cotton comments:

Can you try hardcoding this line to one of the cities

'terms' => $terms[0]->slug

eg 'terms' => 'chicago'

just to see if that's the problem. I've just run the code on my server and it works fine for me so it might be local thing.


Scott Hack comments:

Same error message.

http://www.nolinlakerealestate.com/mls/235-oakland-hills-cir-leitchfield-ky-42754-mls-1299395

The "Other Properties" snippet I am using is calling wp_reset_query -- so I thought that it might be conflicting, so I moved it above that snippet, but still no joy.

I am running a debug toolbar and it gave me this longer error message. Hopefully it helps.

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) LEFT JOIN (SELECT wp_postmeta.post_id,wp_postmeta.meta_value as sort_option FROM wp_postmeta WHERE meta_key = '_current_price') AS SO ON wp_posts.ID = SO.post_id WHERE 1=1 AND wp_posts.ID NOT IN () AND ( wp_term_relationships.term_taxonomy_id IN (6602) ) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY SO.sort_option+0 DESC LIMIT 0, 10
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') AND ( wp_term_relationships.term_taxonomy_id IN (6602) ) AND wp_posts.post_typ' at line 1


Scott Hack comments:

BTW -- where is $term ever introduced? The line "foreach( $terms as $term ) {"

I see $terms -- but not $term in the code.


John Cotton comments:

AND wp_posts.ID NOT IN ()

is the error.

Try changing this line:

'post__not_in' => $post->ID,

to

'post__not_in' => array($post->ID),

<blockquote>BTW -- where is $term ever introduced? The line "foreach( $terms as $term ) {"</blockquote>

That's the line that introduces it - it says "take each item from the array in turn and assign it to $term".


Scott Hack comments:

I'm still trying, but still getting error messages. This time the message is :

Fatal error: Call to a member function the_post() on a non-object in ... looks like it is picking on this line :

"$the_query->the_post();"

<?php
global $post;

// this gets the term(s) for the subdivison tax for the current post
$terms = wp_get_object_terms($post->ID, 'city');

if( !is_wp_error($terms) ) {
// Use the first one (you could use more) to get other posts:
$args = array(
'post_type' => 'mls', // you need to change this if you're using a custom post type
'post__not_in' => array($post->ID), // we don't want the current post!!
'tax_query' => array(
array(
'taxonomy' => 'city',
'field' => 'slug',
'terms' => $terms[0]->slug
)));
$query = new WP_Query( $args );
$post_ids = array();
while($query->have_posts()) {
$the_query->the_post();
$post_ids[] = get_the_ID();
}

// Now use that array of city posts to get the terms
$terms = wp_get_object_terms($post_ids, 'subdivision');
// We test to see if anything exists.
print_r ($terms);
// Finally, loop through to output
echo '<h2>Other Subdivisions</h2>';
foreach( $terms as $term ) {
// do something
}
}
?>


John Cotton comments:

It should be $query->the_post()


Scott Hack comments:

John, great news! You've done it! The sad part is I still need help with the // do something part.

In the code above, you can see at the bottom I've done a print_r ( one of the things I've learned for debugging ) but my php is still so weak, getting this array into the format I want is still daunting.

Array ( [0] => stdClass Object ( [term_id] => 6632 [name] => POPLAR RIDGE [slug] => poplar-ridge [term_group] => 0 [term_taxonomy_id] => 6636 [taxonomy] => subdivision [description] => [parent] => 0 [count] => 2 ) [1] => stdClass Object ( [term_id] => 6613 [name] => SUNSET HARBOR [slug] => sunset-harbor [term_group] => 0 [term_taxonomy_id] => 6617 [taxonomy] => subdivision [description] => [parent] => 0 [count] => 2 ) [2] => stdClass Object ( [term_id] => 6613 [name] => SUNSET HARBOR [slug] => sunset-harbor [term_group] => 0 [term_taxonomy_id] => 6617 [taxonomy] => subdivision [description] => [parent] => 0 [count] => 2 ) [3] => stdClass Object ( [term_id] => 6605 [name] => WATERFRONT SHORES [slug] => waterfront-shores [term_group] => 0 [term_taxonomy_id] => 6609 [taxonomy] => subdivision [description] => [parent] => 0 [count] => 1 ) )


John Cotton comments:

This is a simple list:


echo '<ul>';
foreach ($terms as $term) {
echo '<li><a href="'.get_term_link($term->slug, 'species').'">'.$term->name.'</a></li>';
}
echo '</ul>';


John Cotton comments:

'species' should, of course, be 'subdivision' - I just copied that from the codex :)


Scott Hack comments:

John,

How do I tell it to only display a subdivision link once?

As an example, see this link.

http://www.nolinlakerealestate.com/mls/5-sanctuary-ln-clarkson-ky-42726-mls-1342483


Scott Hack comments:

Found this via Google and it appears to be doing the trick.

$input = array_map("unserialize", array_unique(array_map("serialize", $input)));


Scott Hack comments:

John,

Can you think of a way to exclude the current post's subdivision from the array? I was thinking about just doing an if statement in the loop, and if they matched, not display it... but I am guessing that is pretty dirty way of doing it and could slow down page load times.

-Scott


John Cotton comments:

<blockquote>Can you think of a way to exclude the current post's subdivision from the array? I was thinking about just doing an if statement in the loop, and if they matched, not display it... </blockquote>
That's the only way of doing it when using the wp_get_object_terms function. There are no parameters to pass to limit the results, there is not term id based access to the array that is return (by which you could instantly exclude the value) so a loop through to compare is the only option. Since you will be looping through to output, the extra overhead of a test like

if( $term-> != $post_sub_division)

(having previously set $post_sub_division to the term id of the current post) is minimal. Really, you won't notice it.

2012-09-26

Bryan Headrick answers:

there might be a better way to do this, but the easiest way that comes to mind is to do a custom sql query. First, you need to get the City of the current post: $cities = get_the_terms(get_the_ID(),'city');
//and we'll get the subdivision so we can exclude it later:
$subdivisions = get_the_terms(get_the_ID(),'subdivision');

This will give you an array (obviously each post will only have one city, but wordpress doesn't have a function to return only one taxonomy item) so we need to get down to a single item:
if ( $cities && ! is_wp_error( $cities ) ) :
$city_id='';
foreach ( $cities as $city ) {
$city_id = $city->term_id;
}
if ( $cities && ! is_wp_error( $cities ) ) :
$subdivision_id='';
foreach ( $subdivs as $subdiv ) {
$current_subdiv_id = $subdiv->term_id;
}
endif;


Now, we need to find all (or some) of the subdivisions with posts within this state:

global $wpdb;
$subdivs=$wpdb->get_col(
$wpdb->prepare(
"select distinct(t.term_id) from $wpdb->terms t
join $wpdb->term_taxonomy tt on tt.term_id = t.term_id
join $wpdb->term_relationships tr on tt.term_taxonomy_id = tr.term_taxonomy_id
join
(select id from $wpdb->posts p
join $wpdb->term_relationships tr on p.id = object_id
join $wpdb->term_taxonomy tt on tt.term_taxonomy_id = tr.term_taxonomy_id
where tt.term_id = $city) p on p.id=tr.object_id
where tt.taxonomy = 'subdivision'
and t.term_id<>$current_subdiv_id
limit 5"
));
$subdiv_links = Array();
foreach($subdivs as $subdiv_id){
$subdiv=get_term_by('id',$subdiv_id,'subdivision');
$subdiv_links[]='<a href="'.get_term_link($subdiv->slug, 'subdivision').'">'.$subdiv->name.'</a>';
}


Bryan Headrick comments:

John, I believe that will only display other listings within the current subdivision, rather than other subdivisions in the current state.


Scott Hack comments:

Bryan,

I added a ; to the end of global $wpdb;

But I am also getting this error somewhere around the first line of the join...

syntax error, unexpected T_STRING

-Scott


Bryan Headrick comments:

I had my openning quote in the wrong place. It's fixed now.


Scott Hack comments:

Getting an Parse error: syntax error, unexpected $end


Bryan Headrick comments:

d'oh! Left out another semicolon
fixed


Scott Hack comments:

Still getting the unexpected $end

2012-09-27

Arnav Joy answers:

try this


define this in functions.php

function tax_related_posts($taxonomy = '') {

global $post;

if($taxonomy == '') { $taxonomy = 'post_tag'; }

$tags = wp_get_post_terms($post->ID, $taxonomy);

if ($tags) {
$first_tag = $tags[0]->term_id;
$second_tag = $tags[1]->term_id;
$third_tag = $tags[2]->term_id;
$args = array(
'post_type' => get_post_type($post->ID),
'posts_per_page' => 4,
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => $taxonomy,
'terms' => $second_tag,
'field' => 'id',
'operator' => 'IN',
),
array(
'taxonomy' => $taxonomy,
'terms' => $first_tag,
'field' => 'id',
'operator' => 'IN',
),
array(
'taxonomy' => $taxonomy,
'terms' => $third_tag,
'field' => 'id',
'operator' => 'IN',
)
)
);
$related = get_posts($args);
$i = 0;
if( $related ) {
global $post;
$temp_post = $post;
foreach($related as $post) : setup_postdata($post);
$content .= '<ul class="related-posts-box">';
$content .= '<li><a href="' . get_permalink() . '" title="' . get_the_title() . '">' . get_the_title() . '</a></li>';
$content .= '</ul>';

endforeach;
$post = $temp_post;
}
}

return $content;
}


and call it as

<?php echo tax_related_posts(); ?>

or

<?php echo tax_related_posts('your_tax_name'); ?>

or

see these two question

http://wpquestions.com/question/show/id/1603

http://wpquestions.com/question/show/id/2659