Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I am trying to create a home page where it is a list of categories with an associated thumbnail for each. I would like it so it returns each category once and returns the associated thumbnail from the latest post, or even better does not require me to add a featured image to each post, but rather grabs the thumbnail from the image of the post, using post_mime_type ? I am still a ways off as it is returning the category for each post that is within that category and then associating the image with every category.

Here is the function I am working with:

    function home_filter() {
        if ( is_home() )
        {
        add_filter( 'the_content', 'home_cats' );
        }
    }
    add_action('template_redirect', 'home_filter');
    function home_cats(){
        $args=array(
        'orderby' => 'name',
        'order' => 'ASC',
        );
        $categories=get_categories($args);
           foreach($categories as $category) { 
   echo '<li><a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . '>' . get_the_post_thumbnail($page->ID, 'thumbnail') . $category->name.'</a></li> ';
        } 
}

The home page is simply :

<ul>
<?php while ( have_posts() ) : the_post(); ?>
<?php the_content();?>
<?php endwhile; ?>
</ul>

The output is something like:

<ul>
<li><a href="../?cat=1" title="View all posts in Cat A" ><img src="..image-A.jpg"  />Cat A</a></li> 
<li><a href="../?cat=3" title="View all posts in Cat B" ><img src="..image-A.jpg"  />Cat B</a></li> 
<li><a href="../?cat=1" title="View all posts in Cat A" ><img src="..image-B.jpg"  />Cat A</a></li> 
<li><a href="../?cat=3" title="View all posts in Cat B" ><img src="..image-B.jpg"  />Cat B</a></li>
<li><a href="../?cat=1" title="View all posts in Cat A" ><img src="..image-C.jpg"  />Cat A</a></li> 
<li><a href="../?cat=3" title="View all posts in Cat B" ><img src="..image-C.jpg"  />Cat B</a></li> 
 </ul>

Can someone please help me understand how to return each category only once and include the thumb from the latest post in that category? And even better can anyone think of a way to accomplish this without having to set the feature image every time?

share|improve this question
    
How many posts do you have/are displaying on the home page? Is that number three by chance? It looks like your filter is running multiple times on that page. –  mtekk Jan 22 '11 at 20:45
    
Yes you are right it returns the category for every post that exists in that category –  zac Jan 22 '11 at 22:25
add comment

2 Answers

up vote 2 down vote accepted

Hi @zac:

Normally I like to shy away from recommending direct SQL, but in your use-case I think it is warranted.

I've coded this up as a direct SQL query; as you may notice is a bit complex. You can copy the following code to your theme's functions.php file or add it to the .php file of a plugin you might be writing:

function home_cats($max_image_height=150){
  $categories= get_taxonomy_latest_posts_with_attachment('category');
  echo '<h1>Categories</h1>';
  foreach($categories as $category) {
    $category_link = get_category_link( $category->term_id );
    $category_title = sprintf( __( "View all posts with category: '%s'" ),$category->term_name );
    $post_title = get_the_title($category->post_id);
    $post_link = get_permalink($category->post_id);
    $img_html = wp_get_attachment_image( $category->attachment_id, array( 'thumbnail',$max_image_height ) );
    $html = <<<HTML
<div id="category-{$category->term_slug}" class="category">
  <span style="float:right;">{$img_html}</span>
  <span  style="float:left;">
    <h2><a href="{$category_link}" title="{$category_title}">{$category->term_name}</a></h2>
    <p style="float:left;">Latest post: <a href="{$post_link}" title="{$post_title}">$post_title</a></p>
  </span>
</div>
<br clear="both" />
HTML;
    echo $html;
  }
}
function get_taxonomy_latest_posts_with_attachment($taxonomy) {
    global $wpdb;
    $sql =<<<SQL
SELECT
  categorized_posts.rownum,
  categorized_posts.term_id,
  categorized_posts.term_name,
  categorized_posts.term_slug,
  categorized_posts.post_id,
  categorized_posts.post_date,
  categorized_posts.post_title,
  attachments.ID AS attachment_id,
  attachments.post_title AS attachment_title,
  attachments.post_mime_type AS attachment_mime_type,
  attachments.guid AS attachment_guid
FROM
  (
  SELECT
    rownum,
    term_id,
    term_name,
    term_slug,
    post_id,
    post_date,
    post_title,
    post_parent,
    post_name,
    post_type
  FROM (
    SELECT
      IF( @prev <> {$wpdb->terms}.term_id, @rownum := 1, @rownum := @rownum+1 ) AS rownum,
      @prev := {$wpdb->terms}.term_id,
      {$wpdb->terms}.term_id,
      {$wpdb->terms}.name AS term_name,
      {$wpdb->terms}.slug AS term_slug,
      {$wpdb->posts}.ID as post_id,
      {$wpdb->posts}.post_date,
      {$wpdb->posts}.post_title,
      {$wpdb->posts}.post_parent,
      {$wpdb->posts}.post_name,
      {$wpdb->posts}.post_type
    FROM
      {$wpdb->term_taxonomy}
      INNER JOIN {$wpdb->terms} ON {$wpdb->term_taxonomy}.term_id={$wpdb->terms}.term_id
      INNER JOIN {$wpdb->term_relationships} ON {$wpdb->term_relationships}.term_taxonomy_id={$wpdb->term_taxonomy}.term_taxonomy_id
      INNER JOIN {$wpdb->posts} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
      INNER JOIN (SELECT @rownum := NULL, @prev := 0) AS rownum_initializer ON 1=1
    WHERE 1=1
      AND {$wpdb->posts}.post_type='post'
      AND {$wpdb->posts}.post_status='publish'
      AND {$wpdb->term_taxonomy}.taxonomy='%s'
    ORDER BY {$wpdb->posts}.post_parent DESC, {$wpdb->posts}.post_date DESC
    ) x
  ) categorized_posts
    INNER JOIN (SELECT MAX(ID) AS post_id,post_parent FROM {$wpdb->posts} WHERE post_type='attachment' GROUP BY post_parent) attachment_join ON attachment_join.post_parent=categorized_posts.post_id
    INNER JOIN {$wpdb->posts} attachments ON attachments.ID=attachment_join.post_id
WHERE
  categorized_posts.rownum=1
GROUP BY
  categorized_posts.term_id
ORDER BY
  categorized_posts.term_name
SQL;
  return $wpdb->get_results($wpdb->prepare($sql,$taxonomy));
}

You'll note I separated the logic so that you can get the list of any taxonomy terms and their latests posts with a photo by calling the get_taxonomy_latest_posts_with_attachment() function and passing it a taxonomy identifier, like this:

$post_tags = get_taxonomy_latest_posts_with_attachment('post_tags');

Because of the complexity of the SQL in that function I'm not going to try to explain it (or I'd be here all night) but if you have specific follow up questions, just ask. Anyway, here's what the code looks like on my test side with test data:

Screenshot of a WordPress site with the list of Categories and the Latest Post for Each

P.S. The people in the photos are friends of mine and all work with WordPress in one way or another. Hope they don't mind me using their likeness. :)

share|improve this answer
    
Hey Mike, Thanks a lot! I never mess around with direct queries like this but it seems like it might be just the ticket and opens up some new doors in my themeing. –  zac Jan 24 '11 at 0:31
    
@zac - What you wanted is quite complex as you can see, so the SQL is rather advanced. You can also do it in PHP as @Rarst shows (though I've not tested his specific code for you) but the PHP approach could be much slower if you are not using caching. (P.S. Don't forget to upvote good answers, and don't forget to select a best answer when you are satisfied.) –  MikeSchinkel Jan 24 '11 at 0:37
add comment

That hook contraption is kinda messy, why not just call home_cats() in template?

Try this (note that it is query per category and can get ugly for performance):

function home_cats() {

    $args = array(
        'orderby' => 'name',
        'order' => 'ASC',
    );
    $categories = get_categories($args);

    echo '<ul>';

    foreach ( $categories as $category ) {

        $link = get_category_link($category->term_id);
        $title = sprintf(__("View all posts in %s"), $category->name);

        $posts = get_posts( array(
            'cat' => $category->term_id,
            'numberposts' => 1,
        ) );

        $post = $posts[0];

        $thumbnail =  get_the_post_thumbnail($post->ID);
        echo "<li><a href='{$link}' title='{$title}'>{$thumbnail}{$category->name}</a></li>";
    }

    echo '</ul>';
}

There are different ways to mine for images, I usually go with Get The Image.

share|improve this answer
    
Thanks but it still has the same problem of returning the category for every post that exists and including the image for every category (like in my example output) –  zac Jan 22 '11 at 22:24
    
@zac as per beginning of my answer - don't try to hook this into content, not really meant for that. Simply call home_cats() in your template directly. I am not sure what your problem with image is. How many images and which images do you want to get? –  Rarst Jan 22 '11 at 22:31
    
@zac maybe I misunderstand your code completely. Could you please write out in oyur question what you want with specific example? –  Rarst Jan 22 '11 at 22:33
    
Hi sorry for not being clearer. Yes I did try to call the function as you recommended. I am trying to just have a list of categories on the home page, each category listed once with one thumb associated with each. The thumb that is associated will be grabbed from the most recent post in that category. –  zac Jan 24 '11 at 0:33
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.