Skip to content

Instantly share code, notes, and snippets.

@CurtisL
Created March 12, 2019 20:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CurtisL/47af356d83efe078d227beb08302a6d5 to your computer and use it in GitHub Desktop.
Save CurtisL/47af356d83efe078d227beb08302a6d5 to your computer and use it in GitHub Desktop.
Dynamic ACF Gutenberg Blocks for v5.8

Dynamic ACF Gutenberg Blocks

When ACF v5.8 drops we'll have the ability to create gutenberg blocks from ACF Field Groups. This set of functions will add additional field group options to dynamcily register blocks with various block customization settings.

All you have to do is create your field groups and template.

Usage

Install Script

  • Make sure you're running ACF 5.8 or greater.
  • Copy or include this file in your functions.php.

Create field group

Create the field group(s) you'd like to use as a block. Enable the "Gutenberg Block" setting and configure as needed.

The Field Group title is used for the block name/slug. First Block: is removed from the title, then it is ran through sanitize_title() to generate a block slug. This allows you to optionally namespace the field groups ("Block: Testimonial", "Block: Slider"... etc);

Create template partial

Default template partials location is relative to the root of your theme in templates/acf-blocks/{block-slug}.php

You may filter the template partial location by filtering solum_dynamic_acf_block_partials_location to a path relative to your theme root.

Further debug by adding define('WP_ENV', 'development'); to your wp-config.php. This will add extra admin columns on the ACF Field Group screen displaying the location of what templates are being laoded for each block.

