How to Get More Control Over ACF Flexible Content

Some time ago I wrote about a 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, check my previous article or feel free to 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?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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 an 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 that 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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, to help you 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. As we can see, 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
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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. At Chop-Chop we love these types of solutions!
As I promised earlier, here is the code of the get_theme_part
function:
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 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 );
}
Do you want to know more about ACF flexible content? Feel free to ask questions :)