Wordpress Gutenberg custom columns block

2.7k views Asked by At

I'm trying to create a custom "columns" block for Gutenberg with ACF & Wordpress, so what I want is a easy to manage columns block, where user can input some number representating the amount of wanted columns (for example, 5) and a for will create that columns.

All worked as well, since I've tried to create multiple innerblocks... My code started this way:

acf_register_block_type([
    'name' => "theme-columns",
    'title' => __("Columns", "mytheme"),
    'description' => __("Columns", "mytheme"),
    'category' => "theme-blocks",
    'icon' => "grid-view",
    'render_callback' => [$this->renderers, "columns_renderer"],
    'mode' => "preview",
    'supports' => [
        'align' => true,
        'mode' => false,
        'jsx' => true
    ],
    'enqueue_assets' => function(){
        wp_enqueue_style("bootstrap");
    }
]);

And the columns_renderer function:

public function columns_renderer()
{
    $count = get_field("columns-count");
    ?>
    <div class="row row-cols-1 row-cols-md-3 row-cols-xl-5 justify-content-center">
        <?php for($i = 0; $i < $count; $i++): ?>
        <div class="col">
            <InnerBlocks />
        </div>
        <?php endfor; ?>
    </div>
    <?php
}

So, as (not) expected, it's not working because Gutenberg doesn't support multiple <InnerBlocks /> per block... Searching on the web, I've found some people talking about doing this like core/column block does, using some "hacks"... But I can't undestand what to do...

Can someone help me and give me some way to reach what I need?

Thank you!

-- UPDATE --

Tried to creating a "column" block and settings "columns" to only accept newly created "column" block, but still not working...

public function column()
{
    $this->register_block([
        'name' => "theme-column",
        'title' => __("Column", "mytheme"),
        'description' => __("Column", "mytheme"),
        'category' => "theme-blocks",
        'icon' => "columns",
        'render_callback' => [$this->renderers, "column_renderer"],
        'mode' => "preview",
        'supports' => [
            'align' => true,
            'mode' => false,
            'jsx' => true
        ],
        'enqueue_assets' => function(){
            wp_enqueue_style("theme-main");
        }
    ]);
}
public function column_renderer()
{
    ?>
    <InnerBlocks />
    <?php
}
public function columns()
{
    $this->register_block([
        'name' => "theme-columns",
        'title' => __("Columns", "mytheme"),
        'description' => __("Columns", "mytheme"),
        'category' => "theme-blocks",
        'icon' => "columns",
        'render_callback' => [$this->renderers, "columns_renderer"],
        'mode' => "preview",
        'supports' => [
            'align' => true,
            'mode' => false,
            'jsx' => true
        ],
        'enqueue_assets' => function(){
            wp_enqueue_style("theme-main");
        }
    ]);
}
public function columns_renderer()
{
    $allowedBlocks = ["acf/theme-column"];
    $template = array(
        array('acf/biore-column', []),
    );
    $column_count = get_field("columns-count");
    ?>
    <div class="row py-4">
        <?php for($i = 0; $i < $column_count; $i++): ?>
        <div class="col">
            <InnerBlocks allowedBlocks="<?= esc_attr(wp_json_encode($allowedBlocks)); ?>" template="<?= esc_attr(wp_json_encode($template)); ?>" />
        </div>
        <?php endfor; ?>
    </div>
    <?php
}
1

There are 1 answers

0
mrmryb On

I've actually been doing something very similar, although I am stuck on a different issue now.

It looks like you are still trying to create multiple InnerBlocks in your columns_renderer function. Instead, what I do is I create one InnerBlocks and fill it with a $template made up of multiple Column blocks.

I use templateLock="all" on the Row InnerBlocks to stop any new blocks being added there, something similar could also be achieved by using allowedBlocks depending on if you want to be able to add more Columns from the visual editor or not. I add templateLock="false" to the Columns InnerBlocks in order to overwrite the parent (Row) value and allow content to be added into it.

I use acf to create a cols field with a numeric range slider for the Row block, default value is 1 to create 1 column. As you move the slider, more Columns will be added.

Setting up the blocks:

acf_register_block_type(array(
  'name'                => 'row',
  'title'               => 'Row',
  'description'         => 'A row content block.',
  'category'            => 'formatting',
  'mode'                => 'preview',
  'supports'            => array(
    'align'             => true,
    'anchor'            => true,
    'customClassName'   => true,
    'mode'              => false,
    'jsx'               => true
  ),
  'render_callback' => 'block_row',
));

acf_register_block_type(array(
  'name'                => 'column',
  'title'               => 'Column',
  'description'         => 'A column content block.',
  'category'            => 'formatting',
  'mode'                => 'preview',
  'supports'            => array(
    'align'             => true,
    'anchor'            => true,
    'customClassName'   => true,
    'mode'              => false,
    'jsx'               => true,
  ),
  'render_callback' => 'block_col',
));

Outputting the Row content:

function block_row( $block ){
    $classes = '';
    if( !empty( $block['className'] ) ) {
        $classes .= sprintf( ' %s', $block['className'] );
    }
    if( !empty( $block['align'] ) ) {
        $classes .= sprintf( ' align%s', $block['align'] );
    }

    $cols = get_field('cols');
    if( empty( $cols ) ) {
        $cols = 1;
    }

    for( $x = 0; $x < $cols; $x++ ) {
        $template[] = array( 'acf/column' );
    }
?>
<div class="row-block row <?php echo esc_attr($classes); ?>">
    <h1>Row</h1>
    <?php 
    echo '<InnerBlocks template="' . esc_attr( wp_json_encode( $template ) ) . '" templateLock="all"/>';
    ?>
</div>
<?php
}

Outputting the Column block:

function block_col( $block ){
    $classes = '';
    if( !empty( $block['className'] ) ) {
        $classes .= sprintf( ' %s', $block['className'] );
    }
    if( !empty( $block['align'] ) ) {
        $classes .= sprintf( ' align%s', $block['align'] );
    }

    $template = array( array( 'core/paragraph', array(
            'content' => 'Enter content here',
        ) ) );
?>
<div class="col-block <?php echo esc_attr($classes); ?>">
    <h1>Col</h1>
    <?php 
    echo '<InnerBlocks template="' . esc_attr( wp_json_encode( $template ) ) . '" templateLock="false"/>';
    ?>
</div>
<?php
}