如何将静态古腾堡块转换为使用 PHP 注册的动态块?

2024-02-14

我正在尝试将我为 WordPress 创建的静态古腾堡块转换为动态块。我已经寻找其他解决方案,但没有成功。这个问题 (将使用 @wordpress/create-block 创建的静态 gutenberg 块转换为使用 PHP 注册的动态块的正确方法是什么? https://stackoverflow.com/questions/70944838/what-is-the-correct-way-to-convert-a-static-gutenberg-block-created-using-wordp/71317657)与我正在寻找的非常相似,但我的静态块不是使用 @wordpress/create-block 制作的,在完成所有建议的步骤后,我看到了似乎是弃用问题的内容。

class-hello-tools-custom-blocks.php(注册块)

静态版本如下所示:

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',
) );

动态变化如下:

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' )
) );

同一插件文件中的回调函数如下所示:

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(定义块属性)

正如参考文献中所提到的register_block_type()上面,这是 block.json 文件。

{
    "$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"
        }
    }
}

定制-cta.js

原本的save()函数已被删除,以告诉 Wordpres 它应该动态呈现,但以前的弃用版本已保持不变。

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(处理实际的 PHP 渲染,在回调函数中引用)

<?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>

我注意到渲染回调中无法在 JavaScript 中完成的主要函数是检查wpml_is_rtl and _icl_lang_duplicate_of。为此,使用 PHP/动态渲染来获取该值是有意义的。虽然,重写你原来的save()将函数导入 PHP,弃用前一个块,然后从 PHP 中的属性重置所有变量似乎并不理想。最终目标是相同的输出:渲染的 HTML。我在将现有插件迁移到 Gutenberg 块时遇到了类似的问题,您实际上只需要一点点 PHP 功能即可在最终标记中获得正确的逻辑。

替代方法:

注册您的块后,可以使用以下方法本地化块 JavaScriptwp_本地化_脚本 https://developer.wordpress.org/reference/functions/wp_localize_script/。这种方法的优点是您可以将值从 PHP 传递到 JavaScriptsave()而不必为了访问 PHP 函数而重写所有标记。

类-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');

定制-cta.js

JavaScript 块现在可以访问data并使用其内容来保存所需的标记,例如:

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

如果这是最初导致您切换到使用 PHP 渲染的原因,那么希望您可以实现上面的建议,删除hello-custom-cta-callback.php完全,并重用您现有的save()带有附加变量的函数。

另请检查您的先前版本custom-cta-block.json了解之前如何定义您的属性。为了属性 https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/可编辑的<RichText ../>,该值可以来自保存的标记 - 您可以使用类选择器设置源,例如:

