简体   繁体   中英

How can I convert a static gutenberg block into a dynamic block registering it using PHP?

I'm trying to convert a static Gutenberg block I created for WordPress into a dynamic block. I've looked for other solutions, but have been unsuccessful. This question ( What is the correct way to convert a static gutenberg block created using @wordpress/create-block into a dynamic block registering it using PHP? ) is very similar to what I am seeking, but my static block wasn't made using @wordpress/create-block and after doing all of the suggested steps I'm seeing what seems to be deprecation issues.

class-hello-tools-custom-blocks.php (registering the block)

The static version looks like this:

register_block_type( 'hello-tools/custom-cta', array(
    'style' => $this->plugin_slug . '-public',
    'editor_style' => 'hello-gutenberg-admin',
    'editor_script' => $this->plugin_slug . '-custom-blocks',
) );

The dynamic changes like so:

register_block_type( __DIR__ . '/src/js/blocks/custom-cta/custom-cta-block.json', array(
    'style' => $this->plugin_slug . '-public',
    'editor_style' => 'hello-gutenberg-admin',
    'editor_script' => $this->plugin_slug . '-custom-blocks',
    'render_callback' => array( $this, 'hello_tools_custom_cta_callback' )
) );

and the callback function in that same plugin file looks like:

public function hello_tools_custom_cta_callback( $attributes, $content ) {
    ob_start();
    require plugin_dir_path( __DIR__ ) . 'includes/partials/block-callbacks/hello-custom-cta-callback.php';
    $output = ob_get_contents();
    ob_end_clean();
    return $output;
}

custom-cta-block.json (defining block attributes)

As referenced in the register_block_type() above, this is the block.json file.

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "hello-tools/custom-cta",
    "title": "Custom CTA",
    "category": "hello-blocks",
    "icon": "align-center",
    "description": "Create call to action element with photo image, text, and button link",
    "keywords": [ "call", "action" ],
    "version": "1.0.1",
    "textdomain": "hello-tools",
    "editorScript": "hello-tools-custom-blocks",
    "editorStyle": "hello-gutenberg-admin",
    "style": "hello-tools-public",
    "attributes": {
        "switchOrientation": {
            "type": "boolean",
            "default": false
        },
        "imageID": {
            "type": "integer"
        },
        "imageURL": {
            "type": "string"
        },
        "imageALT": {
            "type": "string",
            "default": ""
        },
        "imageBgColor": {
            "type": "string",
            "default": "orange"
        },
        "hideCTAImageOnMobile": {
            "type": "boolean",
            "default": false
        },
        "renderWithoutImageBg": {
            "type": "boolean",
            "default": false
        },
        "imageRatio": {
            "type": "string",
            "default": "golden"
        },
        "textBgColor": {
            "type": "string",
            "default": "faint-gray"
        },
        "hasIcon": {
            "type": "boolean",
            "default": false
        },
        "iconID": {
            "type": "integer"
        },
        "iconURL": {
            "type": "string"
        },
        "iconALT": {
            "type": "string",
            "default": ""
        },
        "hasSuperHeading": {
            "type": "boolean",
            "default": false
        },
        "superHeading": {
            "type": "string"
        },
        "headline": {
            "type": "string"
        },
        "blurb": {
            "type": "string"
        },
        "ctaButtonText": {
            "type": "string"
        },
        "ctaLinkURL": {
            "type": "string"
        },
        "textClasses": {
            "type": "string",
            "default": "hello-custom-cta__description flex-fill hello-flex hello-flex--fixed-size align-self-stretch align-items-center p-3 p-sm-4 p-lg-5"
        },
        "blockClasses": {
            "type": "string",
            "default": "hello-block--fullwidth px-sm-3 mb-5 mb-md-6 mb-lg-7 mb-7"
        }
    },
    "supports": {
        "multiple": true
    },
    "example": {
        "attributes": {
            "headline": "This is a custom CTA!",
            "blurb": "<p>Add a catchy headline and body text to capture people's attention</p>",
            "ctaButtonText": "Click here"
        }
    }
}

custom-cta.js

The original save() function has been removed to tell Wordpres that it should be rendered dynamically, but the previous deprecation versions have been left in tact.

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { InspectorControls, MediaUpload, MediaUploadCheck, RichText, URLInput } from '@wordpress/blockEditor';
import { Button, PanelBody, TextControl, TextareaControl, ToggleControl, SelectControl, IconButton } from '@wordpress/components';
import { omit } from 'lodash';
import settings from './custom-cta-block.json';
const blockAttributes = settings.attributes;

registerBlockType( settings, {

  edit: ( props ) => {

    const { attributes: { switchOrientation, imageID, imageURL, imageALT, imageBgColor, imageRatio, hideCTAImageOnMobile, textBgColor, hasIcon, iconID, iconURL, iconALT, hasSuperHeading, superHeading, headline, blurb, ctaButtonText, ctaLinkURL, blockClasses, textClasses, renderWithoutImageBg }, setAttributes, className, isSelected } = props;
    const ctaButtonPlaceholderText = __( "Button text", "hello-tools" );
    const flexClasses = `hello-flex hello-wrap--content align-items-center ${switchOrientation && 'switch-orientation'}`;

    const onImageSelect = ( imageObj ) => {

      // use the medium_large image size of it exists, otherwise use the full-size image
      const imageObjURL = ( imageObj.sizes.medium_large ) ? imageObj.sizes.medium_large.url : imageObj.sizes.full.url;
      
      // set the image attributes
      setAttributes( { imageID: imageObj.id } );
      setAttributes( { imageURL: imageObjURL } );
      setAttributes( { imageALT: imageObj.alt } );
    
    }

    const onImageBgChange = ( value ) => {
      setAttributes( { imageBgColor: value } );
      setAttributes( { renderWithoutImageBg: ( value == 'none') ? true : false } );
    }

    const onIconSelectorChange = ( value ) => {
      setAttributes( { hasIcon: value } );
      
      // clear icon image attributes if show icon toggle is changed to false
      if ( !value ) {
        setAttributes( { iconID: '' } );
        setAttributes( { iconURL: '' } );
        setAttributes( { iconALT: '' } );
      }

    }

    const onIconSelect = ( imageObj ) => {
      setAttributes( { iconID: imageObj.id } );

      if ( imageObj.sizes.thumbnail ) {
        setAttributes( { iconURL: imageObj.sizes.thumbnail.url } );  
      } else {
        setAttributes( { iconURL: imageObj.sizes.full.url } );
      }

      setAttributes( { iconALT: imageObj.alt } );
    }

    return (
      <div className="hello-custom-cta-editor p-0 mb-0">
        <InspectorControls>
          <PanelBody
            title={ __( 'Image settings', 'hello-tools' ) }
            initialOpen={ true }
          >
            { imageID && __( 'You can change the CTA image here', 'hello-tools' ) }
            { imageID && (
              <div style={{ margin: '0 0 15px 0', }}>
                <MediaUploadCheck>
                  <MediaUpload
                    onSelect={ onImageSelect }
                    allowedTypes="image"
                    value={ imageID }
                    render={ ( { open } ) => (
                      <Button className="hello-components-button" onClick={ open }>
                        { __( 'Choose new image', 'hello-tools' ) }
                      </Button>
                    ) }
                  />
                </MediaUploadCheck>
              </div>
            ) }
            <SelectControl
              label={ __( 'Image background color', 'hello-tools' ) }
              value={ imageBgColor }
              onChange={ onImageBgChange }
              options={ [
                  { value: 'orange', label: __( 'Orange', 'hello-tools' ) },
                  { value: 'blue', label: __( 'Blue', 'hello-tools' ) },
                  { value: 'powder-blue', label: __( 'Light blue', 'hello-tools' ) },
                  { value: 'faint-gray', label: __( 'Gray', 'hello-tools' ) },
                  { value: 'green', label: __( 'Green', 'hello-tools' ) },
                  { value: 'none', label: __( 'No background color', 'hello-tools' ) },
              ] }
            />
            <SelectControl
              label={ __( 'Image ratio', 'hello-tools' ) }
              value={ imageRatio }
              onChange={ value => setAttributes( { imageRatio: value } ) }
              options={ [
                  { value: 'golden', label: '16:9' },
                  { value: 'half', label: '1:1' },
              ] }
            />
            <ToggleControl
              label={ __( 'Hide image on mobile phones?', 'hello-tools' ) }
              checked={ hideCTAImageOnMobile }
              onChange={ value => setAttributes( { hideCTAImageOnMobile: value } ) }
            />
            <ToggleControl
              label={ __( 'Move photo to the right of the text?', 'hello-tools' ) }
              help='The default is to have the photo to the left of the text, but you can switch it here.'
              checked={ switchOrientation }
              onChange={ value => setAttributes( { switchOrientation: value } ) }
            />
          </PanelBody>
          <PanelBody
            title={ __( 'Text settings', 'hello-tools' ) }
            initialOpen={ true }
          >
            <SelectControl
              label={ __( 'Background color', 'hello-tools' ) }
              value={ textBgColor }
              onChange={ value => setAttributes( { textBgColor: value } ) }
              options={ [
                  { value: 'faint-gray', label: __( 'Gray', 'hello-tools' ) },
                  { value: 'white', label: __( 'White', 'hello-tools' ) },
              ] }
            />
            <ToggleControl
              label={ __( 'Show icon above the text?', 'hello-tools' ) }
              checked={ hasIcon }
              onChange={ onIconSelectorChange }
            />
            { hasIcon && (
              <div style={{ margin: '-15px 0 15px 0', }}>
                <MediaUploadCheck>
                  <MediaUpload
                    onSelect={ onIconSelect }
                    allowedTypes="image"
                    value={ iconID }
                    render={ ( { open } ) => (
                      <Button className="hello-components-button" onClick={ open }>
                        { !iconID && __( 'Choose icon image', 'hello-tools' ) }
                        { iconID && __( 'Replace icon image', 'hello-tools' ) }
                      </Button>
                    ) }
                  />
                </MediaUploadCheck>
              </div>
            ) }
            <ToggleControl
              label={ __( 'Show super heading?', 'hello-tools' ) }
              checked={ hasSuperHeading }
              onChange={ value => setAttributes( { hasSuperHeading: value } ) }
            />
          </PanelBody>
        </InspectorControls>
        <div className={ flexClasses }>
          { renderWithoutImageBg && imageURL && (
          <div className={ `hello-custom-cta__image-wrap ${imageRatio} no-padding hello-flex align-self-stretch align-items-center` }>
            <div className="objectfit__container w-100 h-100">
              <img className="hello-custom-cta__image objectfit__image w-100 h-100" src={ imageURL }  alt={ imageALT } />
            </div>
          </div>
          ) }
          { !renderWithoutImageBg && imageURL && (
          <div className={ `hello-custom-cta__image-wrap ${imageRatio} hello-bg--${imageBgColor} hello-flex align-self-stretch align-items-center p-sm-4 p-lg-5` }>
            <img className="hello-custom-cta__image" src={ imageURL } alt={ imageALT } />
          </div>
          ) }
          { !renderWithoutImageBg && !imageID && (
            <MediaUploadCheck>
              <MediaUpload
                onSelect={ onImageSelect }
                allowedTypes="image"
                value={ imageID }
                render={ ( { open } ) => (
                  <Button onClick={ open }>
                    { __( 'Choose Image', 'hello-tools' ) }
                  </Button>
                ) }
              />
            </MediaUploadCheck>
          ) }
          <div className={ `${textClasses} hello-bg--${textBgColor}` }>
            <div>
              { hasIcon && iconID && (
                <div className="flex-item--fullwidth">
                  <img className="hello-custom-cta__icon circle d-block mx-auto" src={ iconURL } alt={ iconALT } />
                </div>
              ) }
              { hasSuperHeading && (
                <TextControl
                  className="hello-custom-cta__superheading hello-interactive-input"
                  value={ superHeading }
                  onChange={ value => setAttributes( { superHeading: value } ) }
                  placeholder={ __( 'Add super heading here (optional)', 'hello-tools' ) }
                  allowedFormats={ [] }
                />
              ) }
              <TextareaControl
                className="hello-custom-cta__headline w-100 hello-interactive-input font-weight-bold"
                value={ headline }
                onChange={ value => setAttributes( { headline: value } ) }
                placeholder={ __( 'Add CTA headline here', 'hello-tools' ) }
                allowedFormats={ [] }
                rows="2"
              />
              { ( isSelected || ( blurb && blurb != '<p></p>' ) ) && ( 
                <div className="w-100">
                  <RichText
                    tagName="div"
                    multiline="p"
                    className="hello-custom-cta__blurb font-weight-light mt-0 w-100"
                    translate-name="blurb"
                    onChange={ value => setAttributes( { blurb: value } ) }
                    placeholder={ __( 'Subtext goes here (optional)', 'hello-tools' ) }
                    value={ blurb }
                    allowedFormats={ [ 'core/bold', 'core/italic' ] }
                    focusOnInsert={ false }
                  />
                </div>
              ) }
              <div className="hello-tools-editable-btn-text-wrapper">
                <div className="hello-inline-flex">
                  <RichText
                    tagName="div"
                    placeholder={ ctaButtonPlaceholderText }
                    onFocus={ (e) => e.target.placeholder = "" } 
                    value={ ctaButtonText }
                    onChange={ value => setAttributes( { ctaButtonText: value } ) }
                    className="hello-tools-editable-btn-text mt-4 hello-btn hello-btn--blue"
                    withoutInteractiveFormatting
                    allowedFormats={ [] }
                  />
                </div>
              </div>
            </div>
          </div>
        </div>
        { isSelected && (
          <div className="hello-block-post-selector">  
            <form
              key={ "form-link" }
              onSubmit={ ( event ) => event.preventDefault() }
              className="hello-flex hello-bg--white">
              <URLInput
                value={ ctaLinkURL }
                onChange={ value => setAttributes( { ctaLinkURL: value } ) }
                autoFocus={ false }
              />
              <IconButton
                className="hello-bg--white"
                icon={ "editor-break" }
                label={ __( "Apply", "hello-tools" ) }
                type={ "submit" }
              />
            </form>
          </div>
        ) }
      </div>
    );

  },

  save() {
    return null; // Data is rendered via PHP callback
  },

  deprecated: [
    {
      attributes: { ...blockAttributes },

      save( { attributes } ) {

        const { switchOrientation, imageID, imageURL, imageALT, imageBgColor, imageRatio, hideCTAImageOnMobile, textBgColor, hasIcon, iconID, iconURL, iconALT, hasSuperHeading, superHeading, headline, blurb, ctaButtonText, ctaLinkURL, blockClasses, textClasses, renderWithoutImageBg } = attributes;
        const flexClasses = `hello-flex hello-wrap--content align-items-center ${switchOrientation && 'switch-orientation'}`;
        const superHeadingExtraClasses = ( hasIcon && iconID ) ? 'mt-2 ' : '';
        const hideImageOnPhones = ( hideCTAImageOnMobile ) ? ' d-upto-md-none' : '';

        return (
          <div className={ blockClasses }>
            <div className="hello-wrap--narrow">
              <div className={ flexClasses }
                >
                { renderWithoutImageBg && imageURL && (
                <div className={ `hello-custom-cta__image-wrap ${imageRatio} hello-flex align-self-stretch align-items-center${hideImageOnPhones}` }>
                  <div className="objectfit__container w-100 h-100">
                    <img className="hello-custom-cta__image objectfit__image w-100 h-100" src={ imageURL }  alt={ imageALT } />
                  </div>
                </div>
                ) }
                { !renderWithoutImageBg && imageURL && (
                <div className={ `hello-custom-cta__image-wrap ${imageRatio} hello-bg--${imageBgColor} hello-flex align-self-stretch align-items-center p-sm-4 p-lg-5${hideImageOnPhones}` }>
                  <img className="hello-custom-cta__image" src={ imageURL } alt={ imageALT } />
                </div>
                ) }
                <div className={ `${textClasses} hello-bg--${textBgColor}` }>
                  <div>
                    { iconID && (
                      <div className="flex-item--fullwidth">
                        <img className="hello-custom-cta__icon circle d-block mx-auto" src={ iconURL } alt={ iconALT } />
                      </div>
                    ) }
                    { hasSuperHeading && superHeading && (
                      <div translate-name="custom-cta__superheading" className={`${superHeadingExtraClasses}hello-custom-cta__superheading hello-1rem-text font-weight-bold text-uppercase hello-text--orange`}>
                        <span>{ superHeading }</span>
                      </div>
                    ) }
                    <div translate-name="custom-cta__headline" className="hello-custom-cta__headline h2 font-weight-bold my-2 my-md-3">
                      { headline }
                    </div>
                    { ( blurb && blurb != '<p></p>' ) && ( 
                      <RichText.Content 
                        className="hello-custom-cta__blurb" 
                        tagName="div" 
                        value={ blurb } 
                      />
                    ) }
                    { ctaButtonText && ctaLinkURL && (
                      <a href={ ctaLinkURL } translate-name="custom-cta__btn-link" className="hello-custom-cta__btn-link mb-3 mb-md-0 btn hello-btn hello-btn--blue">
                        { ctaButtonText }
                      </a>
                    ) }
                  </div>
                </div>
              </div>
            </div>          
          </div>
        )

      }

    },
    {
      attributes: { 
        ...blockAttributes, 
        blockClasses: {
          type: 'string',
          default: 'hello-block--fullwidth px-sm-3 mb-7',
        },
      },

      save( { attributes } ) {

        const { switchOrientation, imageID, imageURL, imageALT, imageBgColor, imageRatio, hideCTAImageOnMobile, textBgColor, hasIcon, iconID, iconURL, iconALT, hasSuperHeading, superHeading, headline, blurb, ctaButtonText, ctaLinkURL, blockClasses, textClasses, renderWithoutImageBg } = attributes;
        const flexClasses = `hello-flex hello-wrap--content align-items-center ${switchOrientation && 'switch-orientation'}`;
        const superHeadingExtraClasses = ( hasIcon && iconID ) ? 'mt-2 ' : '';
        const hideImageOnPhones = ( hideCTAImageOnMobile ) ? ' d-upto-md-none' : '';

        return (
          <div className={ blockClasses }>
            <div className="hello-wrap--narrow">
              <div className={ flexClasses }
                >
                { renderWithoutImageBg && imageURL && (
                <div className={ `hello-custom-cta__image-wrap ${imageRatio} hello-flex align-self-stretch align-items-center${hideImageOnPhones}` }>
                  <div className="objectfit__container w-100 h-100">
                    <img className="hello-custom-cta__image objectfit__image w-100 h-100" src={ imageURL }  alt={ imageALT } />
                  </div>
                </div>
                ) }
                { !renderWithoutImageBg && imageURL && (
                <div className={ `hello-custom-cta__image-wrap ${imageRatio} hello-bg--${imageBgColor} hello-flex align-self-stretch align-items-center p-sm-4 p-lg-5${hideImageOnPhones}` }>
                  <img className="hello-custom-cta__image" src={ imageURL } alt={ imageALT } />
                </div>
                ) }
                <div className={ `${textClasses} hello-bg--${textBgColor}` }>
                  <div>
                    { iconID && (
                      <div className="flex-item--fullwidth">
                        <img className="hello-custom-cta__icon circle d-block mx-auto" src={ iconURL } alt={ iconALT } />
                      </div>
                    ) }
                    { hasSuperHeading && superHeading && (
                      <div translate-name="custom-cta__superheading" className={`${superHeadingExtraClasses}hello-custom-cta__superheading hello-1rem-text font-weight-bold text-uppercase hello-text--orange`}>
                        <span>{ superHeading }</span>
                      </div>
                    ) }
                    <div translate-name="custom-cta__headline" className="hello-custom-cta__headline h2 font-weight-bold my-2 my-md-3">
                      { headline }
                    </div>
                    { ( blurb && blurb != '<p></p>' ) && ( 
                      <RichText.Content 
                        className="hello-custom-cta__blurb" 
                        tagName="div" 
                        value={ blurb } 
                      />
                    ) }
                    { ctaButtonText && ctaLinkURL && (
                      <a href={ ctaLinkURL } translate-name="custom-cta__btn-link" className="hello-custom-cta__btn-link mb-3 mb-md-0 btn hello-btn hello-btn--blue no-external-icon same-window">
                        { ctaButtonText }
                      </a>
                    ) }
                  </div>
                </div>
              </div>
            </div>          
          </div>
        )

      }

    }
  ],

} );

hello-custom-cta-callback.php (handles the actual PHP rendering, referenced in the callback function)

<?php
global $post;

$plugin_slug = hello_get_plugin_slug();
$is_admin = ( is_admin() ) ? true : false;

$switchOrientation = ( array_key_exists( 'switchOrientation', $attributes ) ) ? $attributes['switchOrientation'] : false;
$imageID = ( array_key_exists( 'imageID', $attributes ) ) ? $attributes['imageID'] : '';
$imageURL = ( array_key_exists( 'imageURL', $attributes ) ) ? $attributes['imageURL'] : '';
$imageALT = ( array_key_exists( 'imageALT', $attributes ) ) ? $attributes['imageALT'] : '';
$imageBgColor = ( array_key_exists( 'imageBgColor', $attributes ) ) ? $attributes['imageBgColor'] : 'orange';
$hideCTAImageOnMobile = ( array_key_exists( 'hideCTAImageOnMobile', $attributes ) ) ? $attributes['hideCTAImageOnMobile'] : false;
$renderWithoutImageBg = ( array_key_exists( 'renderWithoutImageBg', $attributes ) ) ? $attributes['renderWithoutImageBg'] : false;
$imageRatio = ( array_key_exists( 'imageRatio', $attributes ) ) ? $attributes['imageRatio'] : 'golden';
$textBgColor = ( array_key_exists( 'textBgColor', $attributes ) ) ? $attributes['textBgColor'] : 'faint-gray';
$hasIcon = ( array_key_exists( 'hasIcon', $attributes ) ) ? $attributes['hasIcon'] : false;
$iconID = ( array_key_exists( 'iconID', $attributes ) ) ? $attributes['iconID'] : '';
$iconURL = ( array_key_exists( 'iconURL', $attributes ) ) ? $attributes['iconURL'] : '';
$iconALT = ( array_key_exists( 'iconALT', $attributes ) ) ? $attributes['iconALT'] : '';
$hasSuperHeading = ( array_key_exists( 'hasSuperHeading', $attributes ) ) ? $attributes['hasSuperHeading'] : false;
$superHeading = ( array_key_exists( 'superHeading', $attributes ) ) ? $attributes['superHeading'] : '';
$headline = ( array_key_exists( 'headline', $attributes ) ) ? $attributes['headline'] : '';
$blurb = ( array_key_exists( 'blurb', $attributes ) ) ? $attributes['blurb'] : '';
$ctaButtonText = ( array_key_exists( 'ctaButtonText', $attributes ) ) ? $attributes['ctaButtonText'] : '';
$ctaLinkURL = ( array_key_exists( 'ctaLinkURL', $attributes ) ) ? $attributes['ctaLinkURL'] : '';
$textClasses = ( array_key_exists( 'textClasses', $attributes ) ) ? $attributes['textClasses'] : 'hello-custom-cta__description flex-fill hello-flex hello-flex--fixed-size align-self-stretch align-items-center p-3 p-sm-4 p-lg-5';
$blockClasses = ( array_key_exists( 'blockClasses', $attributes ) ) ? $attributes['blockClasses'] : 'hello-block--fullwidth px-sm-3 mb-5 mb-md-6 mb-lg-7 mb-7';
$flexClasses = 'hello-flex hello-wrap--content align-items-center';
$flexClasses = ($switchOrientation) ? $flexClasses . ' switch-orientation' : $flexClasses;
$superHeadingExtraClasses = ( $hasIcon && $iconID ) ? 'mt-2 ' : '';
$hideImageOnPhones = ( $hideCTAImageOnMobile ) ? ' d-upto-md-none' : '';

$is_rtl = ( apply_filters( 'wpml_is_rtl', NULL) ) ? true : false;
if ( is_object( $post ) && get_post_meta( $post->ID, '_icl_lang_duplicate_of', true ) ) { 
    $is_rtl = false; // if this is on a duplicate page, keep the content LTR
}

echo '<pre>'.print_r($attributes,true).'</pre>';
?>

<div class="<?= $blockClasses; ?>">
    <div class="hello-wrap--narrow">
        <div class="<?= $flexClasses; ?>">
            <?php if ($renderWithoutImageBg && $imageURL) : ?>
                <div class="hello-custom-cta__image-wrap <?= $imageRatio; ?> hello-flex align-self-stretch align-items-center<?= $hideImageOnPhones; ?>">
                    <div class="objectfit__container w-100 h-100">
                        <img class="hello-custom-cta__image objectfit__image w-100 h-100" src="<?= $imageURL; ?>"  alt="<?= $imageALT; ?>" />
                    </div>
                </div>
            <?php elseif ( !$renderWithoutImageBg && $imageURL ) : ?>
                <div class="hello-custom-cta__image-wrap <?= $imageRatio; ?> hello-bg--<?= $imageBgColor; ?> hello-flex align-self-stretch align-items-center p-sm-4 p-lg-5<?= $hideImageOnPhones; ?>">
                    <img class="hello-custom-cta__image" src="<?= $imageURL; ?>" alt="<?= $imageALT; ?>" />
                </div>
            <?php endif; ?>
            <div class="<?= $textClasses; ?> hello-bg--<?= $textBgColor; ?>">
              <div>
                <?php if ($iconID) : ?>
                <div class="flex-item--fullwidth">
                    <img class="hello-custom-cta__icon circle d-block mx-auto" src="<?= $iconURL; ?>" alt="<?= $iconALT; ?>" />
                </div>
                <?php endif; ?>
                <?php if ($hasSuperHeading && $superHeading) : ?>
                <div translate-name="custom-cta__superheading" class="<?= $superHeadingExtraClasses; ?>hello-custom-cta__superheading hello-1rem-text font-weight-bold text-uppercase hello-text--orange">
                    <span><?= $superHeading; ?></span>
                </div>
                <?php endif; ?>
                <div translate-name="custom-cta__headline" class="hello-custom-cta__headline h2 font-weight-bold my-2 my-md-3">
                    <?= $headline; ?>
                </div>
                <?php if ($blurb && $blurb !== '<p></p>') : ?>
                <div class="hello-custom-cta__blurb"><?= $blurb; ?></div>
                <?php endif; ?>
                <?php if ($ctaButtonText && $ctaLinkURL) : ?>
                <a href="<?= $ctaLinkURL; ?>" translate-name="custom-cta__btn-link" class="hello-custom-cta__btn-link mb-3 mb-md-0 btn hello-btn hello-btn--blue"><?= $ctaButtonText; ?></a>
                <?php endif; ?>
              </div>
            </div>
        </div>
    </div>
