0

I am using this code to list pages with a specific letter at the beginning of them:

$first_char = 'H';
        echo '<table class="alphabet" style="border:0px;border-color:transparent;">
                    <tbody><tr>
                        <td width="100px;" style="border-color:transparent;"><span class="separator-line"></span></td>
                        <td style="border-color:transparent;padding-left:5px;padding-right:5px;"><span class="letter"><strong>' . $first_char . '</strong></span></td>
                        <td width="100px;" style="border-color:transparent;"><span class="separator-line"></span></td>
                    </tr>
                </tbody></table>';
        $postids=$wpdb->get_col($wpdb->prepare("
        SELECT      ID
        FROM        $wpdb->posts
        WHERE       SUBSTR($wpdb->posts.post_title,1,1) = %s
        ORDER BY    $wpdb->posts.post_title",$first_char)); 

        if ($postids) {
        $args=array(
          'post__in' => $postids,
          'post_type' => 'page',
          'post_status' => 'publish',
          'posts_per_page' => -1,
          'caller_get_posts'=> 1.,
        );
        $my_query = null;
        $my_query = new WP_Query($args);
        if( $my_query->have_posts()) {
          while ($my_query->have_posts()) : $my_query->the_post(); ?>
            <p><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></p>
            <?php
          endwhile;
        }
        wp_reset_query();  // Restore global post data stomped by the_post().
        }

Basically I need to be able to exclude specific IDs from it. I've tried adding an 'exclude' => '//id' to the $args array, but that did nothing.

1
  • 1
    A more elegant and "WordPress-y" solution to selecting posts by title strings can be found here
    – bosco
    Sep 13, 2016 at 19:43

1 Answer 1

1

On a seperate note, 'caller_get_posts' was deprecated in version 3.1 - use 'ignore_sticky_posts' with a boolean argument instead.

'exclude' is not a query argument so WordPress ignores it. Post exclusion via query is done using the key 'post__not_in' with a single post ID or array of IDs instead. However as @vancoder points out, the argument produces computationally expensive queries.

It's also unreasonable to apply both a 'post__in' argument as well as a 'post__not_in' argument as setting one implicitly describes the value of the other. A simpler and more efficient solution is available whenever you might desire to use both: just exclude post IDs from the 'post__in' argument before applying it to the query:

$included_post_ids = $wpdb->get_col( /* ... */ );
$excluded_post_ids = [ /* ids to exclude */ ];

if( !empty( $included_post_ids ) ) {
  $included_post_ids = array_diff( $included_post_ids, $excluded_post_ids );

  $args = [
    'post__in'            => $included_post_ids,
    'post_type'           => 'page',
    'post_status'         => 'publish',
    'posts_per_page'      => -1,
    'ignore_sticky_posts' => true
  ];

  $my_query = new WP_Query( $args );

  // ....
}

Information regarding query arguments can be found in the WP_Query reference on the Codex.

5
  • 1
    post__not_in makes queries expensive. In this scenario it looks like he'd be better off just grabbing all the posts and removing the exclusions in PHP.
    – vancoder
    Sep 13, 2016 at 20:30
  • You are correct, @vancoder. On further contemplation, I'm actually not aware of any use-case in which one would need to use both 'post__in' as well as 'post__not_in' in a single query as their values should be mutually exclusive. I'll update my answer to reflect as much
    – bosco
    Sep 13, 2016 at 20:49
  • While this works, it becomes a problem when I get to the final product. In the end, I want to be able to list certain pages alphabetically and separate them with a header indicating which letter begins there. Right now, I'm using one section of code similar to what I posted above to list the pages with the $first_char (in the example above it is H). This works as long as there a page under the selected letter, BUT if there is no page under that letter, it displays every page, and if the only page under that letter is being excluded, it will display it, regardless. Sep 14, 2016 at 19:39
  • Can I maybe leverage metaboxes to do this? Say I create a metabox checkbox that says display on homepage, then I can create an if statement within my list that says if the metabox is true, then it will display on the homepage? Sep 14, 2016 at 19:46
  • @TrentonMoore what you describe is a problem with the array returned from your $wpdb->get_col() call containing every page ID when it should contain none. Using a metabox as your propose might circumvent the issue, but it won't fix it. I suspect the issue may be related to ticket #28099 - where setting 'post__in' to an empty array results in everything getting returned. Try my updated solution - the important change is to use if( !empty( $included_post_ids ) ) instead of if( $included_post_ids ) so as to account for an empty array.
    – bosco
    Sep 14, 2016 at 20:00

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.