airesvsg/acf-to-rest-api

How to get ACF post object field with ACF fields

josephdonnelly opened this issue ยท 34 comments

Hey, maybe I am missing something, but I am having trouble retrieving custom post ACF data within a post object field.

I have a custom post type call Rentals where I have set up ACF fields.

I can retrieve all of the acf data by getting /wp-json/wp/v2/rentals/{id}

When I create a page with an ACF Post Object field where I select a specific rental, I don't see any of the ACF data in the response. When I get /wp-json/wp/v2/pages/{id} I get this response:

...page data
"acf": {
    "rental": {
      "ID": 191,
      "post_author": "1",
      "post_date": "2017-02-28 18:24:35",
      "post_date_gmt": "2017-02-28 18:24:35",
      "post_content": "",
      "post_title": "Rental 1",
      "post_excerpt": "",
      "post_status": "publish",
      "comment_status": "closed",
      "ping_status": "closed",
      "post_password": "",
      "post_name": "rental-1",
      "to_ping": "",
      "pinged": "",
      "post_modified": "2017-02-28 18:24:35",
      "post_modified_gmt": "2017-02-28 18:24:35",
      "post_content_filtered": "",
      "post_parent": 0,
      "guid": "http://wordpress.site.local/?post_type=rentals&p=191",
      "menu_order": 0,
      "post_type": "rentals",
      "post_mime_type": "",
      "comment_count": "0",
      "filter": "raw"
    }
  },

How do I set it up so the ACF data is included within that Post Object field?

Hi @josephdonnelly,
Thanks for using my plugin.
Please, see this issue #9 (comment)
Thanks


Edit:

Get ACF Fields Recursively:
https://github.com/airesvsg/acf-to-rest-api-recursive

Thanks for the extremely quick response!

I used that snippet, and it works great:

add_filter( 'acf/rest_api/page/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }
    if ( isset($data['acf']) && isset( $data['acf']['rental'] ) ) {
        $data['acf']['rental'] = get_fields($data['acf']['rental']->ID);
    }
    return $data;
}, 10, 3);

However, is there a way to set it up so that any time a Rental posts is being used it displays the acf data? I am going to be using that Rental post type within several custom fields (repeaters/flexcontent/etc) and it will get tedius having to check for those fields manually.

Thanks again for the quick response

Also worth noting, with the above snippet I was receiving this error using version 3 of the plugin:
Warning: Missing argument 3 for {closure}()
for this line of code:
add_filter( 'acf/rest_api/page/get_fields', function( $data, $request, $response ) {

Error goes away when switching back to v2

At this time manually, but you can do a recursive function to check.

Yes, in third version the argument $response not exists.

add_filter( 'acf/rest_api/page/get_fields', function( $data, $request ) {
...
}, 10, 2 );

Arguments of add_filter:

add_filter( $tag, $function_to_add, $priority, $accepted_args );

The $accepted_args argument in the v3 accepts values greater than 0 and less than 3.

Thanks

Thanks again. I got it working with a recursive function

add_filter( 'acf/rest_api/page/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }

    array_walk_recursive( $data, 'deepIncludeRentalFields' );
    
    return $data;
}, 10, 3);

function deepIncludeRentalFields( &$item, $key ) {
    if ( $key == 'rental' ) {
        $item = get_fields( $item->ID );
    }
}

here is a cleaner way for anyone else who finds this issue:

add_filter( 'acf/rest_api/page/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }

    array_walk_recursive( $data, 'deepIncludeACFFields', array( 'rentals' ) );

    return $data;
}, 10, 3 );


function deepIncludeACFFields( &$item, $key, $postTypes ) {
    if ( isset( $item->post_type ) && in_array( $item->post_type, $postTypes ) ) {
        $item = get_fields( $item->ID );
    }
}

@josephdonnelly I am dealing with this same thing... I think. Can you help provide a better explanation of how you fixed this?

I have a custom post type I am pulling info for via the API. On each post there is a Relationship field where you can select other posts from the same post type. The JSON shows the objects from the relationship but NOT any of their custom fields. I want to pull field data from the relationship objects. The snippet above you listed doesn't appear to be working for me. Any help would be awesome!

Thanks!

Hi @travis-zookacreative,
Thanks for using my plugin, bellow I wrote an example most generic.

V3

add_filter( 'acf/rest_api/{type}/get_fields', function( $data ) {
	if ( ! empty( $data ) ) {
		array_walk_recursive( $data, 'get_fields_recursive' );
	}

	return $data;
} );

V2

add_filter( 'acf/rest_api/{type}/get_fields', function( $data, $request, $response ) {
	if ( $response instanceof WP_REST_Response ) {
		$data = $response->get_data();
	}

	if ( ! empty( $data ) ) {
		array_walk_recursive( $data, 'get_fields_recursive' );
	}

	return $data;
}, 10, 3 );

Function to use for both versions:

function get_fields_recursive( $item ) {
	if ( is_object( $item ) ) {
		$item->acf = array();
		if ( $fields = get_fields( $item ) ) {
			$item->acf = $fields;					
			array_walk_recursive( $item->acf, 'get_fields_recursive' );
		}
	}
}

hey @airesvsg thanks for sharing! Unfortunately that throws an error and doesn't load. Any ideas? I am using V2 at the moment. My custom post type is "product" so my first line looks like the following.

add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {

Actually looks like V3 failed as well. Am I missing something? Im adding these snippets to my functions file. Here is a screen shot of the result when routing directly to the relationship field.

screen shot 2017-03-14 at 5 37 28 pm

@travis-zookacreative , Try this one out. It will run the same recursive lookup if the item is an array. I just ran into this and solved it this way

add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }

    array_walk_recursive($data, 'deepIncludeACFFields', array('product'));

    return $data;
}, 10, 3);

function deepIncludeACFFields(&$item, $key, $postTypes) {

    if( isset($item->post_type) && in_array($item->post_type, $postTypes) ) {
        $item = get_fields($item->ID);
    }
    // Add this portion here to look up arrays within the array
    if(is_array($item)) {
        array_walk_recursive($item, 'deepIncludeACFFields', $postTypes);
    }
}

@josephdonnelly I saw you posted something similar earlier up the chain. I've messed around with both but they are throwing a fatal error of "Too few arguments to function {closure}()". I also saw you had this same issue, how did you resolve?

Can you post the full code you're using?

add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }
    array_walk_recursive($data, 'deepIncludeACFFields', array('product'));
    return $data;
}, 10, 3);

function deepIncludeACFFields(&$item, $key, $postTypes) {

    if( isset($item->post_type) && in_array($item->post_type, $postTypes) ) {
        $item = get_fields($item->ID);
    }
    // Add this portion here to look up arrays within the array
    if(is_array($item)) {
        array_walk_recursive($item, 'deepIncludeACFFields', $postTypes);
    }
}

I changed my local server from PHP 7 to 5.6 and that got rid of the too few arguments error. Now instead it just times out hahaha

Ah, so the timeout likely is because you're going in an infinite loop. You get acf fields for a product, which gets acf fields for a product, which gets acf fields for a product... and so on. You will have to add some checks into the deepincludeACFFields to prevent the infinite loop. I'm afraid I don't know enough about PHP or your specific situation to help. Best of luck though,

ahhh that would make sense! Thanks man! Much Appreciated!

Gents, got this working! Great news! However do you know of any ways to ALSO get WP data from the relationship posts? I would also like to grab the slug, and featured img, etc. Not just the ACF fields. Code I got to work is below.

add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }
    array_walk_recursive($data, 'deepIncludeACFFields', array('product'));

    return $data;
}, 10, 3);

function deepIncludeACFFields(&$item, $key, $postTypes, $level=0, $post) {
	if( isset($item->post_type) && in_array($item->post_type, $postTypes) ) {
		$item = get_fields($item->ID);
	}
}