"attributes" :{
    ...
    "blurb": {
        "type": "string",
        "source": "text",
        "selector": ".hello-custom-cta__blurb",
        "default": ""
    },
...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将静态古腾堡块转换为使用 PHP 注册的动态块? 的相关文章

  • jQuery id 选择器在有 .在 ID 中[重复]

    这个问题在这里已经有答案了 我的html代码如下 div class chatbox html markup which is validated div 在上面的代码中dipesh parmardiv 动态添加 我正在使用以下代码访问它
  • 如何获取传单标记簇中点击事件的图块?

    这是我的代码 function onMapClick e e originalEvent defaultPrevented true var orig e originalEvent console log orig target map
  • 通过 Javascript 将图像切割成碎片

    我正在创建一个简单的拼图游戏 为了做到这一点 我需要将我正在使用的图片切成 20 块 Javascript 有没有办法将一张图片切成 20 个相等的部分 并将它们保存为网页中的 20 个不同的对象 或者我只需要进入 Photoshop 自己
  • Office excel将CORS请求作为跨域请求

    我正在尝试从我的 Excel 插件发出跨域请求 正如这里所建议的 http dev office com docs add ins develop addressing same origin policy limitations http
  • Ajax JSON 数据和灯箱冲突

    我有一个带有灯箱插件的画廊设置光廊 http sachinchoolur github io lightGallery docs 该画廊与静态 HTML 完美配合 当我动态抓取 API 数据并尝试让灯箱处理这些项目时 问题就出现了 我似乎无
  • 使用 jquery 通配符检查 cookie 名称

    我有一个生成动态 cookie 的表单 例如 webform 62 1234356 62 1234356 可以是任意数字 我需要使用一些通配符检查来检查名称以 webform 开头的 cookie 是否存在 下面不起作用 if cookie
  • TCPDF UTF-8 符号未显示

    我使用最新的 TCPDF 版本 5 9 但在编码方面遇到一些奇怪的问题 我需要立陶宛语语言符号 例如 但只能得到其中的一小部分 其他的还是这样 所以我该怎么做 我使用默认的 times 字体 它带有 TCPDF 下载 任何帮助 将不胜感激
  • express 或express-generator:我需要两者吗?

    只是探索 Node js 并遇到了 Express 在 npm 存储库站点上https www npmjs com package express https www npmjs com package express它明确指出安装是 np
  • 如果多个键是相同的 JS,则对对象中的值求和

    例如我有 5 个对象 row aa col 1 value 1 row bb col 2 value 1 row bb col 3 value 1 row aa col 1 value 1 row aa col 2 value 1 我想对值
  • jQuery 在 Chrome 下发现错误元素

    我使用 jQuery 迭代 HTML 表 并动态填充每行的行号 通过在文本框中填充行号 function updateRowNums myTable find tr each function index this find input i
  • 如何在 Python 中包含 PHP 脚本?

    我有一个 PHP 脚本 news generator php 当我包含它时 它会抓取一堆新闻项并打印它们 现在 我在我的网站 CGI 中使用 Python 当我使用 PHP 时 我在 新闻 页面上使用了这样的内容 为了简单起见 我删掉了这个
  • 如何检查 Map 或 Set 是否为空?

    对于 JavaScript 中的传统对象 使用以下命令很容易检查它是否为空 Object keys method const emptyObj console log Object keys emptyObj length 0 true i
  • 按通用值对值进行分组:userid 和 ipaddress

    我正在解决数据库中的一个问题 我正在尝试查找使用多个帐户的用户 我有一个用户 ID 和使用的 IP 地址的列表 如下所示 用户 ID IP 地址 1 IP 地址 13 2 IP 地址 23 1 IP 地址 12 4 IP地址56 9 IP
  • 使用本机 JavaScript 获取过渡中的 CSS 值

    这个问题之前被问过 但答案使用了 jQuery here https stackoverflow com q 8920934 3186555 因此 我将调整问题以专门询问native解决方案 to 最小化依赖关系 假设您有一个 div 然后
  • React + Semantic-UI:在 UI MODAL 中使用表单

    在没有 React 的普通旧 Semantic UI 中 我已经能够毫无问题地将表单放入 Modal 中 使用 Semantic UI React 版本 我能够在模态中显示表单 但它并没有按照我期望的方式工作 例如 模态框显示后 模态框内的
  • 通过多个回调优雅地传递“点击事件”

    当未登录的用户单击给定的按钮时 我想停止该事件 收集他的 oauth 收集他的电子邮件 如果我没有 然后执行该事件 我想用 javascript 来做所有事情 因为这会让事情变得更加简单 这就是我执行它的方式 我有两个问题 有没有更优雅的方
  • 为什么 JSON 结果可以是布尔值而不是对象或数组?

    From JSON 网站 http json org JSON 建立在两种结构之上 名称 值对的集合 在各种语言中 这被实现为对象 记录 结构 字典 哈希表 键控列表或关联数组 值的有序列表 在大多数语言中 这被实现为数组 向量 列表或序列
  • javascript:window.print() 打印 2 页,而我有 1 页

    我有一个简单的 HTML 文档 其中仅包含图像标签 我想在文档加载后打印图像 我的代码 img src form1 jpg alt form1 style margin 0 auto display block 它可以工作 但问题是它打印图
  • Three.js WebGL 从着色器绘制圆形自定义填充和边框颜色

    我将 Three js 与 WebGLRenderer 一起使用 我试图找出或查看如何使用 CircleGeometry 绘制圆圈的示例 并能够从顶点或片段着色器控制其填充和边框颜色 如果不使用图像作为纹理 这是否可能 抱歉 如果这真的很简
  • facebook php - 如何获取专辑封面照片

    我需要使用 PHP SDK Facebook 获取专辑封面照片 我尝试 https graph facebook com ALBUM ID picture type album 但我得到默认图像 例如 获取用户 https graph fa

随机推荐

  • “政党模式”背后的原则和好处是什么?

    派对模型 是关系数据库设计的一种 模式 它至少有一部分涉及找到许多实体 例如客户 员工 合作伙伴等 之间的共性 并将其分解到一些更 抽象 的数据库表中 我想了解您对以下问题的想法 政党模式背后的核心原则和动力是什么 它规定您对数据模型做什么
  • hadoop pagerank运行时出现错误

    我已经在我的 vmware 上安装了 hadoop 并设计了我的 jar 文件 pagerank 运行以下命令 hadoop jar PageRank 1 0 0 jar PageRankDriver init 输入输出 2 出现以下错误
  • R 如何在另一个函数中将函数作为字符串传递

    对于这个小难题的任何帮助将不胜感激 我正在尝试向tq transmute函数从tidyquant包裹 参数的值是一个函数 但是我想将其作为字符串传递 在下面示例的范围内 我将通过 Shiny 传递它 selectInput 我已经尝试了所有
  • 如何检测地址/坐标中是否可以使用 Google 街景

    我正在制作一个使用 Google 街景图像 API 的应用程序 我可以毫无问题地根据地址 坐标获取街景图像 但是 我想检测特定地址是否有可用的街景图像 以便为没有街景图像的地址显示不同的行为 到目前为止我唯一的想法是读取返回图像的像素并检测
  • java: R 无法解析为变量

    我知道这个问题以前已经被问过 但我对此无能为力 我刚刚创建了一个新项目并收到此错误 当然 R java 文件没有被生成 我正在使用 JRE7 android 4 2 2 package com example myfirstapp impo
  • TensorFlow 中张量值的条件分配

    我想复制以下内容numpy代码在tensorflow 例如 我想分配一个0到先前值为的所有张量索引1 a np array 1 2 3 1 a a 1 0 a should be 0 2 3 0 如果我写类似的代码tensorflow我收到
  • 如何在Windows服务器上运行solr使其自动启动?

    如何在 Windows 服务器上运行 solr 使其自动启动 我运行它 java jar start jar 但我需要服务器自动执行此操作 使用您需要的命令创建一个批处理文件并在启动时运行它 一些these http windowsxp m
  • 场景生成器中的自定义控件 ClassNotFoundException

    我通过扩展现有控件创建了一个新控件 并且我想在我的 JavaFX 场景中使用这个新控件 我希望能够使用场景生成器编辑场景 但是将新控件添加到 FXML 文件后 我遇到了ClassNotFoundException打开场景生成器时 例如 这是
  • 为什么这段代码不会抛出 StackOverflow 异常

    在 clojure v1 6 0 中 这段代码会永远运行并消耗 100 的一个核心 defn average x y x y 2 defn improve guess x average guess x guess defn sqrt it
  • Spotify API - 在 Google Apps 脚本中检索有效访问令牌

    以下是 Spotify API 的文档 我使用的是隐式授权流程 https beta developer spotify com documentation general guides authorization guide implic
  • IntelliJ 中的 Cucumber.js 调试

    IntelliJ 有一个 Cucumber js 插件 这假设可以在 IDE 内启用运行和调试 cucumber js 的运行配置有效 但调试模式已停用 无法单击 调试 按钮 还有其他人有同样的问题并设法解决它吗 调试对我来说非常重要 谢谢
  • Python3 TypeError:列表索引必须是整数或切片,而不是 str

    我的任务是获取字符串 AAAABBBCCDAABBB 放入这样的列表中 A B C D A B 我现在已经为此工作了 2 个小时 但无法找到解决方案 到目前为止 这是我的代码 list string AAAABBBCCDAABBB i 1
  • 阅读 Facebook 中的消息

    我一直在尝试阅读来自特定用户 朋友 的消息 我在图形 API 中找不到有关查询 Facebook 消息中的消息或线程的正确文档 谁能帮帮我吗 您只能为使用您的应用程序的用户阅读消息 而不能为他的朋友阅读消息 这是一件好事 要为当前用户执行此
  • 为什么 JSF 将 UI 组件的状态保存在服务器上?

    JSF 在服务器端保存 UI 组件的状态到什么时间以及 UI 组件的状态信息具体何时被删除从服务器内存 当应用程序上的登录用户浏览页面时 组件的状态是否会继续在服务器上累积 我不明白在服务器上保持 UI 组件状态有什么好处 直接将验证 转换
  • 签署 Windows EXE 文件

    我有一个EXE http en wikipedia org wiki EXE我想要签署该文件 以便 Windows 不会警告最终用户来自 未知发布者 的应用程序 我不是 Windows 开发人员 所讨论的应用程序是从生成屏幕保护程序的应用程
  • Python:在类实例初始化之前修改传递的参数

    我正在尝试用 Python 实现简化的术语重写系统 TRS 符号代数系统 为此 我真的希望能够在类实例实例化过程中的特定情况下拦截和修改操作数 我想出的解决方案是创建一个元类来修改典型的call类对象 类型为 type 的行为 class
  • 如何在 Javascript 中说明多重图? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 这个问题正在寻找一种实用且简单的方法来使用 Javascript 绘制多重图 首先看this http
  • 在 MVVM 中,ViewModel 响应视图中用户操作的最佳方式是什么?

    我在 MVVM 中的理解是 View 知道 ViewModel ViewModel 知道 Model 但它不能向上工作 因此 Model 对 ViewModel 一无所知 并且 ViewModel 对 View 一无所知 So ViewMo
  • iOS8“现在”更改动态单元格的高度,重新内部单元格内容

    这是一个动态单元格 注意 在示例中 文本不是数据驱动的 它只是单元格本地的一些文本 例如 帮助文本 在运行时 使用单元格内实际的按钮将 UILabel 的 text 从一个单词更改为多行 iOS 完美地调整单元格和表格的大小 but 仅当单
  • 如何将静态古腾堡块转换为使用 PHP 注册的动态块?

    我正在尝试将我为 WordPress 创建的静态古腾堡块转换为动态块 我已经寻找其他解决方案 但没有成功 这个问题 将使用 wordpress create block 创建的静态 gutenberg 块转换为使用 PHP 注册的动态块的正