Add Custom Field to WordPress API & Filter

I found myself in a strange situation where I needed to know if a page had a child. Natively the WP API lets you know if a page has a parent but not the reverse.

First I had to write a function that writes some data to a custom field if a page has a child. Then I started writing this post and realized I needed to do the reverse and now I’ve written a function that writes data to a parent when the child is created.

//sets a custom field to indicate if a page has children to save a call later
add_action( 'save_post', 'has_child_meta' );
function has_child_meta(){
    global $post;
    $post_id = $post->ID;
     if (get_post_type($post_id)==='page'){
         $args = array(
                'parent' => $post_id,                      
                'post_type' => 'page',
                'post_status' => 'publish',
                'number' => 1, //we only need one
            ); 
         $pages = get_pages($args); 
         $number = sizeof($pages);
        if ($number != 0 ) {
            add_post_meta($post_id, 'has_children', 1, true);
        } else {
            add_post_meta($post_id, 'has_children', 0, true);
        }
    }
}    
//sets a custom field to indicate if a page has children to save a call later
add_action( 'save_post', 'has_child_meta_to_parent' );
function has_child_meta_to_parent(){
    global $post;
    $parent = $post->post_parent;
        if ($parent != 0 ) {
            add_post_meta($parent, 'has_children', 1, true);
        } else {
            add_post_meta($parent, 'has_children', 0, true);
        }
    }
}    

Now that I had the data being written to the custom field, I needed to make that data visible in the WP REST API. Luckily, Jeff put up a snippet for that not too long ago.

//PUTS has_children INTO THE API
function children_get_post_meta_cb($object, $field_name, $request){
        return get_post_meta($object['id'], $field_name, true); 
}
function children_update_post_meta_cb($value, $object, $field_name){
  return update_post_meta($object['id'], $field_name, $value); 
}
add_action('rest_api_init', function(){
  register_rest_field('page', 'has_children', 
    array(
    'get_callback' => 'children_get_post_meta_cb', 
    'update_callback' => 'children_update_post_meta_cb', 
    'schema' => null
    )
  ); 
});

Now I also wanted to be able to return data based on the contents of the has_children field. This filter1 does that. Now a URL like the one below will give me pages with children but without parents.
/wp-json/wp/v2/pages?_embed&per_page=30&has_children=1&parent=0

//THIS LETS US SEARCH BY has_children variables
add_filter( 'rest_page_query', function( $args, $request ) {
    $has_children   = $request->get_param( 'has_children' );

    if ( ! empty( $has_children ) ) {
        $args['meta_query'] = array(
            array(
                'key'     => 'has_children',
                'value'   => $has_children,
                'compare' => '=',
            )
        );      
    }

    return $args;
}, 10, 2 );

This will help me make a fairly large menu structure more manageable and I figured having something like this all in one place might help someone else.


1 I usually call things ‘chunks’ of code but I’m trying to improve my vocab.