Great information.
You should consider adding this reply code:
#109 (comment)
To the Readme.
Another suggestion is to use {custom_post_type} instead of {type} in your route examples. Makes understanding easier. At first i was not sure what type was referring to.

Hi,

Fixed - see end of comment

I've been able to use one of the code snippets above:

add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }
    array_walk_recursive($data, 'deepIncludeACFFields', array('product'));

    return $data;
}, 10, 3);

function deepIncludeACFFields(&$item, $key, $postTypes, $level=0, $post) {
	if( isset($item->post_type) && in_array($item->post_type, $postTypes) ) {
		$item = get_fields($item->ID);
	}
}

To successfully get the ACF fields from a page or post-type. However, I am struggling to get the fields from a custom options page. I've tried replacing:

add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {

With

add_filter( 'acf/rest_api/options/get_fields', function( $data, $request, $response ) {

but I haven't had any success yet.

How should I go about getting relational fields from options pages?

The structure that I am working with looks like this:

{
  "acf": {
    "locations": [
      {
        "latitude": "55.9767554",
        "longitdue": "-3.1734137",
        ...
      }
    ],
    "explore_section": [
      {
        "navigation_title": "Testing 123",
        "explore_section_hero": false,
        ...
        "product_testing": {  //I'm looking to get the fields from product testing
          "ID": 63,
          "post_author": "2",
          "post_date": "2017-05-11 05:31:56",
          "post_date_gmt": "2017-05-11 05:31:56",
         ...
        }
      },
      {
        "navigation_title": "Another",
        ...
      }
    ]
  }
}

I'm looking to get the ACF fields from product_testing.

Thanks!

Fix

  • I upgraded to V3
  • Changed the function to:
add_filter( 'acf/rest_api/option/get_fields', function( $data ) {
	if ( ! empty( $data ) ) {
		array_walk_recursive( $data, 'deepIncludeACFFields', array('product') );
	}

	return $data;
} );

Also turns out that I was saying options instead of option - Doh!

hi everyone. for the life of me i cant seem to get this to work!

I have tried both post object as well as post relationship. none of these snippets are returning acf data in the returned post object...

im using the default wp-api. any help is much appreciated! :D

Hi @airesvsg - I am using the above snippets (specifically @travis-zookacreative to return acf post objects in a repeater that is in a custom post type.

the endpoint URL looks like this:
http://138.197.53.59/wp-json/wp/v2/directors-api?filter[name]=oscar-boyson

When I apply deepIncludeACFFields function & filter I get the acf data but i loose the normal post data, I need to be able to pull the title of the post and the slug for a permalink. After reading through the various tickets and trying some different things still cant seem to get both - so is this possible or is it one or the other - and I should include things like title and slug as custom fields.

Thanks!

Hey @airesvsg. Ok, so its been a while since I worked on this project but here is my code at the moment. I'll do my best to remember what it's doing.

So essentially I decided it best to get just the acf fields I want if they exist. So for each custom type that has the repeater field product_scents return the post and the custom fields of it. I can't fully remember all of the details to this as I only did a little and a coworker did the rest.

// Get ACF fields and their related objects
add_filter( 'acf/rest_api/product/get_fields', function( $data, $request, $response ) {
    if ( $response instanceof WP_REST_Response ) {
        $data = $response->get_data();
    }

	foreach( $data['acf']['product_scents'] as $_key => $_product ){
	//	Add Link
		$data['acf']['product_scents'][$_key]->link = get_permalink($_product);
	//	Add Category
		$data['acf']['product_scents'][$_key]->category = get_the_terms($_product->ID, 'product_cat' )[1];
	//	Add All Other Custom Fields
		$_custom_fields	=	get_post_custom($_product->ID);
		foreach( $_custom_fields as $_field => $_value ){
			if( preg_match('/^(_|faqs_)/',$_field) )	continue;
			$data['acf']['product_scents'][$_key]->{$_field}	=	$_value;
		}//foreach
	}//foreach

	foreach( $data['acf']['related_products'] as $_key => $_product ){
	//	Add Link
		$data['acf']['related_products'][$_key]->link = get_permalink($_product);
		$data['acf']['related_products'][$_key]->thumbnail = get_the_post_thumbnail_url($_product);
	//	Add Category
		$data['acf']['related_products'][$_key]->category = get_the_terms($_product->ID, 'product_cat' )[1];
	//	Add All Other Custom Fields
		$_custom_fields	=	get_post_custom($_product->ID);
		foreach( $_custom_fields as $_field => $_value ){
			if( preg_match('/^(_|faqs_)/',$_field) )	continue;
			$data['acf']['related_products'][$_key]->{$_field}	=	$_value;
		}//foreach
	}//foreach

	return $data;

}, 10, 3);

Setting the $item to the full ACF field overwrites all the default post object data. I instead looped through the fields and append them to the item in order to presever the default data:

function deepIncludeACFFields( &$item, $key, $postTypes, $level=0, $post ) {
    if ( isset( $item->post_type ) && in_array( $item->post_type, $postTypes ) ) {
         $acf_fields = get_fields( $item->ID );
         foreach( $acf_fields as $key => $value ) {
             $item->{$key} = $value;
        }
    }
}

Instead of defining it for every possible post type. I added a $types array so it loops through the types and also uses it for the array_walk_recursive() function.

/**
 * ACF Data in Post Object Response
 */

$types = ['post', 'page', 'product']

foreach ($types as $type) {
  add_filter( 'acf/rest_api/'.$type.'/get_fields', function( $data, $response ) use ($types) {

    if ( $response instanceof WP_REST_Response ) {
      $data = $response->get_data();
    }

    array_walk_recursive( $data, 'deepIncludeACFFields', $types );

    return $data;

  }, 10, 3 );
}

function deepIncludeACFFields( &$item, $key, $postTypes ) {
  if ( isset( $item->post_type ) && in_array( $item->post_type, $postTypes ) ) {
    $item->acf = get_fields( $item->ID );
  }
}

I like @stevendaoud's solution for adding the fields to the top level object. I opted to follow the plugin's format of placing all the fields in the acf key. Both adding to the response as opposed to replacing it.

Also, I was getting an error with function( $data, $request, $response ) so I removed $request and it appears to be working. Not sure why mine was giving me the error, guess it's possible it's been deprecated.

function( $data, $request, $response ) breaks in PHP 7.x, I haven't done too much digging as to why. If anyone has an answer that'd be great.

Still works in PHP 5.x

can you provide simple instructions for where to add this filter? what file?

@airesvsg thank you! works like a charm

Hi @airesvsg

I'm trying to implement on the site that I'm developing. I've installed your recursive plugin but the response still doesn't have the acf fields included. I'm on v3 of the acf-to-rest-api plugin.
Please see response here:-

https://nationalgridpurpose.com/wp-json/wp/v2/actionbox/10084

Any advice please?

@mattprudente Did you figure this out? I've installed the plugin but still no acf fields.

Did anyone else figure this out, for adding fields to the base post object as detailed above?

not just the ACF fields, but things like Categories or the Featured Image.

How could i enrich this

"acf": {
"audio_file_url": false,
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ",
"video_file_url": "http://techslides.com/demos/sample-videos/small.mp4",
"attached_resources": [
{
"attachable": [
{
"ID": 115,
"post_author": "1",
"post_date": "2018-10-16 18:32:38",
"post_date_gmt": "2018-10-16 18:32:38",
"post_content": "",
"post_title": "fghfghfgh",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "fghfghfgh",
"to_ping": "",
"pinged": "",
"post_modified": "2018-10-16 18:32:38",
"post_modified_gmt": "2018-10-16 18:32:38",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://104.248.160.79/?post_type=resources&p=115",
"menu_order": 0,
"post_type": "resources",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
}
]
}

Hello
I'm trying to make a post and it doesn't give me a mistake, please help
image

this plugins get api one load is properly but when i edit act content so this update content is not show in my react project and my costume api is working fine