Tom Doe Tom Doe - 14 days ago 5
PHP Question

WP-API Get Posts After a Specific Date

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!

Answer

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 );