Gutenberg(ブロックエディタ)メモ: ショートコードの代替案的なブロックを作る

WordPressのGutenberg
WordPressのGutenberg

WordPressのGutenbergのハンドブックやGitHubの内容などを参考にして、求める機能の実装を試した内容です。

構築環境
WordPress4.9.8
JavaScriptJSX/ESNext
Gutenberg3.9.0

注意点

  • Gutenbergがコアに入る前の段階の記事です
  • 書き方だけではなく、語句や名称にも問題がある可能性があります
  • 内容が正確だと保証できないため、読む方は参考程度に考えてください

実現したいこと

  • ショートコードのような用途で使えるGutenbergでブロックを作る
  • サーバサイドレンダリングを試す

切っ掛けは参考にしたページで以下のように書かれていたためです。

要は編集画面上で検索とかサーバーサイドの処理が絡むようなものをプレビューしながら編集できるわけです。
render_callbackとServerSideRenderを使ったブロックの仕組みはショートコードの後継となるものと思われます。

ショートコードがなくなる話は見ていないので大丈夫だとは思いますが(なくなるという情報はないですよね…?)、後継とまで書かれるなら試しておいた方がよいかと。

ハンドブックや参考にしたGitHubのページは以下の通りです。

サンプルコード1

ショートコードの代替としてブロック例1
ショートコードの代替としてブロック例1

<div><p>ブロックで入力した内容</p></div>

一例目は基本として、上記のように単にブロックで入力した内容をpタグにいれただけのサンプルとなります。

入力欄の下にはサーバーから帰って来た値(前述のHTML)が出力されます。サンプルではCSSを当てていない状態なので単に同じ文字列が表示されているだけになりますが、入力内容に反応して表示が変わることを確認できます。

まずはJS側。


const { registerBlockType } = wp.blocks;
const { Fragment } = wp.element;
const { TextControl } = wp.components;
const { ServerSideRender } = wp.editor;

registerBlockType( 'my-plugin/test-as-shortcode-text', {
  title: 'test-as-shortcode-text',
  icon: 'universal-access-alt',
  category: 'layout',

  attributes: {
    content: {
      type: 'string'
    },
  },
  edit( {attributes, setAttributes} ) {
    const { content } = attributes;

    const onChangeContent = newContent => {
      setAttributes( { content: newContent } );
    };

    return(
      <Fragment>
        <TextControl
          onChange={onChangeContent}
          value={content}
        />
        //ブロック内でサーバーでの処理(PHP側での処理)を表示する必要がない場合は、ServerSideRenderは削除しても可
        <ServerSideRender
          block='my-plugin/test-as-shortcode-text'
          attributes={ attributes }
        />
      </Fragment>
    );
  },
  save() {
    return null;
  }
} );

続いてPHP側(プラグインの形ならプラグイン内のPHPファイル)。


//初期設定
add_action( 'enqueue_block_editor_assets', function() {
  wp_enqueue_script(
    'myplugin-gutenberge',
    plugins_url( 'block.js', __FILE__ ),
    [ 'wp-blocks', 'wp-element', 'wp-components', 'wp-editor' ]
  );
} );

//今回用の設定
register_block_type( 'my-plugin/test-as-shortcode-text', array(
  'attributes' => array(
    'content' => array (
      'type' => 'string'
    )
  ),
  'render_callback' => 'my_textbox_text',
) );
function my_textbox_text($attributes){
  $html  = '';
  $html .= '<div class="my_textbox">';
  $html .= '<p>' . esc_html($attributes['content']) . '</p>';
  $html .= '</div>';
  return $html;
}

ブロック読み込みエラー

個人的に詰まったのがregister_block_typeattributestypeを記載しないとエラーになったことです。

attributesのみやcontentを加えただけではブロック内に以下のようなエラーがでます。

ブロック読み込みエラー: 無効なパラメーター: attributes

基本的には、JS側のregisterBlockTypeattributesに記述した内容をそのまま持ってくる位の感じの方が良いかもしれません(必ずしもそうとは言い切れませんが)。

サンプル2

ショートコードの代替としてブロック例2
ショートコードの代替としてブロック例2

<!-- idにはカテゴリーID。numには表示件数。この2つをいれてカテゴリーの一覧を表示させるショートコード -->
[catlist id="" num=""]

続いて、上記のようなショートコードと同じようなものをブロックで作成します。

表示件数やIDはInspector(サイドバー)で変更する構造も考えましたが、とりあえずショーコードと多少は近い形にしやすいようにinputタグを使っています。

なお、今回は上記のショートコードで動かすように以前に作成したコードを流用しているため、PHP側の方は一例程度にご覧下さい(色々作り方は試している中の1つなので、これがいいとはいえません)。

まずはJS側。


const { registerBlockType } = wp.blocks;
const { Fragment } = wp.element;
const { ServerSideRender } = wp.editor;

registerBlockType( 'my-plugin/test-severside-as-shortcode-input', {
  title: 'test-severside-as-shortcode-input',
  icon: 'universal-access-alt',
  category: 'layout',

  attributes: {
    id: {
      type: 'integer',
      default: '1',
    },
    num: {
      type: 'integerg',
      default: '10',
    },
  },
  edit( {attributes, setAttributes} ) {
    const { id, num } = attributes;

    const onChangeInputId = newId => {
     setAttributes( { id: newId.target.value } );
    };
    const onChangeInputNum = newNum => {
     setAttributes( { num: newNum.target.value } );
    };

    return(
      <Fragment>
        <p>カテゴリーID:<input value={ id } onChange={ onChangeInputId } type="text" /></p>
        <p>表示数:<input value={ num } onChange={ onChangeInputNum } type="text" />※初期値は10件表示</p>
        <ServerSideRender
          block='my-plugin/test-severside-as-shortcode-input'
          attributes={ attributes }
        />
      </Fragment>
    );
  },
  save() {
    return null;
  }
} );

前述のようにカテゴリー一覧の記事用コードがまずあるため、それに合わせてdefalurtで未入力時の値を先に設定しています(カテゴリーIDは1/表示件数は10)。

初期値はPHP側でも設定可能だと思いますが、GutenbergはJS側で値を入れるため初期値もJS側で設定する方がよいと考えてこの形にしています。

後述するようにregister_block_typeの中とはいえPHPファイルにも記載しているので「JS側で設定する」という表現にそぐわない気はしますが。

続いてPHP側です。


//初期設定
add_action( 'enqueue_block_editor_assets', function() {
  wp_enqueue_script(
    'myplugin-gutenberge',
    plugins_url( 'block.js', __FILE__ ),
    [ 'wp-blocks', 'wp-element', 'wp-editor' ]
  );
} );

//今回用の設定
register_block_type( 'my-plugin/test-severside-as-shortcode-input', array(
  'attributes' => array(
    'id' => array (
      'type' => 'integer',
      'default' => 1
    ),
    'num' => array (
      'type' => 'integer',
      'default' => 10
    ),
  ),
  'render_callback' => 'my_catlist_func',
) );
//カテゴリ一覧を表示させる
if(!function_exists('my_catlist_func')){
  function my_catlist_func($attributes) {
    $id = $attributes['id'];
    $num = $attributes['num'];
    $titlelist = new TitleList;
    $html = $titlelist->get_cat_list($id,$num);
    return $html;
  }
}

//本筋から外れるため、カテゴリー表示用の記述は分けて後述します

typeintegerにしているので、数値以外を入れるとブロック内に以下のエラーが出ます。

ブロック読み込みエラー: 無効なパラメーター: attributes

ただし、PHP側のtypestringにすると文字列でも動作してしまい意図しない状態になるため、typeは揃えた方が無難でしょう。

カテゴリー記事一覧のPHPサンプル

このGutenbergの関連記事表示用に作ったのものなのですが、今回のコードにもそのまま流用しているのが以下のものです(そのためタイトル順で並べる指定になっていたりしますが)。

「IDと表示件数を渡せば動作するコード」であれば何でもよいので当初は記載するつもりがなかったのですが、この辺を省くと不完全なサンプルになって分かりにくくなるかもしれないとも思われ、書くだけ書いておこうと記載している次第です。


class TitleList{

public function get_cat_list($catId ,$num){
  $args = ''; //変数の初期化
  $args = array(
    'posts_per_page' => $num,
    'cat' => $catId,
    'orderby' => 'title',
    'order' => 'ASC'
  );
  return $this->loop($args);
}

private function loop($args){
  $html    = '';
  $current = ''; //現在表示中の記事ならクラスを出力
  $currentId = get_the_ID();

  $wp_query = new WP_Query($args);
  if ( $wp_query->have_posts() ){
    $html .= '<ul class="titlelist">';
    while ( $wp_query->have_posts() ){
      $wp_query->the_post();
      $itemId = get_the_ID();

      if($itemId === $currentId){
        $current = ' <span>[現在表示中]</span>';
      } else {
        $current = '';
      }

      $html .= '<li class="titlelist_items"><a href="' . get_the_permalink() . '" title="' .  the_title_attribute( 'echo=0' ) . '">' . get_the_title() . '</a>' . $current . '</li>';
    }
    $html .= '</ul>';
  } else {
    $html .= '<p>';
    $html .= '申し訳ありませんが、現在このカテゴリーの記事はありません。';
    $html .= '</p>';
  }
  wp_reset_query();
  return $html;
}

}

結び

サーバーサイドの処理を含む場合、JSとPHPでどこをどう処理するのかの検討が重要になると思います。

また、入力欄の場所も検討事項です。

ブロック側で何らかの値を入力してServerSideRenderを動作させる場合、初期ブロックのアーカイブのように入力欄はサイドバーにあるのが筋かもしれません。

関連記事

Gutenberg(ブロックエディタ)に関連する記事一覧。

2人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