How to get more control over ACF Flexible Content

Some time ago I wrote about modular approach to WordPress using Advanced Custom Fields Flexible Content. In the post I used a very basic example of how the fields can be used. Here, I would like to show a somewhat more complex solution, which we use at Chop-Chop on a daily basis. If you have any trouble at all understanding the below example, I encourage you to take a look at my previous article first. You can also write to us via our contact form or social media - we’ll be happy to help 🙂

The basic loop

Below you will find a sample code for the ACF Flexible Content field from the official ACF documentation. The code is quite simple, and it works, so why change it?

<?php

// check if the flexible content field has rows of data
if( have_rows('flexible_content_field_name') ):

     // loop through the rows of data
    while ( have_rows('flexible_content_field_name') ) : the_row();

        if( get_row_layout() == 'paragraph' ):

            the_sub_field('text');

        elseif( get_row_layout() == 'download' ): 

            $file = get_sub_field('file');

        endif;

    endwhile;

else :

    // no layouts found

endif;

?>

The problem with the above example is that each flexible content layout is wrapped in an if statement, and we declare variables based on the matching name of the layout afterwards. Such approach leads to creating imperative code, which is not bad in itself, but quickly becomes hard to maintain, and gives us a false sense of control. Our team created a solution which allows us to write code that is more declarative and modular.

Heads up - if you do not have a Flexible Content field ready to begin work on it, this is a good moment to create it. In the Advanced Custom Fields panel we add a new flexible content field, and change its name/slug to page_blocks. Next, we need to prepare all of the modules that you want to include on the site. Once that is done, we can leave the WordPress panel and get back to our code.

Advanced class approach

Take a look at the below code:

<?php
/**
 * Display theme blocks from flexible content (acf)
 *
 * @package WordPress
 * @subpackage themeName
 * @since themeName 1.0
 */

class ContentBlock {

    private static $theme_blocks_locations = array(
        'block_content'              => 'page/content',
    );

    private function __construct(){}

    public static function display_theme_blocks($field_name = 'page_blocks', $sec_param = null, $nav = false){
        if($sec_param == null)
            $sec_param = get_the_ID();

        $counters = array();
        while(have_rows($field_name, $sec_param)){ the_row();
            $block_layout = get_row_layout();

            if( !isset( $counters[ $block_layout ] ) )
            {
                $counters[ $block_layout ] = 1;
            }
            else
            {
                $counters[ $block_layout ]++;
            }

            if(isset(self::$theme_blocks_locations[$block_layout])){
                get_theme_part(self::$theme_blocks_locations[$block_layout], ['layout_counter' => $counters[ $block_layout ]]);
            }
        }
    }
    public static function the_block_title(){
        $block_title = get_sub_field('section_title');
        if(!empty($block_title)):
            echo $block_title;
        endif;
    }
}

The ContentBlock class may look a bit scary at first, but don’t worry, it’s more friendly than it looks. I will try to explain its most important elements, so that you can quickly understand how to use it.

For starters, take a look at the $theme_blocks_locations association table. Here we declare each layout and provide a path to the file responsible for describing it. The selected file has access to the full scope of the layout.

The display_theme_blocks method checks if the given block exists, and then uses get_theme_part to refer to the file with the given layout’s code. The get_theme_part function is not available in WordPress by default. It is a helper created by our company, which allows us to build pages from code divided into smaller blocks. Note that WordPress has a similar built-in function, which you may use as a replacement, called get_template_part. You can find its declaration at the end of this post.

Each new layout that we want to add to the project is declared in $theme_blocks_locations using the following syntax:

'layouts_name' => 'path_to_file'

The last step is to specify where in the code we want to display our flexible content field:

<?php ContentBlock::display_theme_blocks(); ?>

Exemplary use in a project

This is what the contents of the parts folder look like. Notice the block folder, where the flexible content files are situated. To refer to the file visible on the screen, you would need to use: get_theme_part('block/accordion/accordion-item') or get_template_part('parts/block/accordion/accordion-item.php').

Code visible in the screenshot

<?php
$title = get_sub_field('title');
$content = get_sub_field('content');
?>

<div class="accordion-item">
    <header class="accordion-header">
        <h4><?php echo $title; ?></h4>
    </header>
    <div class="accordion-content">
        <?php echo $content; ?>
    </div>
</div>

As I wrote earlier, the file has access to the full scope of the layout, so variables declared using the get_sub_field() function have access to the context.

More examples of sectioned code

<?php
/**
 * Block Name: Two column list
 *
 * This template displays two column list block.
 */

$title_left = get_field('title_left');
$list_left = 'list_left';
$title_right = get_field('title_right');
$list_right = 'list_right'
?>

<section class="two-column-lists">
    <div class="column">
        <h3 class="two-column-list-title"><?php echo $title_left; ?></h3>
        <?php wpc_repeater_loop([
            'name' => $list_left,
            'option' => false,
            'path' => 'block/two-column-list/two-column-list-item',
            'wrapper_tag' => 'ul',
            'wrapper_class' => 'two-column-list'
        ]); ?>
    </div>
    <div class="column">
        <h3 class="two-column-list-title"><?php echo $title_right; ?></h3>
        <?php wpc_repeater_loop([
            'name' => $list_right,
            'option' => false,
            'path' => 'block/two-column-list/two-column-list-item',
            'wrapper_tag' => 'ul',
            'wrapper_class' => 'two-column-list'
        ]); ?>
    </div>
</section>

That’s pretty much it - it’s not as daunting as it looks, isn’t it? I hope that you saw some upsides of the solution we use. Note how the example of the accordion layout has only 13 lines of code. Working with code like that is much easier, and has a real effect on the development time required to implement the given feature. It also limits the possibility of any bugs occurring. In Chop-Chop we love these types of solutions.

As I promised earlier, here is the code of the get_theme_part function:

/**
 * Retrieve part of the template.
 * Uses template engine build into theme to grab the file (relative to "parts" directory),
 * and pass variables to this files local scope.
 *
 * @param string $part
 * @param array  $data
 * @param string $folder
 */
function get_theme_part( $part, $data = array(), $folder = 'parts' ) {
    $engine = Theme_Template_Engine::create( $folder );
    $engine->render( $part, $data );
}

Subscribe to our Newsletter and get hand-picked articles

(Monthly updates. Good vibes only!)

The data you submit in the above form will be received and processed by Chop-Chop Sp. z o.o., located at Wloclawska 161, 87-100 Torun, Poland, EU. We will process the data in order to be able to provide you with marketing information. To read more on the way we handle data, please visit our Privacy Policy.