WordPress shortcodes make it easy to add complex code to the pages. Now that the new editor is here to stay and we are all getting used to the new editor, it is important to make sure that the code that was available as shortcodes should now be given to the users as blocks. Converting the shortcodes to blocks makes it more visual and much easier to use. It also gives the users a better experience as they do not need to remember all the shortcode parameters or keep referring the docs as they set up their sites.

Converting the shortcodes to Gutenberg blocks is fairly easy, but it does require a bit of initial setup to build the blocks.

Setting up your plugin for Gutenberg blocks

The first step is to setup the development environment. The WordPress documentation here gives a lot of information on setting up the initial JS build – https://developer.wordpress.org/block-editor/tutorials/javascript/js-build-setup/

This setup is almost the same whether you are starting to write a new plugin or adding Gutenberg to an existing plugin.

Once the setup is done, it is time to build our block that will work in place of the shortcode.

Top

The plugin folder structure

chamber-dashboard-business-directory
│   cdash-business-directory.php
│  
├───blocks
│       cdash_bd_block.php
│       cdash_block_functions.php
│       cdash_logo_gallery.php
│      
├───build
│       index.asset.php
│       index.js
│       index.js.map
│      
│          
├───shortcodes
│       business_directory_shortcode.php
│      
├───src
│   │   block.json
│   │   dependencies.js
│   │   edit.js
│   │   icon.js
│   │   index.js
│   │   inspectorControls.js
│   │  
│   ├───business_directory_block
│   │       edit.js
│   │       icon.js
│   │       index.js
│   │       inspectorControls.js
│   │      
│   └───logo_gallery_block
│           edit.js
│           index.js
│          
           

After setting up the initial JS build, you will now have the folders /src and /build in your plugins root folder. The src folder will hold all of your block code. It is best to create another folder blocks in the root folder of your plugin for all the block related functions. This will keep our blocks code separate.

Since, I wanted to create multiple blocks in the Business Directory plugin, I created folders inside the src block. The business_directory_block and the logo_gallery_block have the code for the respective blocks. Creating a folder for each block is better to keep the code separate and less cluttered.

Each folder has an index.js which includes the actual block code. Since @wordpress/scripts package is going to look for our block code in the /src/index.js file, we need to add the index.js files for each block here.

import './business_directory_block/index.js';
import './logo_gallery_block/index.js';

Using the shortcode function

The easiest way to convert the shortcode to a block is to pass all the block attributes to the same function that is used for the shortcode.

In the Chamber Dashboard Business Directory plugin we have a shortcode that displays all the business listings. There are many parameters used with the shortcode to control what and how the listings are displayed.

Since we have all the parameters and the styles working like we want in the shortcode, it was just easier to use the shortcode function for the block as well. Also, if there are users who are still using the classic editor, they need the shortcode to exist. So, using the same function means that it is easier to maintain both the shortcode and the block.

Creating the block

Inside the business_directory_block/index.js, add the code to create the block using registerBlockType.

/**
 * Block dependencies
 */

import edit from './edit';

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { dateI18n, format, __experimentalGetSettings } from '@wordpress/date';
import { setState } from '@wordpress/compose';

registerBlockType( 'cdash-bd-blocks/business-directory', {
    title: 'Display Business Directory',
    icon: 'store',
    category: 'cd-blocks',
    attributes: {
        format: {
            type: 'string',
            default: 'grid3',
        },
        perpage:{
            type: 'number',
            default: -1,
        },
        categoryArray:{
            type: 'array',
            default: [],
        },
        category:{
            type: 'string',
            default: '',
        },
    },
    edit: edit,
    save() {
        // Rendering in PHP
        return null;
    },
} );

While creating the block, it is important to remember that all the parameters or attributes of the shortcode should be added in the block attributes section. Since we are using the same function as the shortcode, the attribute names should be the same in both the shortcode and the block.

The edit function

Next, we need to add the edit function for the block in edit.js. Here are the necessary dependencies for the edit function

import ServerSideRender from '@wordpress/server-side-render';
import { __ } from '@wordpress/i18n';
import { SelectControl, 
    Toolbar,
    Button,
    Tooltip,
    PanelBody,
    PanelRow,
    FormToggle,
    ToggleControl,
    ToolbarGroup,
    Disabled, 
    RadioControl,
    RangeControl,
    FontSizePicker } from '@wordpress/components';

    import {
        RichText,
        AlignmentToolbar,
        BlockControls,
        BlockAlignmentToolbar,
        InspectorControls,
        InnerBlocks,
        withColors,
        PanelColorSettings,
        getColorClassName
    } from '@wordpress/editor'
    ;
import { withSelect, widthDispatch } from '@wordpress/data';

const {
    withState
} = wp.compose;

The edit part of the block with inspector controls and server side render.

const formatOptions = [
    { label: 'List', value: 'list' },
    { label: '2 Columns', value: 'grid2' },
    { label: '3 Columns', value: 'grid3' },
    { label: '4 Columns', value: 'grid4' },
    { label: 'Responsive', value: 'responsive' },
 ];
const categoryOptions = [
    { label: 'Select one or more categories', value: null }
];

wp.apiFetch({path: "/wp/v2/business_category?per_page=100"}).then(posts => {
    jQuery.each( posts, function( key, val ) {
        categoryOptions.push({label: val.name, value: val.slug});
    });
}).catch( 

)
const edit = props => {
    const {attributes: {format, categoryArray, category, perpage, }, className, setAttributes } = props;

    const setDirectoryLayout = format => {
        props.setAttributes( { format } );
    };

    const setCategories = categoryArray => {
        props.setAttributes( { categoryArray} );
        console.log(categoryArray);
    };

    const inspectorControls = (
        <InspectorControls key="inspector">
            <PanelBody title={ __( 'Formatting Options' )}>
                <PanelRow>
                    <SelectControl
                        label="Directory Layout"
                        value={ format }
                        options= { formatOptions }
                        /*onChange={ ( nextValue ) =>
                            setAttributes( { format:  nextValue } )
                        }*/
                        onChange = {setDirectoryLayout}
                    />
                </PanelRow>
                <PanelRow>
                <RangeControl
                    label="Number of Businesses per page"
                    min={-1 }
                    max={ 50 }
                    onChange={ ( value ) => setAttributes( { perpage: value} ) }
                    value={ perpage }
                    initialPosition = { -1 }
                    allowReset = "true"
                />
                </PanelRow>
            <PanelBody title={ __( 'Limit By:' )} initialOpen={ false }>
                <PanelRow>
                    <SelectControl 
                        multiple
                        label = "Categories"
                        value = {categoryArray}
                        options = {categoryOptions}
                        onChange = {setCategories}
                    />
                </PanelRow>
            </PanelBody>
        </InspectorControls>
    );
    return [
        <div className={ props.className }>
            <ServerSideRender
                block="cdash-bd-blocks/business-directory"
                attributes = {props.attributes}
            />
            { inspectorControls }
            <div className="businesslist">
                
            </div>
        </div>
    ];
};

export default edit;

The ServerSideRender allows us to render a preview of the dynamic block in the editor. Since it is best to use this for blocks that render heavily on existing PHP functions, it is perfect for this particular block. More info on ServerSideRender can be found here.

Inspector Controls

The edit function has the inspector controls for the block attributes. These controls can be used to modify the shortcode attributes. The block code shown above only shows four attributes for the purpose of this post. The actual shortcode has a lot more parameters.

There is an option to select the format which determines the number of columns for the business directory layout, an option to select one or more categories (which are custom taxonomies in this case) and an option to limit the number of businesses shown per page.

The format and the perpage options are simple dropdowns which pass the selected option to the shortcode function.

The category attribute in the shortcode is a string where you can pass multiple category slugs separated with a comma. To give the option to choose multiple categories, we need to have an array. So, I created a new attribute called ‘categoryArray’ which is an empty array by default. This stores the multiple selected categories.

In the php callback function, the values from the categoryArray are passed to the category attribute as comma separated string.

ServerSideRender uses the block’s call back function. Since we are passing attributes to the ServerSideRender, we need to register the block and its attributes in php.

PHP Callback function

//Business Directory Shortcode rendering
if ( function_exists( 'register_block_type' ) ) {
    // Hook server side rendering into render callback
  register_block_type(
      'cdash-bd-blocks/business-directory', [
          'render_callback' => 'cdash_bus_directory_block_callback',
          'attributes'  => array(
              'cd_block'  => array(
                  'type'  => 'string',
                  'default' => 'yes',
              ),
              'format'    => array(
                  'type'  => 'string',
                  'default'   => 'grid3',
              ),
              'categoryArray'    => array(
                  'type'  => 'array',
                  'default'   => [],
                  'items'   => [
                      'type' => 'string',
                  ],
              ),
              'category'    => array(
                  'type'  => 'string',
                  'default'   => '',
              ),
          ),
      ]
  );
}

function cdash_bus_directory_block_callback($attributes){
  if(isset($attributes['categoryArray']) &amp;&amp; '' != $attributes['categoryArray']){
      $attributes['category'] = $attributes['categoryArray'];
  }

  $business_listings = cdash_business_directory_shortcode($attributes);

  return $business_listings;

}

The cdash_business_directory_shortcode is the function that renders the directory listings through the shortcode. We are just calling the same function and passing in all the attributes from the block into the function.

This will display the block with the business listings with the default values. When one of the values is changed using the inspector controls in the block settings, those values are passed to the shortcode function and the front end display is modified accordingly.

Working Business Directory block:

Leave a Reply

%d bloggers like this: