Filter rows from the wp-admin posts listings
John Mc By - John Mc

Filter rows from the wp-admin posts listings

To filter rows from the WordPress Admin posts listings for posts or CPTs, use the parse_query filter to alter the query

You may have a specific need to filter out listings in the admin area for posts, or more likely for custom posts types.

For instance, in a recent project, the client had a CPT for real estate for sale / to let. They wanted to add in the ability to automatically import listings from an MLS in real-time, ie, as the user performed a search the lookup would trigger an API call to the MLS and import the listings as part of their regular property CPT.

The problems they wanted to overcome were:

  • The listings needed to be part of their CPT so that pagination, filtering etc would work, but they needed to expire after 12 hours so that fresh data would be retrieved after that time
  • Because these imported listings would expire after 12 hours, they wanted to hide these posts from the admin dashboard so that an administrator would mistakenly think that they could edit those listings (they could, but obviously 12 hours later their edits would be lost).

To manage the expiration we added two meta fields when creating WordPress posts programmatically from the API. We added a source meta field as well as an expiry meta field.

The source meta field contained the word “mls-api” and the expiry meta fields contained the current date + 12 hours so that after that time posts would not be displayed (and would be deleted).

Filtering the CPTs out of the admin listings in WordPress

To do the filtering out of the posts from the WordPress admin listing, we used the parse_query filter

    add_filter( 'parse_query', 'filterOutMlsProperties', 10, 1 );
    function filterOutMlsProperties( $query ) {
        if( is_admin() AND $query->query['post_type'] == 'properties' ) {           
            $qv = &$query->query_vars;
            $qv['meta_query'] = array(
                'relation' => 'OR',
                'key' => 'source',
                'value' => 'idx-api',
                'compare' => '!='
                    'key' => 'source',
                    'value' => 'idx-api',
                    'compare' => 'NOT EXISTS'

Add use the add_filter call to add a filter for parse_query and we give it a function call back. We’ve called this function filterOutMlsProperties. This function must take an argument which is the current query before WordPress sends it to the database.

We check first that this is an administrator who’s logged in, and we check that the post_type is our CPT, properties.

Next, we add a meta_query to the current query with a key of source, which is our source meta key we added to the mls-api data. We check that source is not equal to mls-api. This will effectively search for the properties which are NOT from the mls-api. Our meta_query array uses an OR relation and then two sub arrays. One to check that if there is a source key present its not mls-api, and a second one to match rows where the source meta_key is not present, ie, “regular” property listings.

Fixing the Row Counts in the CPT listings page

So far so good. Our listing page now filters out rows imported from the API, but we have one minor problem. The row counts above the listing table shows a count of all rows, even ones not present in this table. That’s just confusing and not extremely professional.

Lets take care of that real quickly!

We call on another WordPress filter called wp_count_posts.

This filter fires before the counts are printed on the screen so we’re able to modify the array of counts.

    add_filter('wp_count_posts', 'fixFilteredRowCount', 10, 3);
    function fixFilteredRowCount( $counts, $type, $perm ) 
        global $wpdb;
        if( is_admin() AND $type == 'listings' ) {

            $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = 'properties' AND ID NOT IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'source' and meta_value = 'mls-api' ) GROUP BY post_status";
            $results = $wpdb->get_results( $query, ARRAY_A);
            $counts = array_fill_keys( get_post_stati(), 0 );
            foreach ($results as $row) { 
                $counts[ $row['post_status'] ] = $row['num_posts']; 

        return (object) $counts;

We perform a new query in the row count filtering function where we look up the counts, per status, for properties CPTs where they do NOT have the mls-api source meta data, and we repopulate the count array with the updated counts.

Now our properties CPT admin listing page has the API data filtered out and the counts above the table reflect the correct numbers too.



Leave a Comment

Your email address will not be published. Required fields are marked *