</div>

I've noticed the main function in your render callback that can't be done in JavaScript is checking for wpml_is_rtl and _icl_lang_duplicate_of . For this, it would make sense to use PHP/dynamic rendering to get this value. Although, rewritting your original save() function into PHP, deprecating the previous block then resetting up all the variables from the attributes in PHP doesn't seem ideal. The end goal is the same output: rendered HTML. I've faced similiar issues migrating existing plugins to Gutenberg blocks where you really just want a tiny bit of PHP functionality to get the right logic in the end markup.

Alternative Approach:

After your block is registered, the blocks JavaScript can be localized by using wp_localize_script . The advantage of this approach is you can pass values from PHP to JavaScript save() instead of having to rewrite all your markup just to access a PHP function.

class-hello-tools-custom-blocks.php

// Revert to original static block
register_block_type( 'hello-tools/custom-cta', array(
    'style' => $this->plugin_slug . '-public',
    'editor_style' => 'hello-gutenberg-admin',
    'editor_script' => $this->plugin_slug . '-custom-blocks',
) );

// Localize script with an array (data) containing the values of PHP functions as needed
function hello_tools_localize_scripts()
{
    $id = get_the_ID(); // Changed from $post->ID in this context

    $is_rtl = (apply_filters('wpml_is_rtl', NULL)) ? true : false;

    if (is_object($post) && get_post_meta($id, '_icl_lang_duplicate_of', true)) {
        $is_rtl = false; // if this is on a duplicate page, keep the content LTR
    }

    // Name of script is santized name from block.json
    wp_localize_script('hello-tools-custom-cta', 'data', array(
        'is_rtl' => $is_rtl,
        'is_admin' => is_admin()
    ));
}
add_action('wp_enqueue_scripts', 'hello_tools_localize_scripts');

custom-cta.js

The block JavaScript will now have access to the data and use its contents to save the desired markup, eg:

save({ attributes }) {
   ...
   return (
       <div className={blockClasses}>
           {data.is_rtl && (
              // save something different for RTL
           )}
        ...
       </div>
   )
}
...

If this is what originally caused you to switch to rendering with PHP, then hopefully you can implement the suggestion above, remove hello-custom-cta-callback.php completely, and reuse your existing save() function with the additional variables.

Also check a previous version of your custom-cta-block.json for how your attributes were previously defined. For attributes that are editable <RichText../> , the value can be sourced from the saved markup - you can set the source using the class selector, eg:

"attributes" :{
    ...
    "blurb": {
        "type": "string",
        "source": "text",
        "selector": ".hello-custom-cta__blurb",
        "default": ""
    },
...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM