timber icon indicating copy to clipboard operation
timber copied to clipboard

When fetching an ACF field that returns WP_Post objects, convert them to Timber\Post

Open 75th opened this issue 1 year ago • 6 comments

Problem

I'm on a project right now that fetches post reference ACF fields all over the place. These are returned as WP_Post objects (or lists thereof). This creates problems when the same template is sent the results of a Timber query in one place and ACF references in another; for instance, if a template gets a WP_Post, it has to use post.permalink, but if it gets a Timber\Post, it has to use post.link.

Solution

Timber\Post::meta() should convert any WP_Post objects it fetches into Timber\Post objects.

Alternatives

Timber\Post could implement the same interface as WP_Post alongside its own stuff, so developers don't have to care as much which kind of object they get. But I'm pretty sure some of those things have been deprecated and removed in the past. (Could be wrong.)

75th avatar Apr 30 '24 21:04 75th

I think transform_value might be what you’re looking for here: https://timber.github.io/docs/v2/integrations/advanced-custom-fields/#transform-values-to-timber/php-objects.

gchtr avatar May 01 '24 06:05 gchtr

D'oh, you're right, that's perfect. Sorry to bother!

75th avatar May 01 '24 13:05 75th

This doesn't seem to handle it well when an ACF field that would be converted is empty:

TypeError: Timber\Factory\TermFactory::build(): Argument #1 ($term) must be of type WP_Term, WP_Error given, called in /var/www/html/vendor/timber/timber/src/Factory/TermFactory.php on line 59

The WP_Error in question is simply Empty Term. Is there a good way around this?

75th avatar May 01 '24 13:05 75th

In TermFactory.php, the from_id method has this:

$wp_term = \get_term($id);

if (!$wp_term) {
    return null;
}

return $this->build($wp_term);

Looks like maybe that !$wp_term should be !$wp_term || $wp_term instanceof \WP_Error perhaps?

EDIT Or perhaps we should check empty($id) before calling get_term()

75th avatar May 01 '24 13:05 75th

Hi @75th ,

Are you willing to create a PR for handling that specific behavior?

Levdbas avatar May 07 '24 15:05 Levdbas

In TermFactory.php, the from_id method has this:

$wp_term = \get_term($id);

if (!$wp_term) {
    return null;
}

return $this->build($wp_term);

Looks like maybe that !$wp_term should be !$wp_term || $wp_term instanceof \WP_Error perhaps?

EDIT Or perhaps we should check empty($id) before calling get_term()

What I can see is that get_term only throws an error if the taxonomy does not exist. Was that your case as well @75th ?

Levdbas avatar May 10 '24 13:05 Levdbas

I have the following code in my project per the documentation:

/**
 * Set Timber to automatically transform ACF values from Wordpress objects to Timber Objects
 *
 * @return TRUE
 *
 */
add_filter( 'timber/meta/transform_value', '__return_true' );

Relationship fields are still returning instances of WP_Post, though.

jameelmoses avatar Sep 25 '24 19:09 jameelmoses

Gi @jameelmoses , can you show us how you are fetching those field values in your templates? This might be another issue

Levdbas avatar Sep 26 '24 07:09 Levdbas

Sure. For example, in a block:

$context  = Timber::context();
$context['fields'] = get_fields();

jameelmoses avatar Sep 26 '24 13:09 jameelmoses

Ah yes, I thought so. The transformation is not actively set on get_fields since no Post object is being implemented on the blocks as well. There were some discussions a while ago to create a class for blocks as well so you could use this directly, but this is not implemented. You might be able to run specific transforms as seen here on your field values.

Levdbas avatar Sep 26 '24 13:09 Levdbas