ACF { Guides Archives https://www.advancedcustomfields.com/resource-category/guides-acf-blocks/ Mon, 04 Dec 2023 18:59:13 +0000 en-US hourly 1 https://wordpress.org/?v=6.4.2 ACF Blocks: How to Use Block Locking https://www.advancedcustomfields.com/resources/acf-blocks-how-to-use-block-locking/ Mon, 04 Dec 2023 18:59:13 +0000 https://www.advancedcustomfields.com/?post_type=resource&p=434572 Locking down your ACF Blocks with block locking ensures those blocks can’t be removed or modified in unauthorized ways. You can think of block locking as a safety mechanism, whether you’re using it on a client’s WordPress site or one of your own. In this tutorial, we’ll show you how to use locking to restrict […]

The post ACF Blocks: How to Use Block Locking appeared first on ACF.

]]>
Locking down your ACF Blocks with block locking ensures those blocks can’t be removed or modified in unauthorized ways. You can think of block locking as a safety mechanism, whether you’re using it on a client’s WordPress site or one of your own. In this tutorial, we’ll show you how to use locking to restrict the movement and removal of individual blocks, and even patterns and templates.

In our previous tutorial on using ACF Blocks with InnerBlocks and parent/child relationships, we built a custom Author Info block (parent). This custom ACF Block used InnerBlocks to pass a series of nested blocks, which included another custom Author Twitter block (child).

The Author Twitter block was only available as a nested block within its parent Author Info block context. We used "parent": [ "acf/author-info" ] in the Author Twitter’s block.json file to establish this relationship and nested discoverability.

Now, we want to extend this Author Info block to refine the editorial experience for our editors. We want to make sure that they can get right to editing, and not accidentally remove or move a block and mess up the overall layout. This is where block locking is useful.

If you want to follow the steps in this tutorial exactly and already have some experience with ACF Blocks, you can download the plugin created in our last tutorial and add to it as we go.


New to ACF Blocks? Start with our tutorial on creating your first ACF Block!


What is Block Locking?

WordPress provides two types of block locking: template locking and individual block locking. You can utilize either of these, or combine them for sophisticated restrictions on your blocks.

Some other considerations when using block locking in WordPress:

  • Developers can use templateLock in their nested blocks.
  • Developers can lock entire templates with template_lock, even when registering custom post types.
  • Individual blocks can be assigned lock parameters in block markup. Assigned lock parameters can be used on the individual blocks passed into a template. The individual block’s lock overrides the template locking, unless the template locking value is contentOnly.

We highly recommend reviewing this helpful video on The Key to Locking Blocks from Wes Theron of the Make WordPress Training Team, along with the WordPress documentation.

Using InnerBlock Template Locking

Template locking can be accomplished by passing templateLock to the InnerBlocks component. Like this:

<?php
<InnerBlocks templateLock="all" />

Template locking options include the following:

  • contentOnly — Prevents all operations. Additionally, the block types that don’t have content are hidden from the list view and can’t gain focus within the block list. Unlike the other lock types, children can’t override this.
  • all — Prevents all operations. It is not possible to insert new blocks, move existing blocks, or delete blocks.
  • insert — Prevents inserting or removing blocks, but allows moving blocks.

Below are screenshots of what each of these would look like when applied to our Author Info block.

Editor with list view sidebar expanded and showing only nested content blocks with a lock icon. The result of having InnerBlock templateLock=contentOnly.

Author Info block with templateLock="contentOnly" set on InnerBlocks.
All nested blocks that do not contain content are hidden from the list view. Only content is editable.

Editor with list view sidebar expanded and showing all nested blocks with a lock icon. The result of having InnerBlock templateLock=all.

Author Info block with templateLock="all" set on InnerBlocks.
All nested blocks are locked to prevent moving, deleting, or inserting new blocks.

Editor with list view sidebar expanded and showing all nested blocks with a lock icon. Also, edit navigation expanded for nested paragraph block with option to 'Move to' highlighted. The result of having InnerBlock templateLock=insert.

Author Info block with templateLock="insert" set on InnerBlocks.
All nested blocks are locked from being removed or having new blocks inserted. Existing blocks can be moved.

Unlocking One Nested Block

For our Author Info block, we want to offer editors the option to remove the author’s biography paragraph, but everything else should remain locked from being removed. We’ll start by locking all the blocks down with templateLock="all" attribute on our InnerBlocks component, and then we’ll use individual locking properties to unlock the one nested block we want to allow them to remove. We’ll make all of our changes to the Author’s Info template.php file, in the author-info directory of the custom plugin we created in our tutorial on InnerBlocks.

For now, we’ll update our InnerBlocks code as follows:

<?php
<InnerBlocks templateLock="all" />

Next, we’ll have to update our individual nested block to allow removal. So, if editors choose to remove the author’s biography paragraph then they have the ability.

How to Use Individual Block Locking

Individual block locking takes priority and overrides template locking, unless templateLock="contentOnly" is used. This hides all blocks that do not have content from the list view, and allows editors to update only the nested content block.

However, we previously decided to apply templateLock="all". To update the individual Paragraph block nested within our passed template, we just need to target it with an additional lock parameter. Here is our abbreviated nested code before applying the parameter:

array(
    'core/paragraph',
    array(
        'style'    => array(...),
        'fontSize' => 'small',
        'content'  => 'Ea qui voluptate irure nulla aliquip nulla anim laborum exercitation eu incididunt.',
    ),
    array(),
),

And here it is after, with our lock applied:

array(
    'core/paragraph',
    array(
        'style'    => array(...),
        'fontSize' => 'small',
        'content'  => 'Ea qui voluptate irure nulla aliquip nulla anim laborum exercitation eu incididunt.',
        'lock'     => array(
            'remove' => false,
        ),
    ),
    array(),
),

