Setting up a controller for ACF fields

Hi,
I’ve been following this thread and hope it’s okay to add my problem in here.

I’ve updated my Controller version from 9.0.0-beta.4 to 2.0.0. The namespaces of my controllers were App so I’ve changed these to App\Controllers. I’m still getting the fatal error:

Fatal error: Uncaught ReflectionException: Class App\Controllers\front-page does not exist in /srv/www/mysite.co.uk/current/web/app/themes/my-theme/vendor/soberwp/controller/src/Loader.php on line 119

All I have in the controller front-page.php is:
<?php

namespace App\Controllers;

use Sober\Controller\Controller;

class FrontPage extends Controller
{
 
}

Can anyone help? I feel really clueless about this.

The filename of the file containing your front page class needs to have the same name as the class; in this case, your file should be FrontPage.php. Controller uses the PSR-4 spec to autoloader files, which determines how files should be named and organized. You can read the details here: https://www.php-fig.org/psr/psr-4/

I renamed the files to App.php and FrontPage.php, changed line 3 to be ‘namespace App\Controllers;’, and I’m still getting a ReflectionException error:

Fatal error: Uncaught ReflectionException: Class App\Controllers\App does not exist in /srv/www/.../vendor/soberwp/controller/src/Loader.php on line 119

You’ll need to provide some more information about your setup for us to try and help. What version of Controller are you using? What is the rest of your code in App.php?

Controller is ^2.0.0. App.php is just the default file that ships with Sage, it just has a siteName and title method. All I did was change line three. The folder structure is /app/controllers/App.php FrontPage.php. This is a Trellis/Bedrock/Sage site, and I really haven’t touched anything other than the composer.json file. The Call Stack says it fails during the __contruct() in the Loader.php file.

Thanks @alwaysblank. I’m getting closer to getting it working now. I’ve renamed:
app.php to App.php
front.page.php to FrontPage.php
page.php to Page.php
I’ve taken all the code I added out of the above pages so there’s nothing there to sully them.

I can now see my dashboard which is definite progress. However when I try to look at the site I get an error to do with name spacing. I’ve been debugging the site having followed A Sage 9 + ACF debugging technique

I’ve now got two errors:
Symfony\Component\Debug\Exception\FatalThrowableError Class 'Sober\Controller\Module\Debugger' not found

Class 'Sober\Controller\Module\Debugger' not found (View: /srv/www/mysite.co.uk/current/web/app/themes/mytheme/resources/views/partials/page-header.blade.php)

page-header.blade.php contains:

@php
if ($_SERVER['WP_ENV'] == 'development') {
  $controller = new \Sober\Controller\Module\Debugger(get_defined_vars(), 'Debugger');
  $fields = get_fields();
  PC::debug($controller);
  PC::debug($fields);
}
@endphp

I really am trying to work it out for myself but I really need help with this bit to help me understand it all.

It looks like the namespace for the debugger has changed to Sober\Controller\Debugger as per the source found here: https://github.com/soberwp/controller/blob/2.0.0/src/Debugger.php

Errors about “Class ‘SomeClass’ not found” or about “ReflectionError” frequently mean that the namespace or class name is wrong either where a class is being instantiated, or in the original definition of that class. If you’re struggling with namespaces, this post I wrote attempting to explain how they work may be helpful: Controller namespace issue, different behavior on localhost and development server

Thank you again @alwaysblank

I’d even looked in the debugger to check the namespace but I think I’m panicking too much to think properly at the moment. My understanding is very tentative but I’m getting there. Your help has been invaluable.

Just got to figure out the ACF bit now …

1 Like

I think I’ve solved this issue:

1 Like

Has anyone tried setting up a flexible field group? I am trying to have a controller get access to a get_sub_fields.

<?php

namespace App;

use Sober\Controller\Controller;

class FrontPage extends Controller
{
  public function content() {
    return(object) [
      'full_width_header_option' => get_sub_field('full_width_header_option'),
      'full_width_header' => get_sub_field('full_width_header')
    ];
  }
}

My view:

<h1>{{ $content->full_width_header }}</h1>

Returns - false

The field does have a value on the backend of WP. Makes me think that I am not accessing the correct part of the WP loop for Flexible Fields.

I did reply to your other post on this but this is more helpful as you have provided some code to look over. The problem here is that you are not looping over any data; there is no context for get_sub_field; how would it know which field to call it against or which row to display?

I like to follow the principal that all logic takes place in the controller and not the view and so I also try to only pass the data I’m actually to need for the view and in its final format.

Controller

public function content() {

    $data = [];

    $flexible_content = get_field('flexible_content_field');

    foreach($flexible_content as $content) {

        $this_content = (object) [
            'full_width_header_option' => $content['full_width_header_option'],
            'full_width_header' => $content['full_width_header']
        ];

        array_push($data, $this_content);
    }

    return $data;

}

This is actually more like a repeater, as with a flexible content field you’ll likely want to different logic based on the particular layout but the concept would remain similar.

You would then loop through the resulting object in your view.

Thanks for the quick reply. I have noticed that you contribute a lot to the forms and am very appreciative.

I am trying to have a controller for each flexible layout.

Example: I have views/flexible-fields/content.blade.php. I want a unique controller that manages all the custom fields for content.blade.php in app/controllers/content.php.

Here is the controller:
app/controllers/content.php

<?php

namespace FlexibleFields\Content\Controllers;

use Sober\Controller\Controller;

class Content extends Controller
{
  public function content() {

  $data = [];

  $flexible_content = get_field('flexible_fields');

  foreach($flexible_content as $content) {

      $this_content = (object) [
          'container' => $content['container'],
          'full_width_header_option' => $content['full_width_header_option'],
          'full_width_subheader_option' => $content['full_width_subheader_option'],
          'full_width_content_option' => $content['full_width_content_option'],
          'full_width_header_element' => $content['full_width_header_element'],
          'full_width_header' => $content['full_width_header'],
          'full_width_subheader' => $content['full_width_subheader'],
          'full_width_content' => $content['full_width_content']
      ];

      array_push($data, $this_content);
  }

  return $data[0];

  }
}

Here is the view
views/flexible-fields/content.blade.php

@if ($content->container)
  <div class="container">
@endif
@if ($content->full_width_header_option)
    <{{ $content->full_width_header_element }}> 
        {{ $content->full_width_header }}
    </{{ $content->full_width_header_element }}>
@endif
@if ($content->full_width_subheader_option)
    {{ $content->full_width_subheader }}
@endif
@if ($content->full_width_content_option)
    {{ $content->full_width_content }}
@endif
@if ($content->container)
    </div">
@endif

How come I cannot access the object from app/controllers/content.php on the view? When I place the logic from app/controllers/content.php to app/controllers/front-page.php the blade then can access it?

Thanks.

You seem to be making the assumption that Controllers are linked to Blades—i.e. that content from Controller.php will be automatically piped to controller.blade.php. This is not the case. Controllers are linked to the WordPress Template Hierarchy—i.e. content from the Controller in Page.php will appear at locations where WordPress would load the Single Page template. There is no way (that I’m aware of) to target individual Blades from Controllers.

The way both Controller and Sage’s own routing do this is by hooking into the values that pass through the body_class filter, and loading the appropriate content. You can determine what controller should be used for your page by looking at the value of the class attribute on your page’s <body>. For instance, take a look at this page:
image
If I wanted to target this with a controller, I could use a Controller called Single or LocationTemplateDefault, because of these classes:
image
Controller names need to conform to PSR-4 naming standards, which is why it’s LocationTemplateDefault, not location-template-default.

If your goal is to encapsulate functionality you intend to use on several controllers, you might try using traits as described in the Controller documentation. They won’t allow you to target specific Blades, but they will allow you to re-use content across Controllers.

1 Like

Holy cow, I totally forgot about the WP hierarchy when I was thinking about the design. More importantly I was able to create a solution.

What I did was create a app/controllers/partials/(this is where all my ACF controllers will live) and used: trait.

From there I passed the trait to my app controller using use.

Example:
app/controllers/partials/content.php

<?php
namespace App\Controllers\Partials;

use Sober\Controller\Controller;

trait Content
{
 public function content() {

 $data = [];

 $flexible_content = get_field('flexible_fields');

 foreach($flexible_content as $content) {

     $this_content = (object) [
         'container' => $content['container'],
         'full_width_header_option' => $content['full_width_header_option'],
         'full_width_subheader_option' => $content['full_width_subheader_option'],
         'full_width_content_option' => $content['full_width_content_option'],
         'full_width_image_option' => $content['full_width_image_option'],
         'full_width_header_element' => $content['full_width_header_element'],
         'full_width_subheader_element' => $content['full_width_subheader_element'],
         'full_width_header' => $content['full_width_header'],
         'full_width_subheader' => $content['full_width_subheader'],
         'full_width_content' => $content['full_width_content'],
         'full_width_image' => $content['full_width_image'],
         'full_width_image_size' => $content['full_width_image_size'],

     ];

     array_push($data, $this_content);
 }

 return $data[0];

 }
}

app/controller/app.php

<?php

namespace App;

use Sober\Controller\Controller;

class App extends Controller
{

use Controllers\Partials\Content;

public function siteName()
{
    return get_bloginfo('name');
}

public static function title()
{
    if (is_home()) {
        if ($home = get_option('page_for_posts', true)) {
            return get_the_title($home);
        }
        return __('Latest Posts', 'sage');
    }
    if (is_archive()) {
        return get_the_archive_title();
    }
    if (is_search()) {
        return sprintf(__('Search Results for %s', 'sage'), get_search_query());
    }
    if (is_404()) {
        return __('Not Found', 'sage');
    }
    return get_the_title();
}

}

This way I can access all of my flexible field groups when they are needed despite the page hierarchy. I would love any other suggestions!

Thanks.

2 Likes

Hello :slight_smile: I really like the idea of creating a controller for acf fields but I have one problem which I can not solve. I have group field for section, the group contains section title and content (content is WYSIWYG editor field). When I want to display value from content field which is WYSIWYG editor I see HTML tags as normal string text.

Example: <p>Lorem <strong>ipsum</strong> dolor sit amet</p>

My controller function looks like this:

public function content() {
   return (object) array( 
    'section' => get_field('section'),
   );
}

And I displayed fields in php like this:
<h3>{{$content->section['title']}}</h3>
<div>{{$content->section['text']}}</div>

My question is: How to display formatted fields from WYSIWYG editor using controllers?
The only way is to manually display the field using the_field() function, but is there any possibility to create controller for that?

1 Like

The issue is not happening at the controller level. Tell your Blade to display that string as unescaped: https://laravel.com/docs/5.6/blade#displaying-data

3 Likes

When will this be updated in the Sage theme? At the moment a fresh Sage install doesn’t come with ACF, and needs to be manually updated.

I came across to this post and finally I think that I sorted out how can I use this https://github.com/soberwp/controller#advanced-custom-fields-module , thanks to everybody that posted here.

I am starting a new project now and I don’t want to mess up everything from the beginning, so I do a recap of the steps that I followed (I hope that this can help somebody in the future)

If I made some big mistakes or something that I should avoid to do, it would be great if you can let me know and save my life :slight_smile:

  • update composer to 2.0.0 (Setting up a controller for ACF fields)
  • change namespace App; to namespace App\Controllers; in /controllers/app.php and /controllers/front-page.php
  • rename app.php to App.php and front-page.php to FrontPage.php

are these steps correct or I should avoid to do something for the best practice?

thanks

1 Like

This is exactly the setup we are using and it works great.

The one thing that isn’t working is ACF options page fields. For that we need to to manually define them using get_option().

I’d really really love to be able to work with flexible content the correct way using a controller, views and partials. I’ve attempted to get flexible content working in this way by lots of trial and error and research but just can’t quite figure it out.

I’m asking if anyone here would be willing to break down the following code example into how you would use this flexible content to achieve the results. I’m sure this would be super helpful to lots of others as well. I’ve been unable to find anyone really breaking down the approach in a way that’s easy to understand.

This code uses two flexible content sections. One full width-one column section and a one full width-two column section. That’s pretty obvious and not really the point but I’m hoping someone will be willing to break this down.

@if( have_rows('flexible_content') )
    @while ( have_rows('flexible_content') ) @php the_row() @endphp
        @if( get_row_layout() == '1_up_full_width' )
            @php 
            $heading = get_sub_field('heading');
            $subheading = get_sub_field('subheading');
            $content = get_sub_field('content');
            $btntxt = get_sub_field('button_text');
            $btnurl = get_sub_field('button_url');
            $bg = get_sub_field('background');
            $bgc = get_sub_field('background_color');
            $bgi = get_sub_field('background_image');
            $fgc = get_sub_field('foreground_color');
            $li = get_sub_field('logo_icon');
            @endphp

            <div class="mt-0 py-0 w-screen h-full bg-cover bg-bottom" style="@if ( $bg == "color" ) background: {{ $bgc }} @else background: url( '{{ $bgi }}' ) @endif">
                @if ( $bg == "image" )
                <div class="h-full bg-ae-green-bg">
                @endif
                    <div class="flex"> 
                        <div class="w-full m-8 md:m-16 p-8 md:p-16 border text-center @if ( $fgc == "dark") border-primary text-primary @else border-white text-white @endif">
                            @if ( $heading )<h2 class="uppercase text-3xl mb-6">{{ $heading }}</h2>@endif
                            @if ( $subheading )<p class="text-md md:text-2xl mb-8 lg:mx-64">{{ $subheading }}</p>@endif
                            @if ( $content )<p class="text-md md:text-2xl mb-8 lg:mx-64">{{ $content }}</p>@endif
                            @if ( $btntxt )
                                <a href="{{ $btnurl }}">
                                <button class="py-3 px-8 mb-8 border bg-transparent uppercase text-lg @if ( $fgc == "dark") border-primary text-primary @else border-white text-white @endif">{{ $btntxt }}</button> 
                                </a>
                            @endif
                            @if ( $li )
                            <div class="py-2 px-4"><img src="{{ $li ['url'] }}" alt="{{ $li ['alt'] }}" class="h-16"></div>
                            @endif

                        </div>
                    </div>
                @if ( $bg == "image" )
                </div>
                @endif
            </div>

        @elseif( get_row_layout() == '2_up_full_width' ) 
            @php 
            $heading = get_sub_field('heading');
            $subheading = get_sub_field('subheading');
            $content = get_sub_field('content');
            $btntxt = get_sub_field('button_text');
            $btnurl = get_sub_field('button_url');
            $usethumb = get_sub_field('add_thumbnail_image');
            $thumb = get_sub_field('thumbnail');
            $heading_2 = get_sub_field('heading_2');
            $subheading_2 = get_sub_field('subheading_2');
            $content_2 = get_sub_field('content_2');
            $btntxt_2 = get_sub_field('button_text_2');
            $btnurl_2 = get_sub_field('button_url_2');
            $usethumb_2 = get_sub_field('add_thumbnail_image_2');
            $thumb_2 = get_sub_field('thumbnail_2');
            @endphp

            <div class="lg:flex p-8 md:p-16 text-center text-grey">
                <div class="lg:flex w-full lg:ml-8 px-4 md:px-8 py-8 lg:py-16 border border-solid">
                    @if ( $thumb )
                    <div class="w-full lg:w-1/2">
                        <img src="{{ $thumb ['url'] }}" alt="{{ $thumb ['alt'] }}" class="px-8 pb-8 lg:pb-0">
                    </div>
                    @endif
                    <div class="w-full @if ( $thumb ) lg:w-1/2 @endif">
                        @if ( $heading )<h2 class="uppercase text-xl md:text-3xl mb-6">{{ $heading }}</h2>@endif
                        @if ( $subheading )<p class="text-md md:text-2xl mb-8">{{ $subheading }}</p>@endif
                        @if ( $content )<p class="text-md md:text-2xl mb-8">{{ $content }}</p>@endif
                        @if ( $btntxt )
                            <a href="{{ $btnurl }}">
                            <button class="bg-white py-3 px-8 border border-grey-light hover:border-transparent uppercase text-lg">{{ $btntxt }}</button>
                            </a>
                        @endif
                    </div>
                    </div>
                    <div class="lg:flex w-full lg:ml-8 px-4 md:px-8 py-8 lg:py-16 border border-solid">
                    @if ( $thumb_2 )
                    <div class="w-full @if ( $thumb_2 ) lg:w-1/2 @endif">
                        <img src="{{ $thumb_2 ['url'] }}" alt="{{ $thumb_2 ['alt'] }}" class="px-8 pb-8 lg:pb-0">
                    </div>
                    @endif
                    <div class="w-full lg:w-1/2">
                        @if ( $heading_2 )<h2 class="uppercase text-xl md:text-3xl mb-6">{{ $heading_2 }}</h2>@endif
                        @if ( $subheading_2 )<p class="text-md md:text-2xl mb-8">{{ $subheading_2 }}</p>@endif
                        @if ( $content_2 )<p class="text-md md:text-2xl mb-8">{{ $content_2 }}</p>@endif
                        @if ( $btntxt_2 )
                        <a href="{{ $btnurl_2 }}">
                        <button class="bg-white py-3 px-8 border border-grey-light hover:border-transparent uppercase text-lg">{{ $btntxt_2 }}</button>
                        </a>
                        @endif
                    </div>
                </div>
            </div>

        @endif

    @endwhile

@else
    <p>Admin, you need to add some content blocks!</p>
@endif