1

I'm using Version 2.0-beta15 with a custom post type that inherits from the WP_REST_Posts_Controller, but needs to query for a date based on an acf field. Yikes!

Endpoint Params

/wp-json/wp/v2/almanac_entry?per_page=3&filter[orderby]=acf_almanac_date&after=2016-12-23T00:00:00&filter[date_query[column]]=acf_almanac_date

The Response

The response returns three items but should only be two, where two are after the date listed, and the third is before the date listed. Below are the three item values of the acf_almanac_date field:

  1. 2016-12-31T00:00:00
  2. 2016-12-24T00:00:00
  3. 2016-12-17T00:00:00 (this date is before the date 2016-12-23T00:00:00 and should have been returned)

Code

Actions are registered as:

add_action( 'init', 'register_custom_post_types' );
function register_custom_post_types() {
    global $wp_post_types;

    $post_type_name = 'almanac_entry';
    if( isset( $wp_post_types[ $post_type_name ] ) ) {
        $wp_post_types[$post_type_name]->show_in_rest = true;
        $wp_post_types[$post_type_name]->rest_base = $post_type_name;
        $wp_post_types[$post_type_name]->rest_controller_class = 'WP_REST_Posts_Controller';
    }
}

add_action( 'rest_api_init', 'wp_rest_add_custom_fields' );
function wp_rest_add_custom_fields() {
    register_rest_field('almanac_entry', 'acf_almanac_date', array (
    'get_callback' => function($object, $field_name, $request) {
      return get_post_meta( $object[ 'id' ], 'almanac_date', true ) . "T00:00:00";
    },
    'update_callback' => null,
    'schema'          => null,
  ));
}

Any help is much appreciated.


Revelation 1

It occurred to me that, perhaps, the param filter[date_query[column]]=acf_almanac_date has WP-API querying for the field acf_almanac_date that is added dynamically in the wp_rest_add_custom_fields function.

Maybe I need to extend the WP_REST_Posts_Controller and override the prepare_items_query function? If true, how might I correlate that to the ACF field acf_almanac_date? Oy vey!

Tom Doe
  • 866
  • 1
  • 13
  • 22

1 Answers1

1

The WordPress REST API doesn't permit querying by post meta values out of the box because it considers them private. To enable querying by a post meta value, you'll need to:

  1. Register the query parameters to the Post controller.
  2. Transform the request arguments to query arguments passed to WP_Query.

Here's a bit of code that works for WordPress 4.7:

// Set the post type to modify.
$post_type = 'almanac_entry';

/**
 * Register `almanac_date_before` and `almanac_date_after`
 * as collection query params.
 *
 * Also support ordering by the `almanac_date` meta value.
 */
add_filter( "rest_{$post_type}_collection_params", function( $params ){
    $params['almanac_date_before'] = array(
        'description'        => __( 'Limit response to posts published before a given ISO8601 compliant date.' ),
        'type'               => 'string',
        'format'             => 'date-time',
    );
    $params['almanac_date_after'] = array(
        'description'        => __( 'Limit response to posts published after a given ISO8601 compliant date.' ),
        'type'               => 'string',
        'format'             => 'date-time',
    );
    $params['orderby']['enum'][] = 'almanac_date';
    return $params;
});

/**
 * Transform almanac_date_before` and `almanac_date_after` into a meta query.
 */
add_filter( "rest_{$post_type}_query", function( $query_args, $request ){
    if ( isset( $request['almanac_date_before'] ) ) {
        if ( ! is_array( $query_args['meta_query'] ) ) {
            $query_args['meta_query'] = array();
        }
        // We only want the 2016-11-23 from 2016-11-23T00:00:00
        $bits = explode( 'T', $request['almanac_date_before'] );
        $query_args['meta_query'][] = array(
            'key'      => 'almanac_date',
            'value'    => $bits[0],
            'compare'  => '<=',
            'type'     => 'DATE',
        );
    }
    if ( isset( $request['almanac_date_after'] ) ) {
        if ( ! is_array( $query_args['meta_query'] ) ) {
            $query_args['meta_query'] = array();
        }
        // We only want the 2016-11-23 from 2016-11-23T00:00:00
        $bits = explode( 'T', $request['almanac_date_after'] );
        $query_args['meta_query'][] = array(
            'key'      => 'almanac_date',
            'value'    => $bits[0],
            'compare'  => '>=',
            'type'     => 'DATE',
        );
    }
    return $query_args;
}, 10, 2 );
  • Thanks for the help, Daniel. Quick question though. If I have the following 5 posts with almanac_date of `2016-12-31`, `2016-12-24`, `2016-12-17`, `2016-12-10`, `2016-12-03` and hit the endpoint: `/wp-json/wp/v2/almanac_entry?almanac_date_after=2016-12-20T00:00:00`, I should only get 2 posts `2016-12-31`, `2016-12-24`, but I get all 5. I know you mentoined WP 4.7. Maybe I'm not running that (I'll have to look), or perhaps I'm missing a param? :( – Tom Doe Nov 23 '16 at 19:12
  • I'm running Version 4.6.1. Maybe I need to update? Assuming that's the issue since you explicitly call out 4.7, and it's not that I'm using the WP REST API Version 2.0-beta15 plugin. – Tom Doe Nov 23 '16 at 19:15
  • 1
    WordPress 4.7 will be released on December 6th. You can use the Beta Tester plugin to update to the release candidate now https://wordpress.org/plugins/wordpress-beta-tester/ – Daniel Bachhuber Nov 25 '16 at 16:24
  • Just updated to 4.7 today, and it worked as you outlined. Thx again for your assistance! – Tom Doe Dec 06 '16 at 19:31