8

I have a page with two sections, each uses a different WP_Query() to pull in events, which are a custom post type. Each WP_Query() queries a meta_key for the event date so that Section 1 only displays upcoming events and Section 2 displays past events.

The upcoming events in Section 1 display all relevant information on my page, so clicking them is not possible.

The past events in Section 2 only display the event title and are clickable. When users click a past event they link to a custom single-event.php template for the past event.

I want to display Previous/Next navigation in the single-event.php template, but the navigation should only point to past events.

I tried using next_post_link() and previous_post_link() but these will link to upcoming events too, which I do not want. I can probably setup a new WP_Query() on my single-event.php and loop through it to get the Prev/Next IDs, but repeating the query seems like a drastic step.

I would really appreciate some insight on a way to filter out upcoming events from my Previous/Next post links. I've seen this question but I would prefer not to use a plugin.

  • 1
    the answer to this is hinted in the other answer to the question you linked - you'll need to filter the join/where/sort clauses in the get_adjacent_post function. – Milo Mar 27 '14 at 19:14
  • Yes it is hinted to. I'd really love to see a working example if possible. – cfx Mar 27 '14 at 19:21
  • 1
    I don't have time to code something at the moment, but a good starting point would be to perform a query via WP_Query, then inspect $your_query_object->request, which will reveal a good bit of the SQL you'll need to pull it off. – Milo Mar 27 '14 at 19:31
  • Thanks for the hint @Milo, think I figured it out! See below! – cfx Mar 27 '14 at 21:15
6

I managed to get this working using nothing but WordPress filters, thanks to @Milo's hint.

Just note that these are pretty specific to my case but you shouldn't have a problem modifying them for your own use. I am using Advanced Custom Fields with a Date Picker field called date and Prev/Next links only point to events with date fields set to any day before today.

I created 5 filters:

  • 1 to modify JOIN (to add wp_postmeta)
  • 1 to modify WHERE for the Previous link
  • 1 to modify WHERE for the Next link
  • 1 to modify SORT for the Previous link
  • 1 to modify SORT for the Next link

Here's what I came up with, it seems to be working but if anyone spots any issues I would love feedback:

function get_adjacent_past_events_join($join) {
  if(is_singular('event')) {
    global $wpdb;
    $new_join = $join."INNER JOIN $wpdb->postmeta AS m ON p.ID = m.post_id ";
    return $new_join;
  }
  return $join;
}
add_filter('get_previous_post_join', 'get_adjacent_past_events_join');
add_filter('get_next_post_join', 'get_adjacent_past_events_join');

function get_prev_past_events_where($where) {
  if(is_singular('event')) {
    global $wpdb, $post;
    $id = $post->ID;
    $current_event_date = get_field('date', $id);
    $today = date('Ymd');
    $new_where = "WHERE p.post_type = 'event' AND p.post_status = 'publish' AND (m.meta_key = 'date' AND (m.meta_key = 'date' AND CAST(m.meta_value AS CHAR) < '$today')) AND (m.meta_key = 'date' AND (m.meta_key = 'date' AND CAST(m.meta_value AS CHAR) < '$current_event_date'))";
    return $new_where;
  }
  return $where;
}
add_filter('get_previous_post_where', 'get_prev_past_events_where');

function get_next_past_events_where($where) {
  if(is_singular('event')) {
    global $wpdb, $post;
    $id = $post->ID;
    $current_event_date = get_field('date', $id);
    $today = date('Ymd');
    $new_where = "WHERE p.post_type = 'event' AND p.post_status = 'publish' AND (m.meta_key = 'date' AND (m.meta_key = 'date' AND CAST(m.meta_value AS CHAR) < '$today')) AND (m.meta_key = 'date' AND (m.meta_key = 'date' AND CAST(m.meta_value AS CHAR) > '$current_event_date'))";
    return $new_where;
  }
  return $where;
}
add_filter('get_next_post_where', 'get_next_past_events_where');

function get_prev_past_events_sort($sort) {
  if(is_singular('event')) {
    global $wpdb;
    $new_sort = " GROUP BY p.ID ORDER BY m.meta_value+0 DESC";
    return $new_sort;
  }
  return $sort;
}
add_filter('get_previous_post_sort', 'get_prev_past_events_sort');

function get_next_past_events_sort($sort) {
  if(is_singular('event')) {
    global $wpdb;
    $new_sort = " GROUP BY p.ID ORDER BY m.meta_value+0 ASC";
    return $new_sort;
  }
  return $sort;
}
add_filter('get_next_post_sort', 'get_next_past_events_sort');
3

i had a pretty similar problem, needed to sort and exclude several posts from prev/next navigation. problem with @cfx's solution was: its not capable for ajax: the is_singular() function returns false, if you load contents via wp-ajax. so it worked on page load, but didn't, when content was changed by ajax. global $post; was helping me out here.

here is my solution:

/**
  * WP: join postmeta to our sql query, so we can filter for custom fields
  *
  * @param $join
  * @return string
  */
function jnz_adjacent_work_join( $join ) {
  global $post;
  if ( get_post_type( $post ) == 'work' ) {
    global $wpdb;
    return $join . "INNER JOIN $wpdb->postmeta AS m ON p.ID = m.post_id ";
  }
  return $join;
}
add_filter('get_previous_post_join', 'jnz_adjacent_work_join');
add_filter('get_next_post_join', 'jnz_adjacent_work_join');



/**
 * WP: Change order of post for prev / next navigation
  * exclude posts with custom field "not_clickable" set to true
  *
  * @param $where
  * @param $operator
  * @return string|void
  */
 function jnz_adjacent_work_where( $where, $operator ) {
   global $post;
   if ( get_post_type( $post ) == 'work' ) :
     global $wpdb;
     $where = $wpdb->prepare("WHERE p.post_title {$operator} '%s' AND p.post_type = 'work' AND p.post_status = 'publish' AND (m.meta_key = 'not_clickable' AND (m.meta_key = 'not_clickable' AND m.meta_value != 1))", $post->post_title );
   endif;

   return $where;
 }

 $gt = '<';
 $lt = '>';
 add_filter( 'get_next_post_where', function( $where ) use ( $lt ) {
   return jnz_adjacent_work_where( $where, $lt );
 });
 add_filter( 'get_previous_post_where', function( $where ) use ( $gt ) {
   return jnz_adjacent_work_where( $where, $gt );
 });

in this case, the costum field query is: exclude all posts, that have cf not_clickable set to true.

another problem i encountered: i had some content created and then implemented that custom field afterwards.. so the query also excluded the posts that didn't even had that field attached to the post, no matter if true or false. just keep that in mind, when using this type of filtering. make sure that every post has a value or consider this in your sql syntax..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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