SoberWP Controller: ACF loops and static functions

Using Sage9 beta 4 and ACF.

Based on the helpful info from @withjacoby in the post SoberWP Controller: Access $post objects data from archive page,

I know when I have a blade template with something like:

@while( have_rows('repeater') ) @php( the_row() )
    @if ( SampleController::controllerFunctionWithSubFields() )
        {{ get_sub_field('content') }}

The function in the controller should be static:

  public static function controllerFunctionWithSubFields()
    return get_sub_field('criteria1') && get_sub_field('criteria2');

But wanted to understand why: If a function is not static in a controller, is it only “run” when the controller is created? Or is something else going on?

Public functions are converted to variables, whereas public static functions are exposed to the view as functions, useful for loops.

Let me know if that helps answer the question?

1 Like

Thx for the message. Sorry if I’m being a bit slow but I wanted to understand.

When are the controller public functions converted into variables? Before any of the Blade code is run?

@withjacoby T’would be nice if all data fell under one blanket function like _data() and you did all your variable declarations there. Having to do individual (non-static) functions just to declare variables is tedious. It is also confusing when you start mixing them with static functions.

I would like to see something like this within a controller:

public function _data() {
    $item = [
       // get some data from external class
    $title = $this->transform( get_the title() );
    $another_var = function() {
        // do some more stuff here

  return compact( 'item', 'title', 'another_var');

Now you have a very clear, simple, and easier to read function for your variables. Using functions just to declare variables is counter-intuitive. A lot of people are getting hung up on this (I know I did).

EDIT: I put in a request to add this functionality #52 Sober Controller

I see your point but in my experience, this would end up into being a monster method which holds far too much logic in one place. I think this boils down to a personal preference to be honest, everyone has a different mindset when developing but I usually prefer to have many small and testable functions more than just one gigantic one.
Either way I agree it would be super nice to have some extra goodies from controller to be able to manipulate/organise data in different ways. I really like your idea of a transform method that could be used to standardise output! :slight_smile:

I like the current method because I can set simple variables like $featuredimagesize in multiple contexts for different post types and use the same template! Super convenient.

1 Like

@Nicolo_Sacchi Transformers come in pretty handy in some cases, such as @MWDelaney with $featuredImageSize.

On the flip-side of smaller methods, you soon have a monster controller. Might be a case for using traits here, just to keep common sets of values together. Won’t reduce the size of the controller, but will clean it up a bit.

Personally, I wrote my own framework so that I can use models and use the whole MVC stack while developing. This way I delegate most of the heavy stuff to the models, organise what I want to expose in a few methods in my controller and keep my templates nice and clean :slight_smile: Having models makes life so much easier!

Ah. I see. Makes sense.

I been trying to use View Composers in lieu of models. They have event listeners that I can see registered in the service container, but they are not firing. Not sure what to make of that.

Premise is simple. Create a composer with the data you want to pass.


namespace App\Composers;

use Illuminate\View\View;

class AcfObjectsComposer {

  public function compose(View $view)
     $view->with('acfObjects', get_field_objects());


This will create an $acfObjects variable for a view to use. As you can see, it uses ACF’s get_field_objects() function. That variable can now be used for loops normally.

Specifying a view or views for that composer via a service provider


namespace App\Providers;

use App\Providers\AbstractProvider;

class ViewComposerProvider extends AbstractProvider {

  public function register()
     \App\sage('view')->composer(['content.single', 'single'], '\App\Composers\AcfObjectComposer');

I bind this provider to the Sage Container, and as I said, it actually creates the event listeners, but they don’t fire – or something else is going on.