Search code examples
phpwordpressdateadvanced-custom-fieldswp-api

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!


Solution

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