1

I have a custom ACF field set on all posts, called event_date, I would like to grab all posts within a given category, but have posts that have event_date defined put first in DESC order of event_date. The args I'm using for query_posts are as follows:

$args = array(
    'posts_per_page' => -1,
    'cat' => $category_id,
    'meta_query' => array(
        array(
            'key' => 'event_date',
            'value' => date("Ymd", time()),
            'compare' => $type == 'upcoming' ? '>=' : '<'
        )
    ),
    'orderby' => 'meta_value',
    'order' => 'DESC',
    'meta_key' => 'event_date'
);

But the problem I'm facing is that the ACF field was created for post types, after all posts have been created, so many of these posts do not have event_date even defined for the meta_key (which omits these posts from the query automatically when using query_posts($args). There are too many posts to go through all posts and save them all manually, would take forever.

Basically, if the meta_key for any post does not have event_date than I need to use the date of the actual post itself (when it was published) to order by. I need all posts with event_date shown first in DESC order of event_date, than all posts that don't have event_date defined, need to be shown after that using the published date of the post in DESC order. How can I do this? Preferably, all in 1 query_posts call?

Thanks :)

  • Never use query_posts, it breaks the main query objects and a huge amount of other stuff that relies on the main query object – Pieter Goosen Feb 5 '16 at 6:23
  • @PieterGoosen what should I use to accomplish this? $wpdb->get_results("SELECT STATEMENT, etc. etc.", ARRAY_A); – Solomon Closson Feb 5 '16 at 6:27
  • Why not run a script which sets the custom field for you, and then after that, you can do one WP_Query and query all posts normally (if this is not the main query, in which case you should be using pre_get_posts) – Pieter Goosen Feb 5 '16 at 6:30
  • This is not using the main query object. This is being used on a regular page to pull in all posts associated with a given category, order by custom field, than if custom field does not exist, order by published date. Not sure if using pre_get_posts will help here, as it might interfere with main wp query (tho not entirely sure of this). Can you please provide example? – Solomon Closson Feb 5 '16 at 6:33
4

You can get both with an OR meta_query that also checks if the key does not exist:

'meta_query' => array(
    'relation' => 'OR',
    array(
        'key' => 'event_date',
        'compare' => 'NOT EXISTS',
    ),
    array(
        'key' => 'event_date',
        'value' => date("Ymd", time()),
        'compare' => '>',
    ),
),
'orderby' => array(
    'meta_value_num' => 'DESC',
    'date' => 'ASC',
),
  • Yes, I was looking at this, but how would I use the following 'orderby' => 'meta_value', 'order' => 'DESC', 'meta_key' => 'event_date' or is this not needed anymore? – Solomon Closson Feb 5 '16 at 7:08
  • You still need those for the order. Note that you can also set multiple orderby fields, see the examples on WP_Query. – Milo Feb 5 '16 at 7:13
  • Yes, I see, however, 'meta_key' => 'event_date', meta_key must be present if orderby has meta_value in it, and meta_key can not be an array. How to do this? – Solomon Closson Feb 5 '16 at 7:23
  • It works without meta_key, just set orderby. see edit above for parameters I tested with. – Milo Feb 5 '16 at 7:36
  • 1
    *some assembly required. – Milo Feb 5 '16 at 8:08
0

The most probable route I will take with this is to run a script that would add the desired custom field to the posts which does not have the custom field assigned.

You can try the following: (NOTE: This is untested)

add_action( 'wp', function ()
{
    $args = [
        'post_type'        => 'post', // Set according to needs
        'posts_per_page'   => -1, // Set to execute smaller chucks per page load if necessary
        'suppress_filters' => true,
        'fields'           => 'ids',
        'meta_query'       => [
            [
                'key'      => 'event_date',
                'compare'  => 'NOT EXISTS'
            ]
        ]
    ];
    $q = get_posts( $args );
    foreach ( $q as $post_id ) {
        add_post_meta( 
            $post_id, // Post ID
            'event_date', // Custom field name
            get_the_date( 'Ymd', $post_id )
        );  
     }
});

You can run this by simply loading any page after which you can remove it. If you set smaller chuncks, like posts_per_page=100, you will refresh the page until all posts are done. Note that this is quite an expensive resource intensve script which might lead to a time out fatal error, so if you have a huge amount of posts you can run smaller amount of posts per page load.

After this you can run your query as normal as per question. Just a tip though, never ever use query_posts, use WP_Query to run your custom query (or use get_posts alternatively)

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.