<?php
/**
* Utility to allow even easier registration of ACF Gutenberg Blocks.
* Adds additional field group options and dynamicly registers the blocks.
* All you have to do is create field group and block template.
*
* Requires ACF V5.8 or greater.
*/
/**
* Add custom gutenberg block category for ACF blocks
*
* @param array $categories existing block categories.
* @param WP_Post $post current post object.
* @return array
*/
function solum_add_acf_block_category( $categories, $post ) {
return array_merge(
$categories,
array(
array(
'slug' => 'acf-blocks',
'title' => __( 'Advanced Blocks' ),
'icon' => 'welcome-widgets-menus',
),
)
);
}
add_filter( 'block_categories', 'solum_add_acf_block_category', 10, 2 );
/**
* Parsing of field groups that have the block logic enabled.
*
* @return void
*/
function solum_register_dynamic_acf_blocks() {
if ( function_exists( 'acf_register_block' ) ) {
$field_groups = acf_get_field_groups();
foreach ( $field_groups as $field_group ) {
if ( ! empty( $field_group['as_block'] ) && 1 === $field_group['as_block'] ) {
// Support for namespacing blocks with "Block: " and avoid naming the block with it.
$title = str_replace( 'Block: ', '', $field_group['title'] );
$slug = sanitize_title( $title );
acf_register_block(
array(
'name' => $slug,
'title' => $title,
'description' => $field_group['description'] ?? '',
'render_callback' => 'solum_render_dynamic_acf_block_partial',
'category' => ! empty( $field_group['block_category'] ) ? $field_group['block_category'] : 'acf-blocks',
'icon' => ! empty( $field_group['block_icon'] ) ? $field_group['block_icon'] : 'welcome-widgets-menus',
'align' => ! empty( $field_group['block_align'] ) ? $field_group['block_align'] : '',
'keywords' => explode( ' ', $field_group['block_keywords'] ),
'post_types' => $field_group['block_post_types'] ? $field_group['block_post_types'] : [],
'mode' => ! empty( $field_group['block_mode'] ) ? $field_group['block_mode'] : 'preview',
'supports' => [
'align' => ! empty( $field_group['block_alignments'] ) ? $field_group['block_alignments'] : false,
'anchor' => $field_group['block_anchor'] ?? true,
'customClassNames' => $field_group['block_customClassNames'] ?? true,
'multiple' => $field_group['block_multiple'] ?? true,
'reusable' => $field_group['block_reusable'] ?? true,
],
)
);
}
}
}
}
add_action( 'acf/init', 'solum_register_dynamic_acf_blocks' );
/**
* Global render helper to parse to the proper template partial for each block type
*
* @param array $block the block being parsed.
* @return void
*/
function solum_render_dynamic_acf_block_partial( $block ) {
// Convert name ("acf/block_name") into path friendly slug.
$slug = str_replace( 'acf/', '', $block['name'] );
$title = $block['title'];
// Allow partial's location to be filtered.
$template_root = apply_filters( 'solum_dynamic_acf_block_partials_location', 'templates/acf-blocks' );
$block_template = "{$template_root}/{$slug}.php";
// Attempt to include a template part from within the defined templates folder.
if ( file_exists( locate_template( $block_template ) ) ) {
include locate_template( $block_template );
} elseif ( defined( 'WP_ENV' ) && 'development' === WP_ENV ) {
// Template not found, add recomendation.
error_log( "[WARNING] Create {$block_template} to complete registration for the \"{$title}\" acf block." );
}
}
/**
* Add field groupoptions to convert field group to a gutenberg block.
*
* @param array $field_group Field group data.
* @return void
*/
function solum_add_acf_fieldgroup_fields_for_blocks( $field_group ) {
acf_render_field_wrap(
array(
'label' => __( 'Gutenberg Block' ),
'instructions' => 'Turn this field group into a gutenberg block. (Will overwrite any location rules!)',
'type' => 'true_false',
'name' => 'as_block',
'key' => 'as_block', // for conditional logic.
'prefix' => 'acf_field_group',
'value' => $field_group['as_block'] ?? 0,
'ui' => 1,
)
);
$when_as_block_is_enabled = [
'field' => 'as_block',
'operator' => '==',
'value' => 1,
];
acf_render_field_wrap(
array(
'label' => __( 'Icon' ),
'instructions' => 'Can be any of <a href="https://developer.wordpress.org/resource/dashicons/" target="_blank">WordPress’ Dashicons</a>, or a custom svg element.',
'type' => 'text',
'placeholder' => 'welcome-widgets-menus',
'name' => 'block_icon',
'prefix' => 'acf_field_group',
'value' => $field_group['block_icon'] ?? 'welcome-widgets-menus',
'conditional_logic' => $when_as_block_is_enabled,
)
);
$category_chocies = array(
'common' => 'Common',
'formatting' => 'Formatting',
'layout' => 'Layout',
'widgets' => 'Widgets',
'embed' => 'Embed',
'acf-blocks' => 'Advanced Blocks',
);
acf_render_field_wrap(
array(
'label' => __( 'Category' ),
'instructions' => 'Blocks are grouped into categories to help users browse and discover them.',
'type' => 'select',
'name' => 'block_category',
'prefix' => 'acf_field_group',
'value' => $field_group['block_category'] ?? 'acf-blocks',
'default' => 'acf-blocks',
'choices' => $category_chocies,
'conditional_logic' => $when_as_block_is_enabled,
)
);
acf_render_field_wrap(
array(
'label' => __( 'Keywords' ),
'instructions' => 'A block may have aliases that help users discover it while searching.',
'type' => 'text',
'name' => 'block_keywords',
'prefix' => 'acf_field_group',
'value' => $field_group['block_keywords'] ?? '',
'conditional_logic' => $when_as_block_is_enabled,
)
);
$_post_types = get_post_types( null, 'objects' );
$exclude_acf = [ 'acf-field', 'acf-field-group' ];
$post_type_chocies = array_reduce(
$_post_types,
function( $carry, $item ) use ( $exclude_acf ) {
if ( in_array( $item->name, $exclude_acf, true ) || $item->_builtin && ! $item->public ) {
return $carry;
}
$carry[ $item->name ] = $item->label;
return $carry;
},
[]
);
acf_render_field_wrap(
array(
'label' => __( 'Supported Post Types' ),
'instructions' => 'Post types that can use this block. (Leave blank to for all post types)',
'type' => 'select',
'multiple' => 1,
'ui' => 1,
'name' => 'block_post_types',
'prefix' => 'acf_field_group',
'value' => $field_group['block_category'] ?? 'acf-blocks',
'choices' => $post_type_chocies,
'conditional_logic' => $when_as_block_is_enabled,
)
);
$alignment_choices = [
'left' => 'Left',
'center' => 'Center',
'right' => 'Right',
];
if ( get_theme_support( 'align-wide' ) ) {
$alignment_choices['wide'] = 'Wide';
$alignment_choices['full'] = 'Full';
}
acf_render_field_wrap(
array(
'label' => __( 'Alignment Options' ),
'instructions' => 'Alignment options to allow.',
'type' => 'checkbox',
'name' => 'block_alignments',
'prefix' => 'acf_field_group',
'value' => $field_group['block_alignments'] ?? [ 'left', 'center', 'right' ],
'choices' => $alignment_choices,
'conditional_logic' => $when_as_block_is_enabled,
)
);
$default_alignment_choices = [
null => '-- Choose Alignment Options First --',
];
if ( ! empty( $field_group['block_alignments'] ) ) {
$default_alignment_choices = wp_array_slice_assoc( $alignment_choices, $field_group['block_alignments'] );
}
acf_render_field_wrap(
array(
'label' => __( 'Default Alignment' ),
'instructions' => 'Select a default from the chosen Alignmet Options values. (save to update available options)',
'type' => 'select',
'name' => 'block_align',
'prefix' => 'acf_field_group',
'value' => $field_group['block_align'] ?? [ 'left' ],
'choices' => $default_alignment_choices,
'conditional_logic' => $when_as_block_is_enabled,
)
);
acf_render_field_wrap(
array(
'label' => __( 'Default Mode' ),
'instructions' => 'What to display when adding this block.',
'type' => 'radio',
'name' => 'block_mode',
'value' => $field_group['block_mode'] ?? 'preview',
'prefix' => 'acf_field_group',
'choices' => [
'preview' => 'Preview',
'edit' => 'Editor',
],
'conditional_logic' => $when_as_block_is_enabled,
)
);
$misc_supports_options = [
[
'name' => 'block_anchor',
'label' => 'Anchor',
'instructions' => 'Support for an anchor link to the specific block.',
'default' => 1,
],
[
'name' => 'block_customClassNames',
'label' => 'Custom Class Names',
'instructions' => 'Support for the custom class names input.',
'default' => 1,
],
[
'name' => 'block_multiple',
'label' => 'Multiple in a post',
'instructions' => 'Allow multiple instances of this block in a post',
'default' => 1,
],
[
'name' => 'block_reusable',
'label' => 'Reusable block',
'instructions' => 'Allow this block to be converted to a reusable block.',
'default' => 1,
],
];
foreach ( $misc_supports_options as $supports ) {
acf_render_field_wrap(
array(
'label' => $supports['label'],
'instructions' => $supports['instructions'],
'type' => 'true_false',
'name' => $supports['name'],
'prefix' => 'acf_field_group',
'value' => $field_group[ $supports['name'] ] ?? $supports['default'],
'default' => $supports['default'],
'conditional_logic' => $when_as_block_is_enabled,
'ui' => 1,
)
);
}
}
add_action( 'acf/render_field_group_settings', 'solum_add_acf_fieldgroup_fields_for_blocks', 10, 1 );
/**
* Force field to be saved as a block location
*
* @param array $field_group field group.
* @return array $field_group
*/
function solum_acf_fieldgroup_save_as_block( $field_group ) {
if ( ! empty( $field_group['as_block'] ) && 1 === $field_group['as_block'] ) {
$title = str_replace( 'Block: ', '', $field_group['title'] );
$slug = sanitize_title( $title );
$field_group['location'] = [
[
[
'param' => 'block',
'operator' => '==',
'value' => 'acf/' . $slug,
],
],
];
}
return $field_group;
}
add_filter( 'acf/validate_field_group', 'solum_acf_fieldgroup_save_as_block', 10, 1 );
/**
* DEBUG: Add block template column to ACF field Groups
*
* @param array $defaults existing columns.
* @return array
*/
function solum_debug_acf_add_admin_column_block_template( $defaults ) {
$defaults['block_template'] = 'Block Template Path';
return $defaults;
}
/**
* DEBUG: Output location of block template
*
* @param string $column current column.
* @param integer $post_id current post id.
* @return void
*/
function solum_debug_acf_output_block_template_column( $column, $post_id ) {
if ( 'block_template' === $column ) {
global $post;
$field_group = (array) maybe_unserialize( $post->post_content );
if ( ! empty( $field_group['as_block'] ) && $field_group['as_block'] ) {
$title = str_replace( 'Block: ', '', $post->post_title );
$slug = sanitize_title( $title );
$template_root = apply_filters( 'solum_dynamic_acf_block_partials_location', 'templates/acf-blocks' );
$template = "{$template_root}/{$slug}.php";
echo '<code>' . $template . '</code>';
if (!file_exists( locate_template($template) )) {
echo '<br/><strong style="color: red;">TEMPLATE MISSING</strong>';
}
}
}
}
/**
* Add admin columns to field groups that dislays the template locations to be loaded.
*/
if ( defined( 'WP_ENV' ) && 'development' === WP_ENV ) {
add_action( 'manage_edit-acf-field-group_columns', 'solum_debug_acf_add_admin_column_block_template', 35, 1 );
add_action( 'manage_acf-field-group_posts_custom_column', 'solum_debug_acf_output_block_template_column', 10, 2 );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment