ACF variables in blade template

Hey Guys!

What’s the best place to keep my Advanced Custom Fields variables?
I know it’s bad practice to have anything like this in a blade template… so where could I store them and call them into the template?

Cheers!

<?php
    // variables
    $subheading = get_field('subheading');
    $video = get_field('video_link');
    $buttons = have_rows('buttons');
    $backgroundOverlay = get_field('background_image_overlay');
?>

<section class="introduction <?php if( $backgroundOverlay ): ?>introduction--with-overlay<?php endif; ?>" 
         style="background-image: url('<?php the_post_thumbnail_url(); ?>')">
    <div class="introduction__content">
        <div class="container container--fixed-lrg">
            <h1>{!! App\title() !!}</h1>
            <?php
                if( $subheading ):
            ?>
                <h2><?= $subheading ?></h2>
            <?php
                endif;
                if( $buttons ): 
            ?>

                <div class="btn-group">
                    <?php 
                        while( have_rows('buttons') ): the_row(); 

                        $text = get_sub_field('text');
                        $colour = get_sub_field('colour');
                        $link = get_sub_field('link');

                    ?>
                        <a href="<?= $link ?>" class="btn btn--lrg <?= $colour ?>">
                            <span><?= $text ?></span>
                        </a>
                    <?php 
                        endwhile; 
                    ?>
                </div>

            <?php 
                endif; 
                if( $video ):
            ?>
                <div class="video-container">
                    <div class="embed-container">
                        <iframe class="videoIframe js-videoIframe" 
                                src="" 
                                frameborder="0" 
                                allowTransparency="true" 
                                allowfullscreen 
                                data-src="{!! App\videoThumbnail($src) !!}">
                        </iframe>
                        <button id="playButton" class="videoPoster js-videoPoster" 
                                style="background-image:url({!! App\videoThumbnail($src)[1] !!}">
                                Play video
                        </button>
                    </div>
                </div>
            <?php
                endif;
            ?>
        </div>
    </div>
</section>

Create a controller for them.

Have a look here for an explanation & setup: GitHub - soberwp/controller: Composer package to enable a controller when using Blade with Sage 9

You might browse this thread as well, if you haven’t already.
https://discourse.roots.io/t/best-practice-resources-for-blade/8341/11?u=smutek

I linked to the post where I got all giddy when it hit me how awesome this setup is.

Hopefully this helps some, I haven’t been on the board lately and others may be able to provide some better examples.

8 Likes

Awesome, I was wondering if I should put them in the helpers.php or a controller, those links really help explain how it works. Thanks heaps @smutek :slight_smile:

1 Like

Sorry I just have one more question!

So say i make a controller for my homepage template:

    public function images()
    {
        return get_field('images');
    }

and Just display it in my template eg:

{{$image['alt']}}

Rather than creating a bunch of functions to return one field, I should be able to create one function in the controller that uses all the fields i have in my first comment like:

    public function homepageFields()
    {
        return array (
         $subheading =>  get_field('subheading'),
         $video = get_field('video_link')
       )
    }

Would that be the right syntax / way of going about it? and how would I display, the $video in the array, in the template? if it’s in the App namespace, ie:

{!! App\homepageFields($subheading) !!}

??

Cheers :slight_smile:

In your second example, you’d be able to access video in your Blade with the following:

{{$homepage_fields['video']}}

Methods in the App namespace are available to every Blade, unless they’re overwritten by another controller–i.e. if you had a controllers/FrontPage.php controller file that also defined homepageFields(), the $homepage_fields variable in views/front-page.blade.php would contain the value returned by the controllers/FrontPage.php version of homepageFields() instead of the one defined in controllers/App.php.

Personally, I don’t like to group unrelated data together in the variables I pass from my controllers to my Blades, because then my templates become more opinionated about how they handle data. That being the case, I’d separate out subheading and video instead of returning them as an array, but that’s a personal preference.

2 Likes

I’m currently working like this:

Controller (controllers/Home.php)

public function location()
{
    return (object) array(
        'title' => get_field('location_title'),
        'button_text' => get_field('location_button_text'),
        'button_link' => get_field('location_button_link')['url'],
    );
}

View (simplified)

<h2>{{ $location->title }}</h2>
<a href="{{ $location->button_link }}">{{ $location->button_text }}</a>

I like object syntax personally.

I split my templates down into components (location is a component used on the home page) and then use a method per component. I feel like this keeps everything nice and logical.

14 Likes

@alwaysblank @nathobson Thanks so much for your suggestions, I should probably get more of an understanding with MVC and take some courses :stuck_out_tongue: .

Really appreciate the help guys!

No problem. All pretty new to me too but once you’re past the initial learning curve, it all clicks very nicely. Having super clean views/templates is particularly satisfying. Maintainability is also greatly improved thanks to the separation of logic and templating. I find that once all the logic is taken care of, moving stuff around/amending your templates is really easy.

2 Likes

It is awesome, that’s what I love about React.js, it’s funny, where I work, we run an MVC environment but it’s such a mess, with php logic and everything in-between mashed into the templates haha so it’s good to understand the proper way of doing it :slight_smile:

This is what I like the most. I can shove all the ugly logic I need into a controller and keep my template clean with {{ $resultOfUglyLogicOhGodDontLookAtIt }}

4 Likes

Only thing is my directory structure is getting pretty crazy. More and more structure = more and more directories :smile:

Flipping between assets, controllers, views and whatever partials inside sub-directories of all of those.

Using object syntax like this is very clever and I like it a lot! I realize it’s mostly aesthetic preference on my part, but for some reason $container->field always looks so much nicer to me than $container['field'].

2 Likes

I’m just getting started here, but this has really given me new hope for Wordpress. I always got frustrated that my template files would get so messy when you started adding logic for ACF fields and so on.

I have created this Related Posts snippet. It utilizes an ACF Bidirectional Post Relationship plugin. The code is as follows:

<?php global $post; ?>
<?php $related_posts = get_field( 'related_posts' ); ?>
<?php if ( $related_posts ): ?>
	<?php foreach ( $related_posts as $p ): ?>
    {{the_post_thumbnail( 'thumbnail', array( 'class' => 'card-img-top img-fluid' ) )}}
	{{get_permalink( $p )}} 
    {{get_the_title( $p )}}
    
	<?php endforeach; ?>
  <?php wp_reset_postdata(); ?>
<?php endif; ?>

Ideally I’d like to remove as much as the logic from the template file. I’ve seen the examples above and I think I get it, but the global $post, The loop and wp_reset_postdata confuses me.

I can’t say this is the most elegant way of doing it but perhaps something like:

Controller

public function related()
{
    global $post;

    $data = [];

    $related_posts = get_field('related_posts');

    foreach ($related_posts as $p) {

        $this_post = (object) array(
            'thumbnail' => get_the_post_thumbnail($p, 'thumbnail', array('class' => 'card-img-top img-fluid')),
            'permalink' => get_the_permalink($p),
            'title' => get_the_title($p),
        );

        array_push($data, $this_post);
    }

    return $data;
}

View

@foreach ($related as $p)

    {{ $p->thumbnail }}
    {{ $p->permalink }}
    {{ $p->title }}

@endforeach

Totally untested and assumes that your returning your related post as the post ID not an object (by looking at what you posted up). It involves two loops (one in the controller and one in the view) but it keeps the view very tidy in my opinion.

5 Likes

This 100% for me :slight_smile:

1 Like

Been using ACF Fluent (with controller of course) and have been very pleased.

Also semi related and worth checking out is:

I use both in every project I work on now. They are wonderful and really bring ACF to the next level for me.

8 Likes

Thank you. It works. However, I don’t see what the $data variable is doing?

Each field within the post gets stored in the $this_post object within the foreach loop which is then added as a new property within the $data object.

It would be fairly easy to build in an acf helper class into Controller that speeds up the process of looping through fields and then outputting to an object notation. All you would need to use in the view then would be @foreach (for repeater fields). Basic fields could be flattened using the key as the variable name.

If you’re using acf in a standard way, it could all be automated to be honest. I’m going to look into building a prototype module over the next few days should time allow.

3 Likes

Been working on something like this. All values returned to the view are returned in object notation should that be required.

In a Controller Class;

<?php

namespace App\Controllers;

use Sober\Controller\Controller;
use Sober\Controller\Module\Acf;

class Single extends Controller
{
    // all returned values from Acf, even if multidimensional arrays are in object notation 

    public function acf()
    {
        return Acf::get();
        // gets all fields
        // use $acf->repeater in view
        // use $acf->text_field in view
    }

    public function text()
    {
        return Acf::get('text_field');
        // gets 1 field and returns flattened
        // use $text in view
    }

    public function combo()
    {
        return Acf::get(['repeater_field', 'text_field']);
        // gets 2 fields, and returns in object notation using advanced custom fields key values
        // use $combo->text_field in view
        // use $combo->repeater_field in view
    }
}
4 Likes