Controller and ACF bug or doing something wrong?

Hi all,

Something odd is happening on my archive page for a post type. I have a post type called community-project, an archive template archive-community-project.blade.php and in app/controllers I have a controller for this ArchiveCommunityProject.php

I’m using Controller to enable the ACF fields to be automatically available to the view via:

<?php

namespace App\Controllers;

use Sober\Controller\Controller;

class ArchiveCommunityProject extends Controller
{
    protected $acf = true;
}

I have 2 Community Project posts with different custom field values for various ACF fields, however when I access these on the archive page, they are showing the same value! I’m only getting the value of the first post. The code on archive-community-project.php looks like this:

@while (have_posts()) @php(the_post())
      @php(var_dump($nomination_name))
@endwhile

nomination_name is the name of my ACF field and post A and post B definitely have different values - so why is the archive showing the same value for both?

By the way, I’m using "soberwp/controller": "^2.0.0"

Many thanks
Kevin

Further info… just using @php(var_dump(get_field('nomination_name'))) on the template works and I get the different values

For reference: The Controller ACF integration implementation.

Archive pages behave a little strangely with some WordPress functions because functions that refer to individual posts (i.e. get_the_ID()) will return a value for the first post in the archive when called outside of an explicit loop.

Controllers collect all the data at the “top level” which means they aren’t re-run inside the loop on your page (the @while (have_posts() ... bit). That means that any functions you call at the controller level that refer to single posts will return the value for the first post in the loop. I believe that’s what’s happening here. If you look at the implementation I linked to above, Controller is just calling get_fields() which assumes you’re looking to pull fields from whatever it gets when it runs get_the_ID()—so in this case, that would be the first post in your archive loop.

In other words, you seem to be assuming that this loop:

@while (have_posts()) @php(the_post())
      @php(var_dump($nomination_name))
@endwhile

will re-evaluate $nomination_name each time the loop is run. It will not: That variable is set by the controller when it passes data to the Blade, and it doesn’t change as you run that loop. That’s why when you run @php(var_dump(get_field('nomination_name'))) you get the correct values: You’re querying each post on each iteration of the loop.

There are a number of ways you could address this, depending on what you want to do and how you want to do it: You could loop through all posts in the controller and pull out/add the data you want before passing that data on to the archive. You could just add a little logic in your Blade and simply include get_field('nomination_name') in each iteration of your loop. IIRC, meta fields on posts are now directly accessible from the $post object, so you should be able to just use $post->nomination_name in your Blade loop. Or you could do something completely different, depending on your needs.

2 Likes

@alwaysblank yes you’re right, I kind of assumed that you’d get a controller for each iteration of the loop I guess in an object orientated way and that each would have it’s own values. Obviously not! That being the case I think the simplest route for this will be just to use some logic in my Blade! Thanks for the swift reply.

Kevin

1 Like

No problem! For this situation a little bit of logic in the template is probably the easiest solution, I agree.

On some other projects where I had slightly more complex needs, I started just using array_map() to modify the existing loop, and then passing that directly to the Blade as an array that contained only the things I needed:

// app/controllers/Archive.php
public function archiveItems()
{
    global $posts;
    return array_map(function ($post) {
        return [
            'title' => $post->post_title,
            'subtitle' => get_field('subtitle', $post->ID),
            'link' => get_permalink($post),
        ]
        }, $posts);
}
// resources/views/archive.blade.php
@foreach($archive_items as $item)
    <h1>{{$item['title']}}</h1>
    <p class="subtitle">{{$item['subtitle']}}</p>
    <a href="{{$item['link']}}">Read More</a>
@endforeach

Since the controller pulls from the $posts global, the array will always contain the correct posts (even when paginating).

1 Like

@alwaysblank That’s interesting! I will definitely have to have a play with that when I’m not quite so up against it time wise as I am with this project.