ダイナミックブロックの作成

ダイナミックブロックはフロントエンド側でレンダリングされる際に、動的に構造とコンテンツを構築するブロックです。

ダイナミックブロックの代表的な2つの使用例です。

  1. 投稿が更新されていなくてもコンテンツを変更するブロック。WordPress 自身から1つ例を挙げると「最近の更新」ブロックがあります。このブロックは新しい投稿が発行されるとすべての使用箇所を更新します。
  2. HTML、CSS、JS などのコードを更新するとすぐにWeb サイトのフロントエンド側で反映されるブロック。たとえば新しいクラスを追加したり、HTML 要素を追加したり、その他の方法でレイアウトを変更してブロックの構造を更新した場合、ダイナミックブロックを使えばサイト内のすべてのブロックの使用箇所に即座に変更を適用できます。ダイナミックブロックを使わない場合、ブロックコードが更新されると Gutenberg の妥当性検証プロセスが適用され、ユーザーに検証メッセージ「ブロックの外観は外部で更新されました」が表示されます。

多くのダイナミックブロックでは save コールバック関数は null として返されるべきです。これを受けてエディターはデータベースにブロック属性のみを保存します。その後、これらの属性はサーバー側レンダリングコールバックに渡されるため、サイトのフロントエンドでどのようにブロックを表示するか決定できます。null を返すと、エディターはブロックのマークアップの妥当性検査プロセスをスキップするため、頻繁にマークアップを変更する際の問題を回避できます。

ダイナミックブロック内で InnerBlocks を使用している場合には、<InnerBlocks.Content/> を使用して save コールバック関数内で InnerBlocks を保存する必要があります。

ブロックの HTML 表現も保存できます。サーバー側レンダリングコールバックを提供すると、この HTML はコールバックの出力で置換されますが、ブロックが無効化されたり、レンダリングコールバックが削除される場合には、レンダリングされます。

ブロック属性は、ブロックのために保存したい任意のコンテツや設定に対して使用できます。最初の最新の投稿ブロックの例では、フロントエンドに表示したい最新の投稿数を属性として保存できます。あるいは2番めの例では、フロントエンドで表示したい各コンテンツの部品、たとえば見出しテキスト、段落テキスト、画像、URLとして属性を使用できます。

次のコード例では最後の投稿だけをリンクとして表示するダイナミックブロックを作成します。

ESNext

import { registerBlockType } from '@wordpress/blocks';
import { withSelect } from '@wordpress/data';

registerBlockType( 'gutenberg-examples/example-dynamic', {
    title: 'Example: last post',
    icon: 'megaphone',
    category: 'widgets',

    edit: withSelect( ( select ) => {
        return {
            posts: select( 'core' ).getEntityRecords( 'postType', 'post' ),
        };
    } )( ( { posts, className } ) => {
        if ( ! posts ) {
            return 'Loading...';
        }

        if ( posts && posts.length === 0 ) {
            return 'No posts';
        }

        const post = posts[ 0 ];

        return <a className={ className } href={ post.link }>
            { post.title.rendered }
        </a>;
    } ),
} );

ES5

( function( blocks, element, data ) {
    var el = element.createElement,
        registerBlockType = blocks.registerBlockType,
        withSelect = data.withSelect;

    registerBlockType( 'gutenberg-examples/example-dynamic', {
        title: 'Example: last post',
        icon: 'megaphone',
        category: 'widgets',
        edit: withSelect( function( select ) {
            return {
                posts: select( 'core' ).getEntityRecords( 'postType', 'post' ),
            };
        } )( function( props ) {
            if ( ! props.posts ) {
                return 'Loading...';
            }

            if ( props.posts.length === 0 ) {
                return 'No posts';
            }
            var className = props.className;
            var post = props.posts[ 0 ];

            return el(
                'a',
                { className: className, href: post.link },
                post.title.rendered
            );
        } ),
    } );
}(
    window.wp.blocks,
    window.wp.element,
    window.wp.data,
) );

これはダイナミックブロックですので、クライアントのデフォルトの save 実装をオーバーライドする必要はありません。代わりに、サーバーコンポーネントが必要です。サイトのフロントエンドのコンテツは register_block_type の render_callback プロパティに呼び出される関数に依存します。

<?php

/**
 * Plugin Name: Gutenberg examples dynamic
 */

function gutenberg_examples_dynamic_render_callback( $attributes, $content ) {
    $recent_posts = wp_get_recent_posts( array(
        'numberposts' => 1,
        'post_status' => 'publish',
    ) );
    if ( count( $recent_posts ) === 0 ) {
        return 'No posts';
    }
    $post = $recent_posts[ 0 ];
    $post_id = $post['ID'];
    return sprintf(
        '<a class="wp-block-my-plugin-latest-post" href="%1$s">%2$s</a>',
        esc_url( get_permalink( $post_id ) ),
        esc_html( get_the_title( $post_id ) )
    );
}

function gutenberg_examples_dynamic() {
    // automatically load dependencies and version
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');

    wp_register_script(
        'gutenberg-examples-dynamic',
        plugins_url( 'build/block.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    register_block_type( 'gutenberg-examples/example-dynamic', array(
        'editor_script' => 'gutenberg-examples-dynamic',
        'render_callback' => 'gutenberg_examples_dynamic_render_callback'
    ) );

}
add_action( 'init', 'gutenberg_examples_dynamic' );

いくつか注意点があります。

  • 依然、edit 関数はエディターのコンテキストにおけるブロックの外観を表示します (レンダリングバージョンとまったく異なる場合もあります。これはブロック作者の好みによります)
  • 組み込みの save 関数は null を返すだけです。これはレンダリングがサーバー側で実行されるためです。
  • サーバー側レンダリングは、ブロック属性とブロック内部コンテンツを引数に取る関数で、ショートコードに似たマークアップを返します。

ブロックエディター内でのライブレンダリング

Gutenberg 2.8 は <ServerSideRender> ブロックを追加しました。JavaScript の代わりに PHP を使用してサーバー上でレンダリングを実行します。

サーバー側レンダリングはフォールバックです。常に JavaScript によるクライアントサイドレンダリングが好まれます。クライアントレンダリングは速く、エディターの操作性が高くなります).

ESNext

import { registerBlockType } from '@wordpress/blocks';
import ServerSideRender from '@wordpress/server-side-render';

registerBlockType( 'gutenberg-examples/example-dynamic', {
    title: 'Example: last post',
    icon: 'megaphone',
    category: 'widgets',

    edit: function( props ) {
        return (
            <ServerSideRender
                block="gutenberg-examples/example-dynamic"
                attributes={ props.attributes }
            />
        );
    },
} );

ES5

( function( blocks, element, serverSideRender ) {
    var el = element.createElement,
        registerBlockType = blocks.registerBlockType,
        ServerSideRender = serverSideRender;

    registerBlockType( 'gutenberg-examples/example-dynamic', {
        title: 'Example: last post',
        icon: 'megaphone',
        category: 'widgets',

        edit: function( props ) {
            return (
                el( ServerSideRender, {
                    block: 'gutenberg-examples/example-dynamic',
                    attributes: props.attributes,
                } )
            );
        },
    } );
}(
    window.wp.blocks,
    window.wp.element,
    window.wp.serverSideRender,
) );

注意: このコードは wp-server-side-render パッケージを使用し、wp-data を使用しません。PHP コード内の依存性を更新してください。自動で依存性を設定するには wp-scripts と ESNext 設定を使用してください (PHP コード設定については gutenberg-examples リポジトリーを参照してください)。