With the 'remove' => false above, we’re stating that the nested Paragraph block should be allowed to be removed. Yes, the false is confusing, but this is how ya do it! 😉

Now we have our desired control of the block, with both template locking and individual block locking in place.

Author Info block example with lines pointing to differentiated blocks.

Author Info block example with lines pointing to differentiated blocks.

Next Steps and Considerations

This tutorial shows how you can use block and template locking to set editorial conditions and prevent unauthorized removal or modification of your ACF Blocks, but there is a lot of room left to explore.

There is another element to curating your editorial experience, which can be utilized when placing your blocks within patterns. If you plan to leverage your ACF Blocks in the context of patterns, then you’ll want to check out content-only block locking.

You could also extend your block content control strategy by preventing certain roles or users from locking and unlocking blocks. Be sure to check out how to restrict locking and unlocking for roles, specific users, or even post types.

If you’ve been following along, your final, full plugin code should match our version: author-info-block-v2.zip

In the next tutorial, we’ll show you how to use WordPress global styles and block styles with your ACF Blocks, providing a consistent and easy to use experience when managing settings and styles.

The post ACF Blocks: How to Use Block Locking appeared first on ACF.

]]>
ACF Blocks: Using InnerBlocks and Parent/Child Relationships https://www.advancedcustomfields.com/resources/acf-blocks-using-innerblocks-and-parent-child-relationships/ Wed, 11 Oct 2023 15:13:31 +0000 https://www.advancedcustomfields.com/?post_type=resource&p=408935 In this tutorial, we’ll show you how to utilize the InnerBlocks component within an ACF Block. We’ll also demonstrate how you can leverage relationships to nest blocks while limiting their discoverability. By the end of the tutorial, you’ll know how to create an “Author Info” block that mixes ACF Blocks with WordPress core blocks, allowing […]

The post ACF Blocks: Using InnerBlocks and Parent/Child Relationships appeared first on ACF.

]]>
In this tutorial, we’ll show you how to utilize the InnerBlocks component within an ACF Block. We’ll also demonstrate how you can leverage relationships to nest blocks while limiting their discoverability.

By the end of the tutorial, you’ll know how to create an “Author Info” block that mixes ACF Blocks with WordPress core blocks, allowing editors to assign an author image, name, bio, and a social link.

In future tutorials, we’ll demonstrate how to use the Block Locking API (introduced in WordPress 5.9) to lock certain blocks from being modified or moved, and show how to extend your ACF Blocks with alternative Block Styles.


New to ACF Blocks? Start with our tutorial on creating your first ACF Block!


The video below provides a walkthrough of the Author Info block we’re going to create.

Author Info block example with lines pointing to differentiated blocks.

Author Info block example with lines pointing to differentiated blocks.

Steps to complete

  1. Create a WordPress plugin to organize our custom code
  2. Register parent and child ACF Blocks with block.json
  3. Register ACF field for the author’s Twitter
  4. Create display logic with InnerBlocks and block template

Requirements

ACF Blocks is a premium feature found only in ACF PRO. You will also need a WordPress site where you have access to the wp-content directory. We highly recommend using Local or another WordPress development environment to create a sandbox site so you can experiment in complete safety.

Nesting Blocks With InnerBlocks

The InnerBlocks WordPress component is handy for creating a custom block that passes a series of curated nested blocks. There are a few things about how InnerBlocks works that we should cover before we get too deep into the tutorial.

Some native WordPress blocks already utilize InnerBlocks as part of their functionality, including the Columns and Social Icons blocks.

It is important to note that a block can only contain a single InnerBlocks component. This is due to the way the contents of InnerBlocks is stored on the block.

Using nested templates in InnerBlocks

Passing a template of blocks into the InnerBlocks component can pre-populate nested blocks and placeholder attributes, expediting the editorial experience.

Admittedly, the process to create your nested block template can be a bit clumsy, especially if you’re using several blocks and nesting is a few levels deep. Typically, we recommend something like the following process:

  1. Create your nested block layout in the block editor, assigning all desired styles and attributes.
  2. Select the parent block in the editor and choose Copy.
  3. Visit the WPHTML Converter and paste your block markup from the editor. Click Convert to PHP, and copy the converted markup.
  4. Paste the converted markup into your block’s template.php and assign it to a variable so you can use it later on. Be sure to nest it within an array(). We’ll cover this in our last step in this tutorial.

Here is a simple example of what this might look like:

Example of passing a template to InnerBlocks

<?php
    $my_block_template = array(
        array(
            'core/group',
            array(
                'layout' => array(
                    'type' => 'constrained',
                ),
            ),
            array(
                array(
                    'core/paragraph',
                    array(
                        'align'   => 'center',
                        'content' => 'I'm a paragraph.',
                    ),
                    array(),
                ),
                array(
                    'core/separator',
                    array(),
                    array(),
                ),
            ),
        ),
    );

    //...

    <InnerBlocks template="<?php echo esc_attr( wp_json_encode( $my_block_template ) ); ?>" />

Using parent and ancestor relationships in blocks

A common pattern for using InnerBlocks is to create a custom block that’s only available if its parent block is inserted. This allows builders to establish a relationship between blocks, while also limiting a nested block’s discoverability. Currently, there are two relationships builders can use: parent and ancestor. The differences are:

  • If you assign a parent, then you’re stating that the nested block can only be used and inserted as a direct descendant of the parent.
  • If you assign an ancestor, then you’re stating that the nested block can be used and inserted as any descendant of the parent.

The key difference between parent and ancestor is that parent has finer specificity, while an ancestor has greater flexibility in its nested hierarchy.

In our Author Info block, we want the Author Twitter block to be nested inside as a direct descendant. Author Info is defined as the parent in the Author Twitter’s block.json file:

Example block.json with parent key


{ ... "parent": [ "acf/author-info" ] }

Alright, let’s start building our custom plugin and blocks!

1. Create a WordPress plugin to organize our custom code

For our example, we’ll create a custom WordPress plugin to organize our code, although you can create the blocks directly in your theme if you wish. Create a new author-info-block folder in your WordPress site’s wp-content/plugins folder. Then add an author-info-block.php file in the folder. It’s within this file that we’ll define our new WordPress plugin with the following code:

wp-content/plugins/author-info-block/author-info-block.php

<?php
/**
 * Plugin Name: Author Info Block for ACF PRO
 * Description: An example ACF PRO Block to display Author Info.
 * Text Domain: demo-author-block-acf
 */

2. Register parent and child ACF Blocks with block.json

We need to register two ACF Blocks:

  1. Author Info (parent)
  2. Author Twitter (child)

If you’ve followed our Create Your First ACF Block tutorial, then this should be pretty familiar. However, we’re doing things a bit differently, because we’re organizing our blocks in a custom WordPress plugin for this tutorial instead of a child theme.

To register our blocks, we’ll continue editing author-info-blocks.php, and add the following code right after the header:

wp-content/plugins/author-info-block/author-info-block.php

<?php
/**
 * Register our blocks.
 *
 * @link https://www.advancedcustomfields.com/resources/whats-new-with-acf-blocks-in-acf-6/
 *
 * @return void
 */
function demo_author_block_acf_blocks_register() {
    /**
     * We register our blocks with WordPress's handy
     * register_block_type();
     *
     * @link https://developer.wordpress.org/reference/functions/register_block_type/
     */
    // Author Info (parent) block.
    register_block_type( __DIR__ . '/blocks/author-info' );
    // Author Twitter (child) block.
    register_block_type( __DIR__ . '/blocks/author-twitter' );
}
add_action( 'init', 'demo_author_block_acf_blocks_register', 5 );

The code snippet above is registering two blocks with register_block_type(). See the WordPress docs for more information on the args you can pass with this function.

All WordPress blocks, including ACF Blocks, require a block.json file to register their properties. Each block must have its own block.json file.

First, create a blocks directory in your plugin, and then two more directories nested within it: author-info and author-twitter.

Each of these blocks needs the required block.json file. We’ll also create an empty template.php in each directory, which is where our display logic will go (we’ll get to this later). When you’re done, you should have the following within your plugin’s blocks directory:

  • author-info-block/blocks/author-info/block.json
  • author-info-block/blocks/author-info/template.php
  • author-info-block/blocks/author-twitter/block.json
  • author-info-block/blocks/author-twitter/template.php

Let’s start by populating our Author Info block’s block.json metadata.

blocks/author-info/block.json

{
    "name": "acf/author-info",
    "title": "Author Info (ACF Block)",
    "description": "Display an author's info and picture.",
    "icon": "id",
    "keywords": ["author", "writer"],
    "acf": {
        "mode": "preview",
        "renderTemplate": "template.php"
    },
    "supports": {
        "align": false,
        "anchor": true,
        "mode": false
    }
}

And here is the block.json code for the Author Twitter block.

blocks/author-twitter/block.json

{
    "name": "acf/author-twitter",
    "title": "Author Twitter (ACF Block)",
    "description": "Display an author's Twitter link.",
    "icon": "twitter",
    "keywords": ["author", "twitter"],
    "acf": {
        "renderTemplate": "template.php"
    },
    "supports": {
        "anchor": false,
        "align": false
    },
    "parent": [ "acf/author-info" ]
}

The configuration keys used in ACF Blocks are generally the same as those used in WordPress core blocks, but with the addition of the acf configuration key.

If you’re new to registering WordPress block types with block.json metadata, then be sure to dig in to WordPress Block Editor Handbook’s reference: Metadata in block.json.

Some things of note in the block.json files:

  • "renderTemplate": "template.php": Found in both files, this is where our template logic will exist for each block. You are free to change the names of your template files as long as that change is reflected in your JSON file as well. We’ll add the code to these template files in a later step.
  • "parent": [ "acf/author-info" ]: This is found only in the Author Twitter block, making it available only within the Author Info block. The parent configuration key automatically designates the block as only available when used as a child block of the named parent.

Be sure to save your progress, and then go ahead and activate your custom plugin. Because we’ve already defined our ACF Blocks with block.json in the plugin, activating it will automatically make those blocks available. However, the Author Twitter block will only be available in the block inserter if its parent, the Author Info block, exists. Also, since our template.php files are empty, you’ll see nothing in the block editor. Let’s keep going and fix that.

3. Register ACF field for the author’s Twitter

We want to allow editors to be able to add a custom Twitter username, which will in turn create a link for the author’s Twitter profile. So, we need to register a custom field for this and assign it to our Author Twitter block. There are two ways to go about this. One way is to register everything through ACF’s UI, use “Generate PHP” in the ACF’s Tools menu, and then drop the generated code into your plugin’s author-info-block.php file. Alternatively, you can register the fields via PHP in author-info-block.php.

If you’re using ACF’s UI, go ahead and create a new field group for Author Twitter and add a single Text field for Twitter. Be sure to assign it to the Author Twitter block under the “Location Rules” settings.

ACF new field group screen with Twitter field added.

ACF new field group screen with the Presentation tab activated.

Changing settings on the Twitter field’s Presentation tab can provide an easier editorial experience:

  • Presentation > Instructions: https://twitter.com/[username]
  • Presentation > Prepend: https://twitter.com/

We could stop here, because technically our field group is registered and assigned to the block. However, we want to keep our entire custom block plugin portable and distributable between projects. So, we’ll want to co-locate the registration of our newly created Author Twitter field group to reside within our custom plugin. Lucky for us ACF makes this easy too!

Click on Tools in the ACF menu, then select your Author Twitter field group and click Generate PHP. This will give you the code to drop right into your plugin.

Exporting the PHP for a field group using ACF's built-in tools.

Once you’ve added the generated code to your plugin’s author-info-block.php, you can go ahead and delete the field group that you created in ACF’s UI, because it is now registered with code.

wp-content/plugins/author-info-block/author-info-block.php

<?php
/**
 * Register our Author Twitter field group
 * directly in our PHP for portability.
 *
 * @link https://www.advancedcustomfields.com/resources/register-fields-via-php/
 */
add_action(
    'acf/include_fields',
    function() {
        if ( ! function_exists( 'acf_add_local_field_group' ) ) {
            return;
        }

        acf_add_local_field_group(
            array(
                'key'      => 'group_64e3e09959e8f',
                'title'    => 'Author Twitter',
                'fields'   => array(
                    array(
                        'key'          => 'field_64e3e09a7a544',
                        'label'        => 'Twitter',
                        'name'         => 'twitter',
                        'type'         => 'text',
                        'instructions' => 'https://twitter.com/[username]',
                        'prepend'      => 'https://twitter.com/',
                    ),
                ),
                'location' => array(
                    array(
                        array(
                            'param'    => 'block',
                            'operator' => '==',
                            'value'    => 'acf/author-twitter',
                        ),
                    ),
                ),
            )
        );
    }
);

We’ve gone ahead and streamlined the output above, because not all options are totally necessary. ACF will use defaults for the omitted options. As mentioned previously, it’s entirely possible to skip registering the fields in the UI, and just write the PHP code needed. The Author Info block is controlled by the PHP in author-info-block.php, not the selections made in the admin UI.

4. Create display logic with InnerBlocks and block template

At this point, you have two blocks registered, but there is no display logic for either of them. This is where we utilize InnerBlocks and pass down a block template string to assign and automatically populate the Author Info block upon creation.

First, we’ll set up our display logic for our Author Twitter block, which is outputting the Author Twitter field group we created in the last step.

Open the Author Twitter block’s author-info-block/blocks/author-twitter/template.php and insert the following code.

blocks/author-twitter/template.php

<?php
/**
 * Author Twitter (child) block.
 * This block is only available as a child
 * block within the parent Author Info block.
 */

// Grab our ACF field.
$twitter_handle = get_field( 'twitter' );
?>

<?php if ( empty( $twitter_handle ) ) : ?>
    <p>Please enter a Twitter handle.</p>
<?php else : ?>
    <p>
        <svg height="0.95rem" width="0.95rem" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="icon icon-twitter" viewBox="0 0 24 24"><path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"/></svg>
        <a href="https://twitter.com/<?php echo esc_html( $twitter_handle ); ?>">
            @<?php echo esc_html( $twitter_handle ); ?>
        </a>
    </p>
<?php endif; ?>

This will check for our Twitter field value and display it if there is one available.

Next, we’ll add the Author Info block’s display logic in the corresponding demo-acf-plugin/blocks/author-info/template.php file.

blocks/author-info/template.php

<?php
/**
 * Author Info block (parent).
 *
 * @param array  $block The block settings and attributes.
 * @param string $content The block inner HTML (empty).
 * @param bool   $is_preview True during backend preview render.
 * @param int    $post_id The post ID the block is rendering content against.
 *                     This is either the post ID currently being displayed inside a query loop,
 *                     or the post ID of the post hosting this block.
 * @param array $context The context provided to the block by the post or its parent block.
 */

// Support custom id values.
$block_id = '';
if ( ! empty( $block['anchor'] ) ) {
    $block_id = esc_attr( $block['anchor'] );
}

// Create class attribute allowing for custom "className".
$class_name = 'demo-author-block-acf';
if ( ! empty( $block['className'] ) ) {
    $class_name .= ' ' . $block['className'];
}

/**
 * A template string of blocks.
 * Need help converting block HTML markup to an array?
 * 👉 https://happyprime.github.io/wphtml-converter/
 *
 * @link https://developer.wordpress.org/block-editor/reference-guides/block-api/block-templates/
 */
$inner_blocks_template = array(
    array(
        'core/columns',
        array(
            'verticalAlignment' => 'center',
            'style'             => array(
                'spacing' => array(
                    'padding' => array(
                        'top'    => 'var:preset|spacing|30',
                        'right'  => 'var:preset|spacing|30',
                        'bottom' => 'var:preset|spacing|30',
                        'left'   => 'var:preset|spacing|30',
                    ),
                ),
            ),
        ),
        array(
            array(
                'core/column',
                array(
                    'verticalAlignment' => 'center',
                    'width'             => '120px',
                ),
                array(
                    array(
                        'core/image',
                        array(
                            'align'           => 'center',
                            'sizeSlug'        => 'thumbnail',
                            'linkDestination' => 'none',
                            'className'       => 'is-style-rounded',
                            'url'             => 'https://i.pravatar.cc/120',
                        ),
                        array(),
                    ),
                ),
            ),
            array(
                'core/column',
                array(
                    'verticalAlignment' => 'center',
                    'width'             => '',
                ),
                array(
                    array(
                        'core/paragraph',
                        array(
                            'fontSize' => 'large',
                            'content'  => 'Taylor Swift',
                        ),
                        array(),
                    ),
                    array(
                        'core/paragraph',
                        array(
                            'style'    => array(
                                'spacing' => array(
                                    'margin' => array(
                                        'bottom' => '0',
                                        'top'    => '0',
                                    ),
                                ),
                            ),
                            'fontSize' => 'small',
                            'content'  => 'Ea qui voluptate irure nulla aliquip nulla anim laborum exercitation eu incididunt.',
                        ),
                        array(),
                    ),
                    array(
                        'acf/author-twitter-v1',
                        array(
                            'name' => 'acf/author-twitter-v1',
                            'data' => array(
                                'twitter' => 'wp_acf',
                            ),
                            'mode' => 'auto',
                        ),
                        array(),
                    ),
                ),
            ),
        ),
    ),
);

?>

<?php if ( ! $is_preview ) { ?>
    <div
        <?php
        echo wp_kses_data(
            get_block_wrapper_attributes(
                array(
                    'id'    => $block_id,
                    'class' => esc_attr( $class_name ),
                )
            )
        );
        ?>
    >
<?php } ?>

    <InnerBlocks
        class="demo-author-block-acf__innerblocks"
        template="<?php echo esc_attr( wp_json_encode( $inner_blocks_template ) ); ?>"
    />

<?php if ( ! $is_preview ) { ?>
    </div>
<?php } ?>

There is a lot going on in the code snippet above. Let’s break it down. The biggest piece of this snippet is the $inner_blocks_template (near the top), a variable we assign to our template property within <InnerBlocks> (near the bottom). The $inner_blocks_template variable consists of an array of blocks and their attributes, with further nested blocks within those. Near the bottom of the long nested array you’ll see that we’re passing our custom acf/author-twitter block with a default twitter field value of wp_acf. This will help pre-populate the field when the Author Info block is added in a new post.

The nesting and proper syntax to get these complex template strings correct can be pretty hard to figure out. The array that defines the $inner_blocks_template has over 30 more arrays nested within it. The first array is core/columns, a WordPress core block that allows us to display multiple columns that each contain blocks. Diving a little deeper into the core/columns array, we see it’s a further array containing style values:

$inner_blocks_template = array(
    array(
        'core/columns',
        array(
            'verticalAlignment' => 'center',
            'style'             => array(
                'spacing' => array(
                    'padding' => array(
                        'top'    => 'var:preset|spacing|30',
                        'right'  => 'var:preset|spacing|30',
                        'bottom' => 'var:preset|spacing|30',
                        'left'   => 'var:preset|spacing|30',
                    ),
                ),
            ),
        ),

This nesting quickly becomes complex, even for relatively simple uses of InnerBlocks. The easiest way to create an array like this is to create the layout and design within the block editor, and then switch to “List View” and click the three dots to the right of your block. Click Copy block to copy the WordPress block HTML to your clipboard. Finally, paste it into a tool like WPHTML Converter to convert the markup into the proper array format.

Below is a video demonstrating the conversion process.

If you’ve been following along, your final, full plugin code should match our version: author-info-block-v1.zip

Wrapping Up

This tutorial illustrates how to create an Author Info block and integrate InnerBlocks with ACF Blocks to create nested blocks with parent/child relationships. However, there is still much more to explore.

In the next tutorial, we’ll show you how to use block and template locking to lock down key blocks in your markup and set editorial conditions. This helps ensure your editors only edit what they need to, setting them up for the best editorial experience.

The post ACF Blocks: Using InnerBlocks and Parent/Child Relationships appeared first on ACF.

]]>
How To Upgrade a Legacy Block to block.json With ACF 6 https://www.advancedcustomfields.com/resources/how-to-upgrade-a-legacy-block-to-block-json-with-acf-6/ Tue, 20 Sep 2022 09:20:55 +0000 https://www.advancedcustomfields.com/?post_type=resource&p=338618 ACF 6.0 introduces support for WordPress’s block.json method of block registration, the recommended way of registering blocks since WordPress 5.8 and newer. You don’t have to upgrade your blocks, but you’ll need to do so if you want to take advantage of new WordPress functionality like attribute defaults (for things like background colors) or block […]

The post How To Upgrade a Legacy Block to block.json With ACF 6 appeared first on ACF.

]]>
ACF 6.0 introduces support for WordPress’s block.json method of block registration, the recommended way of registering blocks since WordPress 5.8 and newer. You don’t have to upgrade your blocks, but you’ll need to do so if you want to take advantage of new WordPress functionality like attribute defaults (for things like background colors) or block assets loading for compatibility with block patterns.

It’s a relatively quick and painless process to upgrade your blocks from the acf_register_block_type method in ACF 5 to the new block.json format, and in this guide we’ll demonstrate how to upgrade our testimonial block from the ACF 5 documentation to a block.json block.

Let’s start with the old block example:

functions.php

add_action('acf/init', 'my_acf_blocks_init');
function my_acf_blocks_init() {

    // Check function exists.
    if( function_exists('acf_register_block_type') ) {

        // Register a testimonial block.
        acf_register_block_type(array(
            'name'              => 'testimonial',
            'title'             => __('Testimonial'),
            'description'       => __('A custom testimonial block.'),
            'render_template'   => 'template-parts/blocks/testimonial/testimonial.php',
            'mode'              => 'preview',
            'category'          => 'formatting',
        ));
    }
}

template-parts/blocks/testimonial/testimonial.php

<?php

/**
 * Testimonial Block Template.
 *
 * @param   array $block The block settings and attributes.
 * @param   string $content The block inner HTML (empty).
 * @param   bool $is_preview True during AJAX preview.
 * @param   (int|string) $post_id The post ID this block is saved to.
 */

// Create id attribute allowing for custom "anchor" value.
$id = 'testimonial-' . $block['id'];
if( !empty($block['anchor']) ) {
    $id = $block['anchor'];
}

// Create class attribute allowing for custom "className" and "align" values.
$className = 'testimonial';
if( !empty($block['className']) ) {
    $className .= ' ' . $block['className'];
}
if( !empty($block['align']) ) {
    $className .= ' align' . $block['align'];
}

// Load values and handle defaults.
$text = get_field('testimonial') ?: 'Your testimonial here...';
$author = get_field('author') ?: 'Author name';
$role = get_field('role') ?: 'Author role';
$image = get_field('image') ?: 295;
$background_color = get_field('background_color');
$text_color = get_field('text_color');

?>
<div id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $className ); ?>">
    <blockquote class="testimonial-blockquote">
        <span class="testimonial-text"><?php echo esc_html( $text ); ?></span>
        <span class="testimonial-author"><?php echo esc_html( $author ); ?></span>
        <span class="testimonial-role"><?php echo esc_html( $author_role ); ?></span>
    </blockquote>
    <div class="testimonial-image">
        <?php echo wp_get_attachment_image( $image, 'full' ); ?>
    </div>
    <style type="text/css">
        #<?php echo esc_attr( $id ); ?> {
            background: <?php echo esc_attr( $background_color ); ?>;
            color: <?php echo esc_attr( $text_color); ?>;
        }
    </style>
</div>

Upgrading Your Block

To upgrade this block, we need to turn the code into a new block.json file, which contains a JSON object of all the settings for the block.

If you want to store blocks in your theme, you need to make sure you’re using WordPress 6.0 which introduced support for that. If you’re storing them in a plugin, you need at least WordPress 5.8.

blocks/testimonial/block.json

{
    "name": "acf/testimonial",
    "title": "Testimonial",
    "description": "A custom testimonial block.",
    "style": [ "file:./testimonial.css" ],
    "category": "formatting",
    "icon": "admin-comments",
    "keywords": ["testimonial", "quote"],
    "acf": {
        "mode": "preview",
        "renderTemplate": "testimonial.php"
    },
    "align": "full"
}

The key changes in the transition from the old method is that all ACF specific configuration now lives inside an acf key. You can view the full options for that array in our ACF block.json documentation.

You’ll also notice that the WordPress convention for block.json files is that all property keys are camelCase. We’ve respected this in ACF’s configuration, and anything that used to contain underscores is now camelCased, for example render_template is now renderTemplate.

Asset Loading

Another major change regards asset loading. Instead of using the enqueue_script, enqueue_style functions, or an enqueue_assets callback, you should use the new WordPress provided block asset functions. You’re mostly to use script or style which will load your assets in both the backend editor, and the frontend view too. Although there are options which will load separate assets in each view if you prefer.

For all the asset properties of your block.json file, you can either pass an asset handle which has already been registered before you register your block as a string – or you can use the file:./filename.css method above.

This is a custom format called a WPDefinedAsset. You can also add a filename.asset.php which provides some metadata for your asset, such as dependencies and versions. This is useful to ensure your block also has things like jQuery available. See the WPDefinedAsset documentation for full details of this.

Most of these options also support arrays of multiple files, but be aware of the version of WordPress that introduced support for multiple assets for the type of asset loading you’re using.

If you opt to use wp_register_style() or wp_register_script() style handles, we recommend you register them on the line before you load the block to ensure it’s available.

Loading the New block.json Block

Registering the block is as simple as using the WordPress native function register_block_type, for which you pass a path to the folder containing the block.json file. If you prefer, you can instead pass the full path to the block.json, but it is important to note your block must be called block.json to load. No other file names are allowed as of WordPress 6.0.

We also recommend registering the block earlier than acf/init, so we use the WordPress default of init. You may also find if you want to use script or style handles you should pass a priority earlier than the default of 10. In our example below we use 5.

functions.php

add_action( 'init', 'register_acf_blocks', 5 );
function register_acf_blocks() {
    register_block_type( __DIR__ . '/blocks/testimonial' );
}

This should be all you need to do to upgrade your ACF 5 block to an ACF 6 block! Your existing templating will still work and shouldn’t need any changes, although we’ve got a few notes on that below.

Templating

Our previous ACF 5 documentation for blocks showed the following method of applying styles specific to a block, using the $block['id']:

<style type="text/css">
#<?php echo esc_attr( $id ); ?> {
    background: <?php echo esc_attr( $background_color ); ?>;
    color: <?php echo esc_attr( $text_color ); ?>;
}
</style>

Various new WordPress features since 5.8, such as the Query Loop block and Block Patterns, mean using either of those features would result in $id duplication.

In ACF 6.0, we’ve changed block IDs to overcome this, and while we still generate one for your template, if you have two copies of the exact same block on a page, their IDs will still be the same. For this reason, you should not rely on the $block['id'] being unique, and not use it to target CSS classes.

Instead, we’ve updated our testimonial template file to build a style property which is added to the DOM element instead:

block/testimonial/testimonial.php

<?php
/**
 * Testimonial Block Template.
 *
 * @param   array $block The block settings and attributes.
 * @param   string $content The block inner HTML (empty).
 * @param   bool $is_preview True during backend preview render.
 * @param   int $post_id The post ID the block is rendering content against.
 *          This is either the post ID currently being displayed inside a query loop,
 *          or the post ID of the post hosting this block.
 * @param   array $context The context provided to the block by the post or its parent block.
 */

// Support custom "anchor" values.
$anchor = '';
if ( ! empty( $block['anchor'] ) ) {
    $anchor = 'id="' . esc_attr( $block['anchor'] ) . '" ';
}

// Create class attribute allowing for custom "className" and "align" values.
$class_name = 'testimonial-block';
if ( ! empty( $block['className'] ) ) {
    $class_name .= ' ' . $block['className'];
}
if ( ! empty( $block['align'] ) ) {
    $class_name .= ' align' . $block['align'];
}

// Load values and assign defaults.
$text             = get_field( 'testimonial' ) ?: 'Your testimonial here...';
$author           = get_field( 'author' ) ?: 'Author name';
$author_role      = get_field( 'role' ) ?: 'Author role';
$image            = get_field( 'image' ) ?: 295;
$background_color = get_field( 'background_color' );
$text_color       = get_field( 'text_color' );

// Build a valid style attribute for background and text colors.
$styles = array( 'background-color: ' . $background_color, 'color: ' . $text_color );
$style  = implode( '; ', $styles );

?>
<div <?php echo $anchor; ?>class="<?php echo esc_attr( $class_name ); ?>" style="<?php echo esc_attr( $style ); ?>">
    <blockquote class="testimonial-blockquote">
        <span class="testimonial-text"><?php echo esc_html( $text ); ?></span>
        <span class="testimonial-author"><?php echo esc_html( $author ); ?></span>
        <span class="testimonial-role"><?php echo esc_html( $author_role ); ?></span>
    </blockquote>
    <div class="testimonial-image">
        <?php echo wp_get_attachment_image( $image, 'full' ); ?>
    </div>
</div>

The post How To Upgrade a Legacy Block to block.json With ACF 6 appeared first on ACF.

]]>
What’s New With ACF Blocks in ACF 6 https://www.advancedcustomfields.com/resources/whats-new-with-acf-blocks-in-acf-6/ Tue, 20 Sep 2022 09:19:09 +0000 https://www.advancedcustomfields.com/?post_type=resource&p=338311 ACF 6.0 includes ACF Blocks Version 2. This next generation of ACF Blocks brings us much closer to the native block experience, while still giving you the PHP based templating language you know as a WordPress developer. In the last few releases of WordPress, Gutenberg has made significant changes to all aspects of the block […]

The post What’s New With ACF Blocks in ACF 6 appeared first on ACF.

]]>
ACF 6.0 includes ACF Blocks Version 2. This next generation of ACF Blocks brings us much closer to the native block experience, while still giving you the PHP based templating language you know as a WordPress developer.

In the last few releases of WordPress, Gutenberg has made significant changes to all aspects of the block editor and block registration, and this has impacted ACF Blocks. We’ve not been able to move as fast as we would have liked on implementing changes to support these new features for back compatibility reasons. ACF 6.0 ships with a new block versioning system, allowing you to opt in to new versions which will change things like the markup and structure of ACF Blocks in both the backend and frontend, and may require updates to your theme to support.

Backwards Compatibility

The changes to blocks in ACF 6.0 are designed to be backwards compatible with ACF 5.x, with one notable exception. Due to the changes in block IDs, if you use parse_blocks() to read data from an ACF Block, it will no longer have a block ID present in that data. For workarounds to this, see our examples for adding a block ID back in.

Whilst we recommend users upgrade to block.json blocks to take advantage of new and upcoming WordPress features if they can, blocks registered with acf_register_block_type() will continue to work with ACF 6.

Block.json Support

Since WordPress 5.8, WordPress has supported – and recommended – that blocks are registered through a JSON configuration file. All of the WordPress Block Documentation was migrated to only show block.json configuration objects, and it became confusing for users to know what WordPress configuration options were usable in ACF PRO. ACF 6.0 introduces support for block.json registration of blocks with ACF.

For blocks registered through a block.json file, you can mark them as ACF Blocks by adding a new ACF configuration key to the JSON file, which contains the ACF specific configuration, for example:

{
    "name": "all-fields-block",
    "title": "All Fields Test Block",
    "description": "All fields test block",
    "style": "file:./all-fields-block.css",
    "category": "theme",
    "icon": "admin-comments",
    "apiVersion": 2,
    "keywords": [
        "test",
        "quote"
    ],
    "acf": {
        "mode": "preview",
        "renderTemplate": "all-fields-block.php",
        "postTypes": [ "post" ]
    },
    "styles": [
        { "name": "default", "label": "Default", "isDefault": true },
        { "name": "red", "label": "Red" },
        { "name": "green", "label": "Green" },
        { "name": "blue", "label": "Blue" }
    ],
    "supports": {
        "align": true,
        "anchor": true,
        "alignContent": false,
        "color": {
            "text": true,
            "background": true,
            "link": true
        },
        "alignText": true,
        "spacing": {
            "margin": [
                "top",
                "bottom"
            ],
            "padding": true
        },
        "typography": {
            "lineHeight": true,
            "fontSize": true
        },
        "fullHeight": true
    },
    "attributes": {
        "backgroundColor": {
            "type": "string",
            "default": "purple"
        }
    },
    "example": {
        "attributes": {
            "data": {
                "text": "This is an example text field",
                "text_area": "This is an example text area field"
            }
        }
    }
}

You then need to use the standard WordPress block registration function register_block_type, rather than acf_register_block_type:

register_block_type( 'path/to/folder/containing/block.json' );

If you don’t specify a namespace for your block like in our example above: "name": "all-fields-block",, ACF will automatically add it if it is an ACF block to become the equivalent of "name": "acf/all-fields-block" although you are now free to use your own namespace for your blocks.

This new method of registering blocks is now the recommended way to register a block, replacing the acf_register_block_type function.

The renderTemplate property should either be a full path to the template file, or relative to the location of the JSON file.

You can also now specify default attributes for your block, as shown in our example above in the attributes key. You can use this to set defaults for the WordPress provided block attributes, such as backgroundColor.

WordPress uses a camelCase format inside JSON files, and ACF adopts that too. Configuration keys such as render_template when used in acf_register_block_type need to be renderTemplate when used in JSON, likewise align_content and full_height should be alignContent and fullHeight for example. All previous configuration objects are supported, except for enqueue_style, enqueue_script and enqueue_assets for reasons explained below:

Block Asset Loading

When ACF Blocks was first introduced, it supported enqueueing block specific assets before WordPress had native support. Recent (and upcoming) updates to Gutenberg will require these assets to be loaded using specific WordPress asset configuration keys such as editorScript, script, editorStyle and style.

WordPress 5.9 users who use Block Styles may have noticed that the ACF registration of styles was not applied to those previews, which is why all ACF Block users should migrate their blocks to use those WordPress provided methods of asset loading.

The previous methods of asset loading in ACF Blocks will continue to work for the time being in acf_register_block_type, but as WordPress continues to make changes across releases we expect that to stop working – especially with the upcoming plan to isolate each block in an iframe.

Block ID Changes

ACF 6.0 will no longer save block IDs to the block comment of ACF Blocks. This affects all versions of ACF Blocks and means you can easily copy and paste blocks, simplify your post type templates, use your blocks in block patterns, or duplicate blocks without having any issues where block IDs clash.

This change shouldn’t affect most users, since we still generate a $block['id'] for your templates as we know some users use that for element IDs, but the format of those IDs will change. Just like in ACF 5.x, block IDs may be duplicated, but the number of situations where this can happen has been reduced significantly to one: if you have multiple blocks on the same page with the exact same data, attributes and context.

Due to the dynamic block ID generation, this change is backwards compatible for the majority of users, unless you use parse_blocks() to read ACF data, and rely on it returning a block ID.

Internally, ACF builds the new dynamic block ID from a md5 hash of the block’s attributes and context, but as WordPress block context may not be available in parse_blocks() so you will not reliably be able to match our dynamic block ID with the result of parse_blocks().

There are a couple of options to get around this. You could generate your own hash through a checksum of the sorted data attributes, use user entered anchors, or you could automatically generate an ID that is saved server side whenever a block is saved using the acf/pre_save_block filter.

Here’s a couple of possible examples, the first will automatically generate an anchor attribute if the user doesn’t enter one:

add_filter(
    'acf/pre_save_block',
    function( $attributes ) {
        if ( empty( $attributes['anchor'] ) ) {
            $attributes['anchor'] = 'acf-block-' . uniqid();
        }
        return $attributes;
    }
);

Alternatively, you could generate a new custom ID attribute which would be used on the front-end too if set, but note that it will be regenerated each time the block is saved, and this id will not be used in the backend editor:

add_filter(
    'acf/pre_save_block',
    function( $attributes ) {
        if ( empty( $attributes['id'] ) ) {
            $attributes['id'] = 'acf-block-' . uniqid();
        }
        return $attributes;
    }
);

Block Versioning Details

A new blockVersion (inside the ACF key in block.json) or acf_block_version (if using acf_register_block_type) key is now supported. For blocks registered via block.json, this defaults to 2. For acf_register_block_type, this defaults to 1.

For now, the main change between the two versions is the way <InnerBlocks /> markup is rendered. If you use block version 2 (which requires WordPress 5.8+), the container markup when using InnerBlocks will match between backend and frontend rendering, with any inner blocks wrapped in a new (and single level) <div class="acf-inner-blocks-container"></div> element. This can help make your CSS layouts easier, as you can be sure both render views have the same markup.

Whilst this wrapper must appear in the backend block editor, if you wish to disable it for the frontend, you can do that with a new filter, and optionally enable or disable it based on the block name:

add_filter( 'acf/blocks/wrap_frontend_innerblocks', 'acf_should_wrap_innerblocks', 10, 2 );
function acf_should_wrap_innerblocks( $wrap, $name ) {
    if ( $name == 'acf/test-block' ) {
        return true;
    }
    return false;
}

If you just wish to modify the class being used for the wrapper, instead of the default acf-inner-blocks-container you can simply add a class to your <InnerBlocks /> tag inside your block template, such as …

<InnerBlocks class="test-wrapper-class second-class" />

Block Registry Standardization

In previous versions of ACF PRO, a lightweight shim of the block configuration was registered in PHP, and then the full configuration registered in JS.

In this build of ACF, the full block configuration is now saved in the WordPress PHP Block Registry. This makes it easier for you to access details of the block configuration in your templates or render callbacks.

Block Context Changes

During development, we saw a conversation on twitter discussing the viability of using data from an ACF Block inside blocks contained in its <InnerBlocks> children.

We suspected this would be possible using block context, the system core blocks like the Query Loop block use to know the current post type.

ACF 6.0 introduces support for block context. This means if you want to pass ACF field data from a parent block to an InnerBlock, the InnerBlock will automatically reload the template, showing changes live as you make changes to fields in the parent. This support should also improve the reliability of blocks used inside Query Loop blocks.

To enable this, you need to tell WordPress to share the ACF data attribute to its child blocks with a custom key (acf/fields in our example) with providesContext in block.json:

"providesContext": {"acf/fields": "data"}

Then, to consume that context in an ACF block, you need to pass the context key in usesContext:

"usesContext": ["acf/fields"],

One thing to note: ACF Blocks use field keys in the block editor, and block data is only hydrated to field names upon save. Because of this, you should use $context['acf/fields']['field_key'] to get access the data inside your template if it exists before defaulting back to $context['acf/fields']['field_name'] if it doesn’t exist to support both backend and frontend output of context data.

You should also be aware that if you share ACF data through to your child blocks, the full ACF fields data will be sent back to the server in order to render previews. This may have performance implications for your blocks in the editor.

Blocks Without Fields

With Full Site Editing and the Query Loop block, it’s now much more likely you might want to use ACF Blocks without any specific fields attached to the block, for example because it’s showing post templating elements like titles or post content, or because you want to display fields which are added on the post being displayed, rather than from the block.

ACF 6.0 adds support for this by providing advisory text when a block has no fields assigned, whereas previously the block’s edit mode would simply be blank and not selectable.

Developers who wish to modify the default advisory text can do so with the new acf/blocks/no_fields_assigned_message filter which offers 2 parameters: the message to be displayed and the name of the block it will be displayed on.

Block Bug Fixes

We’ve also fixed a number of other bugs which apply to all versions of ACF Blocks.

  • The render_block_preview JavaScript event is now always fired every time a block preview is displayed, rather than just the first time.
  • Accordion fields inside an ACF block now are styled to match the native sidebar experience of the block editor.
  • ACF Block preloading now works again for blocks saved in edit mode, and for blocks without any saved field data (due to changes in block preloading for the removal of Block IDs, you may need to edit each block once before it can be preloaded.)
  • ACF Block edit forms will now behave correctly if they’re not visible when loaded.

The post What’s New With ACF Blocks in ACF 6 appeared first on ACF.

]]>