ServerSideRender和Media Object:将图像数据对象传递给php呈现器的属性,即使它未设置

时间:2021-08-25 作者:Joel Abeyta

编辑:简单地说,我遇到的问题是,在js端保存为属性的图像数据对象(js中的imgDataObj)正在传递到PHP端,即使我没有在serversiderender组件中传递它,也没有在PHP渲染回调中侦听它。

我有一个自定义脚本,可以创建具有自定义大小的图像集。我正试着把它放到一个街区里。ServerSideRender似乎是一条出路。我用它制作了一些块,但从来没有一块有图像。因此,用户可以从媒体库上传/拾取图像,并为每个断点设置自定义大小。块将图像的大小和ID号发送到PHP端。我们不需要整个图像对象。例如,它会产生一个错误,即URL太长。我的脚本只需要ID。然而,即使我没有在属性中传递image对象,它也会显示在PHP端。

我在非serversiderender块中实现了这一点,但它有点笨重。Serversiderender看起来更加优雅,但我只是缺少了一些东西。

这是我的js:

import React from \'react\';

if (wp.element && wp.editor) {
    const { __ } = wp.i18n; // Import __() from wp.i18n
    const {
        registerBlockType,
        getBlockDefaultClassName
    } = wp.blocks;

    const {
        useBlockProps,
        MediaUploadCheck,
    } = wp.blockEditor;

    const {
        Button,
        PanelBody,
        PanelRow,
        SelectControl,
        TextControl,
        ToggleControl,
    } = wp.components;
    
    const {
        InspectorControls,
        MediaUpload
    } = wp.editor;

    const { serverSideRender: ServerSideRender } = wp;

    const el = wp.element.createElement;

    const icon = el(\'svg\',{
            xmlns: \'http://www.w3.org/2000/svg\',
            viewBox: \'0 0 93 93\',
        },
        el(\'title\', \'icon\' ),
        el(\'g\',{
            id: \'Layer_2\',
            \'data-name\': \'Layer 2\'
        },
            el(\'g\',{
                id: \'Layer_1-2\',
                \'data-name\': \'Layer 1\'
            },
                el(\'path\',{
                    d: \'M30,30V93H93V30ZM80.47,61l-19,19L50.66,69.11,36,83.77V36H87V67.52Z\'
                }),
                el(\'circle\',{
                    cx: \'46.21\',
                    cy: \'46.53\',
                    r: \'5.76\',
                }),
                el(\'polygon\', {
                    points: \'6 87 6 6 87 6 87 13 93 13 93 0 0 0 0 93 13 93 13 87 6 87\'
                }),
                el(\'polygon\', {
                    points: \'21 87 21 21 87 21 87 28 93 28 93 15 15 15 15 93 28 93 28 87 21 87\'
                }),
            )
        )
    );

    registerBlockType(\'cw-blocks/responsive-image\', {
        title: \'Responsive Image\',
        icon: icon,
        category: \'embed\',
        attributes: {
            imgW: {
                type: \'number\',
                default: 1600,
            },
            imgH: {
                type: \'number\',
                default: 1200,
            },
            imgWMed: {
                type: \'number\',
                default: 800,
            },
            imgHMed: {
                type: \'number\',
                default: 600,
            },
            imgWSmall: {
                type: \'number\',
                default: 400,
            },
            imgHSmall: {
                type: \'number\',
                default: 300,
            },
            crop: {
                type: \'boolean\',
                default: false,
            },
            linkURL: {
                type: \'string\',
                default: \'\'
            },
            targetBlank: {
                type: \'boolean\',
                default: false
            },
            pos: {
                type: \'text\',
                default: \'\'
            },
            imgDataObj: {
                type: \'array\',
                default: {}
            },
            imgID: {
                type: \'string\',
                default: \'\'
            }
        },

        edit: (props) => {
            const blockProps = useBlockProps();
            const { className, setAttributes } = props;
            const { attributes } = props;

            const setImgData = function (imgDataObj){
                setAttributes({ imgDataObj: imgDataObj });
                setAttributes({ imgID: imgDataObj[\'id\'] });
            }

            const resetImgData = function (){
                setAttributes({ imgDataObj: {} });
                setAttributes({ imgID: \'\' });
            }

            return [
                <InspectorControls>
                    <PanelBody title="Image" initialOpen={true}>
                        <MediaUploadCheck>
                            <MediaUpload
                                className="cw-resp-image wp-admin-cw-resp-image"
                                allowedTypes={[\'image\']}
                                multiple={false}
                                value={props.attributes.imgDataObj ? props.attributes.imgDataObj.id : \'\'}
                                onSelect={setImgData}
                                render={({ open }) => (
                                    attributes.imgDataObj.id ?
                                        <div>
                                            <p>
                                                <img src={attributes.imgDataObj.url} width={attributes.imgDataObj.width / 2} />
                                            </p>

                                            <p>
                                                <Button onClick={resetImgData} className="button is-small">Remove</Button>
                                            </p>
                                        </div> :
                                        <Button onClick={open} className="button">Select/Upload Image</Button>
                                )}
                            />
                        </MediaUploadCheck>
                        <SelectControl
                            label="Position"
                            value={props.attributes.pos}
                            onChange={(pos) => setAttributes({ pos: pos })}
                            options={[
                                { label: \'Left\', value: \'left\' },
                                { label: \'Center\', value: \'center\' },
                                { label: \'Right\', value: \'right\' },
                            ]}
                        />
                    </PanelBody>

                    <PanelBody title="URL" initialOpen={false}>
                        <TextControl
                            label="URL"
                            value={props.attributes.linkURL}
                            onChange={(linkURL) => setAttributes({ linkURL: linkURL })}
                            type="text"
                        />
                        <ToggleControl
                            label="Open link in new tab"
                            checked={props.attributes.targetBlank}
                            onChange={(targetBlank) => setAttributes({ targetBlank: targetBlank })}
                        />
                    </PanelBody>

                    <PanelBody title="Sizes" initialOpen={true}>
                        <PanelRow>
                            <p>Both width and height are required to create a cropped image.</p>
                        </PanelRow>

                        <TextControl
                            label="Small Width (phones)"
                            value={props.attributes.imgWSmall}
                            onChange={(imgWSmall) => setAttributes({ imgWSmall: parseInt(imgWSmall) })}
                            type="number"
                        />
                        <TextControl
                            label="Small Height (phones)"
                            value={props.attributes.imgHSmall}
                            onChange={(imgHSmall) => setAttributes({ imgHSmall: parseInt(imgHSmall) })}
                            type="number"
                        />

                        <TextControl
                            label="Medium Width (tablets)"
                            value={props.attributes.imgWMed}
                            onChange={(imgWMed) => setAttributes({ imgWMed: parseInt(imgWMed) })}
                            type="number"
                        />
                        <TextControl
                            label="Medium Height (tablets)"
                            value={props.attributes.imgHMed}
                            onChange={(imgHMed) => setAttributes({ imgHMed: parseInt(imgHMed) })}
                            type="number"
                        />

                        <TextControl
                            label="Large Width (desktop)"
                            value={props.attributes.imgW}
                            onChange={(imgW) => setAttributes({ imgW: parseInt(imgW) })}
                            type="number"
                        />
                        <TextControl
                            label="Large Height (desktop)"
                            value={props.attributes.imgH}
                            onChange={(imgH) => setAttributes({ imgH: parseInt(imgH) })}
                            type="number"
                        />
                    </PanelBody>

                    <PanelBody title="Options" initialOpen={false}>
                        <ToggleControl
                            label="Crop"
                            checked={props.attributes.crop}
                            onChange={(crop) => setAttributes({ crop: crop })}
                        />
                    </PanelBody>
                </InspectorControls>,
                <div {...blockProps}>
                    <ServerSideRender
                        block="cw-blocks/responsive-image"
                        attributes={{
                            // no image object sent here
                            imgW: attributes.imgW,
                            imgH: attributes.imgH,
                            imgWMed: attributes.imgWMed,
                            imgHMed: attributes.imgHMed,
                            imgWSmall: attributes.imgWSmall,
                            imgHSmall: attributes.imgHSmall,
                            crop: attributes.crop,
                            loading: attributes.loading,
                            linkURL: attributes.linkURL,
                            targetBlank: attributes.targetBlank,
                            pos: attributes.pos,
                            imgID: attributes.imgID,
                        }}
                    />
                </div>
            ];
        },
        save: () => {
            return null;
        },
    });
}
这里是php

<?php

function cw_register_respimg_block(){
    wp_register_script(\'responsive-image\', get_template_directory_uri() . \'/src/js/admin/image-block.js\', array(
        \'wp-blocks\',
        \'wp\'
    ));

    register_block_type(
        \'cw-blocks/responsive-image\',
        array(
            \'attributes\' => array(
                // no image object received here
                \'imgW\' => array(
                    \'type\' => \'number\',
                    \'default\' => 1600,
                ),
                \'imgH\' => array(
                    \'type\' => \'number\',
                    \'default\' => 1200,
                ),
                \'imgWMed\' => array(
                    \'type\' => \'number\',
                    \'default\' => 800,
                ),
                \'imgHMed\' => array(
                    \'type\' => \'number\',
                    \'default\' => 600,
                ),
                \'imgWSmall\' => array(
                    \'type\' => \'number\',
                    \'default\' => 400,
                ),
                \'imgHSmall\' => array(
                    \'type\' => \'number\',
                    \'default\' => 300,
                ),
                \'crop\' => array(
                    \'type\' => \'boolean\',
                    \'default\' => false,
                ),
                \'linkURL\' => array(
                    \'type\' => \'string\',
                    \'default\' => \'\'
                ),
                \'targetBlank\' => array(
                    \'type\' => \'boolean\',
                    \'default\' => false
                ),
                \'pos\' => array(
                    \'type\' => \'text\',
                    \'default\' => \'\'
                ),
                \'imgID\' => array(
                    \'type\' => \'string\',
                    \'default\' => \'\'
                ),
            ),
            \'render_callback\' => \'cw_respimg_renderer\',
            \'editor_script\' => \'responsive-image\'
        )
    );
}

function cw_respimg_renderer($block_attributes, $content){
    $html = \'\';

    // need the image id to build custom srcset
    if($block_attributes[\'imgID\']) {

        // build size option data for image script
        $respimg_sizes = array();
    
        if($block_attributes[\'imgWSmall\'] || $block_attributes[\'imgHSmall\']) {
            $respimg_sizes[\'respimg_size_small\'] = array(
                \'w\' => $block_attributes[\'imgWSmall\'] ? $block_attributes[\'imgWSmall\'] : NULL,
                \'w\' => $block_attributes[\'imgHSmall\'] ? $block_attributes[\'imgHSmall\'] : NULL,
                \'crop\' => $block_attributes[\'crop\'],
            );
        }
    
        if($block_attributes[\'imgWMed\'] || $block_attributes[\'imgHMed\']) {
            $respimg_sizes[\'respimg_size_med\'] = array(
                \'w\' => $block_attributes[\'imgWMed\'] ? $block_attributes[\'imgWMed\'] : NULL,
                \'w\' => $block_attributes[\'imgHMed\'] ? $block_attributes[\'imgHMed\'] : NULL,
                \'crop\' => $block_attributes[\'crop\'],
            );
        }
    
        if($block_attributes[\'imgW\'] || $block_attributes[\'imgH\']) {
            $respimg_sizes[\'respimg_size_large\'] = array(
                \'w\' => $block_attributes[\'imgW\'] ? $block_attributes[\'imgW\'] : NULL,
                \'w\' => $block_attributes[\'imgH\'] ? $block_attributes[\'imgH\'] : NULL,
                \'crop\' => $block_attributes[\'crop\'],
            );
        }
    
        $resp_image = get_cw_img($block_attributes[\'imgID\'], \'respimg_size_large\', $respimg_sizes);
    
        // build image link
        $link_start = \'\';
        $link_end = \'\';
    
        if($block_attributes[\'linkURL\']) {
            $link_start = $block_attributes[\'targetBlank\'] ? \'<a href="\'.$block_attributes[\'linkURL\'].\'" target="_blank">\' : \'<a href="\'.$block_attributes[\'linkURL\'].\'">\';
            $link_end = \'</a>\';
        }
    
        $html .= \'<div class="cw-resp-img-mother \'.$block_attributes[\'pos\'].\'">\'.$link_start.$resp_image.$link_end.\'</div>\';
    }

    return $html;
}

add_action(\'init\', \'cw_register_respimg_block\');
非常感谢您的帮助。

1 个回复
最合适的回答,由SO网友:Sally CJ 整理而成

我遇到的问题是,在JS端保存为属性的图像数据对象(JS中的imgDataObj)正在被传递到PHP端,即使我没有在serversiderendercomponent中传递它,也没有在PHP渲染回调中侦听它

如果您使用WordPress 5.8,则可能会出现此问题uses Gutenberg 10.7 其函数名为__experimentalSanitizeBlockAttributes 定义在@wordpress/blockswp.blocks 包裹请参见中的第1255-1293行wp-includes/js/dist/blocks.js 或位于的源https://github.com/WordPress/gutenberg/blob/v10.7.0/packages/blocks/src/api/utils.js#L236-L277 对于Gutenberg 10.7.0版本。

该函数的作用是:

/**
 * Ensure attributes contains only values defined by block type, and merge
 * default values for missing attributes.
 *
 * @param {string} name       The block\'s name.
 * @param {Object} attributes The block\'s attributes.
 * @return {Object} The sanitized attributes.
 */
export function __experimentalSanitizeBlockAttributes( name, attributes ) {
并且在wp-includes/js/dist/server-side-render.js (第220行)或https://github.com/WordPress/gutenberg/blob/v10.7.0/packages/server-side-render/src/server-side-render.js#L75-L77 (对于Gutenberg 10.7.0),您可以看到ServerSideRender 适用于__experimentalSanitizeBlockAttributes() 在属性上:

const sanitizedAttributes =
    attributes &&
    __experimentalSanitizeBlockAttributes( block, attributes );

这意味着在注册块类型时,属性将与设置的属性合并,这就是为什么在您的情况下imgDataObj 即使您没有在attributes 属性传递给ServerSideRender

如何解决这个问题,因为你说过我们不需要整个图像对象;,然后,只需删除imgDataObj 从注册块类型时的属性。

然后,在edit 功能如下:

// At the top in the file, add:
const { useSelect } = wp.data;

// Then in the edit function, add this:
const imgDataObj = useSelect( select => {
    const { getEntityRecord } = select( \'core\' );
    return attributes.imgID && getEntityRecord( \'root\', \'media\', attributes.imgID );
}, [ attributes.imgID ] );
请注意,您还需要进行其他更改,但有关更多详细信息,请参阅GitHub上的我的代码。(参见底部的链接)

但在此之前,请阅读以下注释。。

其他问题/说明InspectorControlsMediaUpload 应从导入@wordpress/block-editorwp.blockEditor 包裹

  • text 不是有效的属性type 然而,在JS和PHP中pos 使用的属性text 作为类型。因此,请确保您的属性使用正确的类型。

    媒体/帖子ID是数字,所以我要更改imgID 把某事归因于某人number.

    访问对象中的属性时,应使用点符号,除非属性名称包含空格或hypens等字符(-). 因此imgDataObj[\'id\'], 我会用imgDataObj.id.

    您的edit 函数返回一个元素数组,因此每个顶级元素都应该具有唯一的key 属性,例如。<InspectorControls key="my-key">.

    在PHP中注册脚本时(使用wp_register_script()), 您应该指定脚本中使用的所有WordPress/Gutenberg包。因此array( \'wp-blocks\', \'wp\' ), 我会用array( \'wp-element\', \'wp-editor\', \'wp-blocks\', \'wp-block-editor\', \'wp-components\', \'wp-server-side-render\' ).

    实际上,要允许更大的属性对象,您可以设置httpMethodPOST 像这样:<ServerSideRender httpMethod="POST" .../>.

  • 尝试/检查我的代码

    您可以找到源代码(ESNext+JSX)on GitHub, 和检查this diff 看看我改变了什么。

    相关推荐

    Admin Theme customization

    我遵循wordpress codex网站上关于通过插件创建管理主题的说明。我激活了插件,但我的样式表没有包含在<head>.. 这是我的代码:add_action( \'admin_init\', \'kd_plugin_admin_init\' ); add_action( \'admin_menu\', \'kd_plugin_admin_menu\' ); function kd_plugin_admin_init() { /* Register