ACF Custom Location Rules based on Block Settings

The Location Rules settings with Advanced Custom Fields give you a lot of control over where your field groups are displayed. However they don’t let you conditionally show fields based on other fields out of the box. And now with the Block Editor, what if you want some page settings to show, only when a certain block has been added to the page, or even a specific setting within a block.

I found myself in this predicament with a client request, and I’ll do my best to explain the process.

Field Group based on Pardot Form Block Settings

In my example, I’m embedding a Pardot form onto the page. I created a custom ACF Block for adding a Pardot Form to the page. The Pardot form is added as an iframe, and dealing with the success from that form is a whole other project that I’ll have to write about in the future.

On our Resource Custom Posts we have an automatic Thank You page generated after the form has been submitted. We have a custom field group on the page that builds this thank you content. Namely the short message, and link to the asset file for the resource.

They wanted to use this same Pardot from block on Pages, as well as a Landing Page CPT. But they also wanted the option to replace the form with a message, as well as redirect to a custom page. Or redirect to our Auto Thank You Page.

For the Auto Thank You Page option, I wanted to reuse as much of the fields and code that I already added for Resources. So I wanted to have this same field group show up, but only when the Auto Thank You option was selected in the Pardot Form Block.

ACF Custom Location Rules

The ACF documentation has a good documentation for adding custom location rules.

The first thing you’ll need to do is create a new class that extends ACF_Location. Then you need to register that class with ACF.

/**
 * Loads the custom location rules for Pardot Block Success Type
 *
 * @link https://www.advancedcustomfields.com/resources/custom-location-rules/
 */
function ajs_acf_init_location_auto_thank_you() {
	if ( function_exists( 'acf_register_location_type' ) ) {
		include_once(  TEMPLATE_DIR .'/inc/classes/AJS_ACF_Location_Pardot_Block_Success_Type.php' );
		acf_register_location_type( 'AJS_ACF_Location_Pardot_Block_Success_Type' );
	}
}
add_action( 'acf/init', 'ajs_acf_init_location_auto_thank_you' );

Using the ‘acf/init’ hook you’ll need to include the new class file, as well as use the acf_register_location_type() function. You pass in the name of your new class.

Over in the class we extend the initialize function with some settings for our new location.

<?php
if ( !defined('ABSPATH') ) {
	exit;
}

class AJS_ACF_Location_Pardot_Block_Success_Type extends ACF_Location {

	public function initialize() {
		$this->name = 'pardot_block_success_type',
		$this->label = __( 'Pardot Block Success Type' );
		$this->category = 'forms';
	}

}

This adds the location, but we still need to add any values to select from. We continue in our new class and add the get_values() function.

public function get_values( $rule ): array {
	return array(
	        'auto-thank-you' => 'Auto Thank You'
	);
}

I only needed the one option for this block, but you could add multiple values to the array to choose from.

Now I have my custom location able to assign a field group.

Screenshot of ACF Location rules

For ACF to know if it should display the field group, you need to add the match() function to the class. Lets see the code and then break down what’s happening.

public function match( $rule, $screen, $field_group ): bool {
	if ( isset( $screen['post_id'] ) ) {
		$post_id = (int) $screen['post_id'];
	} else {
		return false;
	}
		
	$post = get_post( $post_id );
	$blocks = parse_blocks( $post->post_content );
		
	foreach ( $blocks as $block ) {
		$acf_block = ajs_get_acf_block( $block, 'acf/pardot-form' );
			
		if ( $acf_block ) {
			$success_type = $acf_block['attrs']['data']['success_type'] ?? false;
				
			if ( $success_type === 'auto-thank-you' ) {
				if ( $rule['operator'] === '!=' ) {
					return false;
				}
				
				return true;
			}
		}
	}	
	return false;
}

Our function has three arguments, the $rule, the $screen, and the $field_group that we are checking.

The screen lets us look for the post_id. I use an isset check to make sure it’s available and then set the $post_id so I can grab the contents of the post.

Next we get the post and use the parse_blocks() function to grab all of the block content in the post.

We then loop through all of the blocks to find our Pardot Form Block and check the settings. Here I’m using ajs_get_acf_block( $block, 'acf/pardot-form'). This is a recursive function that goes through all the content of a block looking for the specific name of the block we added.

Note: As provided this will return only the first instance of a block. If you need to find all the copies of a block you’ll need to modify this function.

Recursive Get ACF Block Function

/**
 * Finds a target block with a parsed blocks object for a post
 *
 * Uses recursion to look through any nested innerBlock sections.
 *
 * @param $block_object
 *
 * @param $target_block
 *
 * @return object|false
 */
function ajs_get_acf_block( $block_object, $target_block ) {
 
	if ( $block_object['blockName'] === $target_block ) {
		return $block_object;
	}
	
	if ( ! empty( $block_object['innerBlocks'] ) ) {
		foreach ( $block_object['innerBlocks'] as $innerBlock ) {
			$innerBlockObject = ajs_get_acf_block( $innerBlock, $target_block );
			if ( $innerBlockObject ) {
				return $innerBlockObject;
			}
		}
	}
	
	return false;
}

We need the recursive function because our parsed blocks don’t separate out blocks from Groups or Columns. Those are instead in an innerBlocks block object. So we need to loop through all of the content inside that innerBlocks as well.

The function either returns the found block, or false.

...

if ( $acf_block ) {
	$success_type = $acf_block['attrs']['data']['success_type'] ?? false;
				
	if ( $success_type === 'auto-thank-you' ) {
		if ( $rule['operator'] === '!=' ) {
			return false;
		}
				
		return true;
	}
}

Returning to our match function, now that we have the block we can check the success type.

Based on the selected operator we can return if this was a match or not. I’m using “Is equal to” so the operator is ==, but you could also use this with the ACF “is not equal to” operator.

Import note

The only issue I’ve ran into with this method, is the match doesn’t update while you are working on the page. So after you change the setting in a block you have to refresh the page before your custom field group will display.

It’s a minor annoyance that did require a little extra description to the client. But the ability to control the field groups based on block settings is worth it.

Here’s the full code we used.

<?php

/**
 * Loads the custom location rules for Pardot Block Success Type
 *
 * @link https://www.advancedcustomfields.com/resources/custom-location-rules/
 */
function rb_acf_init_location_auto_thank_you() {
	if ( function_exists( 'acf_register_location_type' ) ) {
		include_once(  TEMPLATE_DIR .'/inc/classes/RB_ACF_Location_Pardot_Block_Success_Type.php' );
		acf_register_location_type( 'RB_ACF_Location_Pardot_Block_Success_Type' );
	}
}
add_action( 'acf/init', 'rb_acf_init_location_auto_thank_you' );

/**
 * Finds a target block with a parsed blocks object for a post
 *
 * Uses recursion to look through any nested innerBlock sections.
 *
 * @param $block_object
 *
 * @param $target_block
 *
 * @return object|false
 */
function rb_get_acf_block( $block_object, $target_block ) {
 
	if ( $block_object['blockName'] === $target_block ) {
		return $block_object;
	}
	
	if ( ! empty( $block_object['innerBlocks'] ) ) {
		foreach ( $block_object['innerBlocks'] as $innerBlock ) {
			$innerBlockObject = rb_get_acf_block( $innerBlock, $target_block );
			if ( $innerBlockObject ) {
				return $innerBlockObject;
			}
		}
	}
	
	return false;
}


// File '/inc/classes/RB_ACF_Location_Pardot_Block_Success_Type.php


<?php
/**
 * Registers custom ACF Location for Pardot Block Success Type
 *
 * The Match will parse the current page and find the Pardot Form Block.
 * If the Block settings are set to 'auto-thank-you' the match will return true.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class RB_ACF_Location_Pardot_Block_Success_Type extends ACF_Location {
	public function initialize() {
		$this->name = 'pardot_block_success_type';
		$this->label = __( 'Pardot Block Success Type');
		$this->category = 'forms';
	}
	
	public function match( $rule, $screen, $field_group ): bool {
		if ( isset( $screen['post_id'] ) ) {
			$post_id = (int) $screen['post_id'];
		} else {
			return false;
		}
		
		$post = get_post( $post_id );
		$blocks = parse_blocks( $post->post_content );
		
		foreach ( $blocks as $block ) {
			$acf_block = rb_get_acf_block( $block, 'acf/pardot-form' );
			
			if ( $acf_block ) {
				$success_type = $acf_block['attrs']['data']['success_type'] ?? false;
				
				if ( $success_type === 'auto-thank-you' ) {
					if ( $rule['operator'] === '!=' ) {
						return false;
					}
					
					return true;
				}
			}
		}
		
		return false;
	}
	
	public function get_values( $rule ): array {
		return array(
			'auto-thank-you' => 'Auto Thank You'
		);
	}
}

Leave a Reply