Register an ACF block with modern practices

here’s what I have been using:

/**
 * Register ACF Blocks.
 */
function theme_blocks_init()
{
    // Directory containing the blocks, within the 'resources/views' directory.
    $directory = resource_path('views') . '/blocks/';

    // Iterate over the directory provided and look for blocks.
    $block_directory = new DirectoryIterator($directory);

    foreach ($block_directory as $block) {
        if ($block->isDir() && !$block->isDot()) {
            register_block_type($block->getRealpath());
        }
    }
}

add_action('init', __NAMESPACE__ . '\\theme_blocks_init');

all your blocks will go in /resources/views/blocks/, each in their own directory

so for example, a carousel block would go in /resources/views/blocks/carousel/

each block has a block.json, along with a corresponding blade file - the file has to be named the same as the block slug, so in this example it would be ‘carousel.blade.php’

in the block.json:

  "acf": {
    "mode": "auto", // or whatever
    "renderCallback": "\\App\\blade_render_callback",
    "postTypes": ["page"], // or whatever
    "blockVersion": 2
  },

then your callback function:

/**
 * Callback for rendering Blade templates.
 * Name of the Blade template at resources/views/blocks/{title}.blade.php
 */
function blade_render_callback($block, string $content = '', bool $is_preview = false, int $post_id = 0)
{
    $slug                = str_replace(THEME_BLOCK_SLUG . '/', '', $block['name']);
    $block['slug']       = $slug;

        echo \Roots\view('blocks.' . $block['slug'] . '.' . $block['slug'], [ 'block' => $block ])->render();
}

this is a truncated example, but it should work barebones, if you define the THEME_BLOCK_SLUG constant. That is going to be the block namespace. So in this example in the block.json the name is ‘jf/carousel’ - therefore the THEME_BLOCK_SLUG would be ‘jf’. You can hardcode it if you want; I just have a bunch of theme constants set earlier in the file that I use.