Currently reading: Black Like Me by John Howard Griffin

Use ACF posts as though you're in The Loop™

Imagine that you've got a bunch of template partials calling the usual parameter-less WP templating functions, e.g. the_title(), that automatically use the "current" post as its data source.

Everything's peachy until you need to add some related posts to the mix via ACF. You go to reuse some of those partials only to find they don't work. A post queried by ACF is never "current", so what's the best, or at least cleanest, course of action to make it so?

One option that we want to avoid is rewriting the partials. We don't want to have to pass an ID or WP_Post object to them or the functions they call as that muddies up all the existing code. The simplest path forward is reassigning that "current" post ourselves and making WP aware of it.

In order to prepare for a post to be considered "current", called the "global post" in WP parlance, the core code uses setup_postdata(). We need to leverage this but also assign our desired global post. Because if you follow the source code, you'll find that WP uses the global post variable in this process but doesn't reassign it. That's up to us. And if we don't do this part, our partials will use the wrong post data.

Luckily, it's quite simple. Since I wanted name symmetry with another function we'll be using shortly, wp_reset_postdata(), I named mine similarly: wp_setup_postdata().

if (! function_exists('wp_setup_postdata')) {
    function wp_setup_postdata($post) {
        $GLOBALS['post'] = $post;
        setup_postdata($post);
    }
}

Two lines is all the potatoes needed. Really. While setup_postdata() doesn't reassign the global post object for whatever reason, only using its data, wp_reset_postdata() does reassign it: to whatever the page's main query ended up with. We're thus only doing one thing differently from setup_postdata(): replacing the global post with our own queried from ACF.

With our new function in place, it's quick and easy (and name-symmetrical!) to spin through our queried posts in a loop and reuse our existing partials.

<section class="entry-related">
	<h2><?= __("Related {$category}") ?></h2>
	<div class="inner">
		<?php foreach ($related as $post) : ?>
			<?php wp_setup_postdata($post) ?>
			<?php get_template_part('partials/entry/card') ?>
		<?php endforeach ?>
		<?php wp_reset_postdata() ?>
	</div>
</section>

Our included card partial is now able to call those parameter-less functions like get_permalink() and the_title() without editing them to pass in any parameters as would otherwise be required. It's thus usable in both a standard WP loop and in custom loops targeting posts pulled by ACF ⸺ or even just an array of post IDs.

Leave a comment

Your email address will not be published. Required fields are marked *