Getting an alphabetic post list with two letters onclick on letter

Maybe I’ve not understand your question, in this case sorry, my english is far from perfect.

I understand that you have a page template that show the letters list and when you click a letter, the same page show a list of posts where ‘post_title’ start with this letter.

Now you want that when you are showing that list, is possible to filter posts using 2 letters instead of one, where the first letter is the one choosen before.

Is this right? If so, this should works:

<?php
/**
 * Template Name: Capital Template
*/

get_header();

the_post();
$page_link = get_permalink();

$letter = isset($_GET['letter']) ? $_GET['letter'] : null;
// limit the 'letter' param to 2 chars
if ( $letter && strlen($_GET['letter'])>2 ) $letter = substr($letter, 0, 2);
?>

<?php
// PAGE FILTERS PART
$letters = array('A','B','C','D','E','F','G','H','I','J','K','L','M',
                 'N', 'O','P','Q','R','S','T','U','V','W','X','W','Z');
if ( ! $letter ) {
  // adjust the query args to fit your needs
  $args = array('posts_per_page' => 50, 'orderby' => 'post_title', 'order' => 'ASC');
  $posts = get_posts($args);
} else {
  global $wpdb;
  // adjust the query to fit your needs  
  $posts = $wpdb->get_results( $wpdb->prepare(
    "SELECT * FROM $wpdb->posts WHERE
    post_type="post" AND post_status="publish" AND
    post_title LIKE %s ORDER BY post_title ASC",
    $letter . '%'
  ) );
}
echo '<ul>';
foreach ( $letters as $l) {
  $param = $letter ? $letter.$l : $l;
  printf('<li><a href="https://wordpress.stackexchange.com/questions/108270/%s?letter=%s">%s</a></li>',$page_link, $param, $param);
}
echo '</ul>';
if ($letter) {
  printf('<p id="#filter-remover"><a href="https://wordpress.stackexchange.com/questions/108270/%s?letter=%s">%s</a>',
    $page_link, $letter,
    __('Back to capital letter &quot;' . $letter . '&quot; filter')
  );
  printf(', <a href="%s">%s</a><p>',
    $page_link,
    __('or remove capital letter filter at all.')
  );
}
?>

<?php
// PAGE CONTENT PART
global $post;
if ( ! empty($posts) ) :
  foreach ($posts as $post) {
    setup_postdata($post);
?>

<?php /*  YOUR LOOP CODE HERE */ ?>

<p><?php the_title(); ?></p>

<?php
}
else :
  echo '<p>';
  _e('Sorry, no posts match your criteria.');
  if ( $letter ) {
    printf('<br /><a href="%s">%s</a>',
  $page_link,
  __('Try to remove the capital letter filter.')
);
  }
  echo '</p>';
endif;
wp_reset_postdata();


get_footer();

Edit: jquery trick

One fault of my code above is that the filter shows all capital letter(s), even if there is no posts for that: if the filter ‘A’ not have post, also ‘AB’, ‘AC’ and so on will not have posts so, why show them?
Using php you can loop posts and put only filter that have a chance to get some results, but this mean looping 2 times.
I think there is a better solution: jQuery.

Note: this works only if you the query shows all posts, or better, all the posts you want to filter out (i.e. no LIMIT clause in sql query)

First of all, in ‘FILTER’ part, change the foreach loop in this:

echo '<ul class="letter-filters">';
foreach ( $letters as $l) {
  $param = $letter ? $letter.$l : $l;
  printf('<li><a href="https://wordpress.stackexchange.com/questions/108270/%s?letter=%s" class="letter-filter">%s</a></li>',
    $page_link, $param, $param
  );
}
echo '</ul>';

Then in the loop code part, use something like this:

<?php
echo '<div id="posts-container" data-letter="' . $letter. '">';
if ( ! empty($posts) ) :
  foreach ($posts as $post) {
    setup_postdata($post);
   /*  YOUR LOOP CODE */
   $datacapital = $letter ? strtoupper( substr(get_the_title(), 0, strlen($letter)+1 ) ) : '';
?>
  <div class="post" id="post-<?php the_ID()?>" data-capital="<?php echo $datacapital; ?>">
  <p><?php the_title(); ?></p>
 </div>

 ...

 endif;
 echo '</div>';
 wp_reset_postdata();

Finally, the jQuery code:

<script>
// please, do not use jquery like so,
// register and enqueue it like every WP developer should do :)
// this is for semplicity of answer

jQuery().ready(function($) {
  if ( $('#posts-container').data('letter') != '' ) {
    var oldcapital = null;
    $('ul.letter-filters li').hide();
    $('.post').each(function() {
      var capital = $(this).data('capital');
      if ( ! oldcapital || (oldcapital != capital) ) {
        oldcapital = capital;
        $('a.letter-filter').each(function() {
           if ( $(this).text() == capital ) $(this).parent().show(); 
        });
        }
      });
    }
  });
</script>

Please note I’m not a js/jquery developer, maybe there is a more elegant/performant way to to do the same thing…

Of course this is not the full code of the page, just the relevant part.

Hope it helps.

Leave a Comment