ワードプレスのエディター「Gutenberg」にカスタムブロックを追加する方法

アバター付きの吹き出しコメントブロック「Chat」
先生
先生

こんにちは。ここではワードプレスのエディタ「Gutenberg」に自分でブロックを追加する方法を紹介していこうと思います。

は〜い、よろしくおねがいします。

Nora
Nora

こんな感じでアバター付きの吹き出しコメントを表示するブロックを作成してみましたが、どうでしょう?

エディタ「Gutenberg」ではページのコンテンツをブロック毎に管理・編集することが出来ますので、ワードプレスでブログをやっていたり、記事を書くことの多い方には、便利なブロックが増えると有り難いですよね。

例として作成したチャット形式のブロックに限らず、スライダーやら特別なレイアウトのセクションなど、あらゆるブロックを挿入することができれば、Gutenbergがコンテンツ作成も楽しんで書くことができる便利なツールとなる筈です。

それに実はブロックって結構簡単に実装できますし、痒いところに手が届くようなブロックが欲しいなら、ブロックの作成方法は知っておいて損はしないですよ。

  • Table of Contents

ブロックの追加に必要なファイル

Gutenbergのブロック編集では主にJavaScriptを使用しますので、JavaScriptを使い慣れている方が好ましいです。それに加えて、スタイルを定義するためのCSSファイルが2つ必要になります。

  • block.js(ブロックを追加)
  • block-style.css(フロントエンド用)
  • block-editor-style.css(編集画面用)

ファイル名は管理しやすいもので別に何でも構いませんが、拡張子だけは.jsと.cssでお願いします。

GutenbergではReactを使用しているので、Reactを使い慣れていると編集は非常に楽になると思います。ちょっとデータを保存する方法が異なるだけで使えるようになります。

僕はReactを使ったことがありませんでしたが、ブロック編集を勉強するついでにある程度覚えられたくらいですので、凝ったものを作ろうとしない限り、事前にReactを使いこなせている必要は無い筈です。

また少しだけPHPにコードを書く必要もありますが、ほんの少しですのでワードプレスに慣れている方なら楽勝です。

PHP側で必要なコード

では、まずはワードプレスにブロックを追加するCSSとJavaScriptをロードするためのコーディングを施していきます。

具体的に必要な処理は以下の2点のみですのでパパっと書いてしまいましょう。

  • ブロックを登録するJavaScriptと編集画面用のCSSをロードさせる
  • フロントエンド(実際に表示されるページ)用のCSSをロードさせる

ブロックを登録するコードをロードさせる

必要なコードは以下のとおりです。

add_action( 'enqueue_block_editor_assets', 'register_block_type' );
function register_block_type() {

  // CSSのハンドル
  $block_style_handle = 'your-custom-block';
  // CSSファイルの登録
  wp_enqueue_style( $block_style_handle, PATH_TO_YOUR_CSS_FILE );

  
  // JavaScriptのハンドル
  $block_script_handle = 'your-custom-block';
  // JavaScriptファイルの登録
  wp_enqueue_script( $block_script_handle, PATH_TO_YOUR_JS_FILE, array( 'wp-blocks', 'wp-element', 'wp-components' ) );

}
Nora
Nora

Gutenbergにブロックを登録するためにフックが用意されていますので、そこでファイルをエンキューさせる関数をコールするだでOK!

これが基本的な形になります。

必要に応じて依存するJSファイルのハンドルに「wp-i18n」「lodash」を追加するなどしてください。

ちなみに「PATH_TO_YOUR_CSS_FILE」「PATH_TO_YOUR_JS_FILE」はブロックを追加するためのCSSファイルとJavaScriptファイルのURLとなりますので、任意のファイルの指定してください。

フロントエンド側にスタイルを出力

こちらはテーマなどでもお馴染みの方法です。

add_action( 'wp_enqueue_scripts', 'enqueue_block_style' );
function enqueue_block_style() {

  // CSSのハンドル
  $block_style_handle = 'your-custom-block-style';
  // CSSファイルの登録
  wp_enqueue_style( $block_style_handle, PATH_TO_YOUR_CSS_FILE );

}

ただ、個人的にはページスピードを意識すると、テーマにブロックを追加するのであれば、テーマから出力されるCSSファイル内に統一して定義し、プラグインとしてブロックを追加するのであれば、インラインで出力することをオススメします。

関連:ワードプレスのページスピードを改善する方法

ブロックのカテゴリーを追加する方法(オプション)

本当に簡単なので以下のコードをコピペして、「slug」「title」「icon」の値をそれぞれ編集するだけでカテゴリーは追加できます。

add_filter( 'block_categories', 'register_block_categories', 10, 2 );
function register_block_categories( $block_categories, $post ) {

  return array_merge(
    $categories,
    array(
      array(
        'slug' => 'your-block-category',
        'title' => __( 'Your Block Category', 'your_textdomain' ),
        'icon'  => 'admin-plugins',
      ),
    )
  );

}

余談として、第二引数で投稿データを取得できますので、投稿タイプやフォーマットなどにより、ブロックカテゴリーの制御もできます。

JavaScript側で必要なコード

Nora
Nora

では、ロードされるJavaScriptファイルをエディタで開きましょう。

まだ白紙の状態ですが、以下のコードが基本的な形で、JSX抜きに書いていくとこんな感じになるかと思います。

// 翻訳用の関数
const { __ } = wp.i18n;
// ブロックを登録するのに必要な関数
const { registerBlockType } = wp.blocks;
// 要素を生成する関数
const el = wp.element.createElement;

// ブロックとして登録するID
const blockName = 'your-block/sample'

// 登録
registerBlockType( blockName,
  {
    // タイトル
    title: __( 'Your Sample', 'your_textdomain' ),
    // アイコン
    icon: 'universal-access-alt',
    // カテゴリー
    category: 'widgets',
    // 詳細
    description: __( 'This is a sample block.' 'your_text_domain' ),

    // 保存されるデータ
    attributes: {

      sampleAttr: {
        type: 'string',
        default: 'default string value'
      }
      
    }

    // 編集画面のブロックとして出力
    edit: function( props ) {

      // データを保存する関数
      const { setAttributes } = props;

      // 保存されているデータ
      const { sampleAttr } = props.attributes;

      // HTMLを生成
      return el(
        'div',
        {
          className: 'your-block-wrapper edit-mode'
        },
        el(
          'input',
          {
            onChange: value => setAttributes( { sampleAttr: value } ), // データを保存
            value: sampleAttr
          }
        )
      );
 
    }

    // 実際にコンテンツのブロックとして出力
    save: function( props ) {

      // 保存されているデータ
      const { sampleAttr } = props.attributes;

      // HTMLを生成
      return el(
        'div',
        {
          className: 'your-block'
        },
        el(
          'p',
          {
            className: 'your-block-text'
          }
          sampleAttr
        )
      );
 
    }

  }
);

上に書いたサンプルでは、ブロック「ウィジェット」に追加する形になっています。ブロックを登録する関数はこんな形です。

registerBlockType( blockId, Object );

次に第二引数としてのオブジェクトですが、まず「edit」と「save」以外を覚えましょう。

  • title: タイトル
  • icon: アイコン(上の例ではDashiconを使用。SVGなども使用できます)
  • category: カテゴリー(ブロックを追加する際に出てくるパネルにある区分。「common」「formatting」「layout」「widgets」「embed」「reusable」が標準)
  • description: 詳細
  • attributes: ブロックに使用される編集可能なデータリスト

これらは通常のオブジェクトタイプのキーと値ですので、通常のJavaScriptと同じと思って大丈夫です。

そして次に「edit」と「save」ですが、これらに書かれている部分がReactが使用されている部分です。

「edit」と「save」ではレンダリングされる値を返す

ここでちょっと知っておきたいのがJSXの存在です。今まで使ったこと無い方は「Babel · The compiler for next generation JavaScript」 へ一度移動してみてください。ウェブ上の変換ツールのページです。左側にHTMLらしきコードを入力すると、何やら右側に変換されたJavaScriptのコードが出力されるのが見て分かると思います。そして、そこには「React.createElement」というコードがいくつか登場しています。

この左側にあるコード、HTMLらしきコードと書きましたが、実はこれがJSXというやつです。HTMLを書いても恐らく問題なく機能すると思いますので、見慣れない方は暫く変換して形だけでも見慣れてください。また別の機会に紹介できればと思いますが、基本的な形は以下を参照。

// JSX
<Fragment>
  <HTMLタグ名1 プロパティ名1="値1" プロパティ名2={ 変数1 } />
  <コンポーネント名1 { ...オブジェクト } >{ 文字列変数 }</コンポーネント名1>
</Fragment>

これを変換するとだいたいこんな感じです。
実際はエンコードされますが、分かりやすいように書いています。

// JS
React.createElement(
  Fragment,
  null,
  React.createElement(
    'HTMLタグ名1',
    {
      プロパティ名1: "値1",
      プロパティ名2: 変数値1
    },
  ),
  React.createElement(
    コンポーネント名1,
    オブジェクト,
    文字列変数
  )
);

// Gutenberg版
wp.element.createElement(
  Fragment,
  null,
  wp.element.createElement(
    'HTMLタグ名1',
    {
      プロパティ名1: "値1",
      プロパティ名2: 変数値1
    },
  ),
  wp.element.createElement(
    コンポーネント名1,
    オブジェクト,
    文字列変数
  )
);

GutenbergではこのJSXを変換した右側にあるコードを更にGutenberg用に置き換えたコードが使用されます。僕がさっき上に書いたコードを見ると「React.createElement」が使用されていないことが分かると思います。これはワードプレスが管理しやすいように「wp.element.createElement」に置き換えているためで、Gutenbergにブロックを追加するにはこれが必要になります。

Nora
Nora

「毎回こんなコード書いて、変換されたコードをコピペするのは面倒すぎる!」と思った初心者の方! Nodeで自動的にJSXを変換して、上に僕が書いたようなコードにする環境は簡単に作れます。僕はwebpackで自動化させていますが、今回はブロックの追加方法を紹介しているだけですので、とりあえず分かりやすいこの形でご容赦を。。。

最初の例に書いたコードをHTMLに変換すると、

// edit
<div class="your-block-wrapper edit-mode"><input value="{ sampleAttr }" onChange="setAttributes( { sampleAttr: changedValue } )" /></div>
// save
<div class="your-block"><p class="your-block-text">{ sampleAttr }</p></div>

のような感じになりますが、実際にはワードプレスが用意してくれているコンポーネント(JSXで使用可能なテンプレートパーツ)も使っていくことになるので、このHTMLっぽいのは「何となくこんな感じかな?」と思える程度の認識で大丈夫です。

最終的にCSSでスタイルを付けられるようなHTMLの形になってくれれば問題無しです。

データの保存方法

Reactにはデータを更新するためのメソッド「setState」が備わっていますが、Gutenbergでは値を保存するための関数「setAttributes」が引数として受け取るプロパティ「props」に用意されています。使い方は「setAttributes( { attrName: Value } )」の様に更新されるキーと値をセットにしたオブジェクトを渡す形となっており、Reactの「setState」の代わりに使用すればいいと思います。ええ、Gutenbergでは「setState」は使用せず、「setAttribute」で必要なデータを保存して更新します。

先程のサンプルのようにフォーム要素のinputなどでは、onChangeなどのイベントコールバックを用意して、そのコールバックにより「setAttributes」がコールされるように書きましょう。

コンポーネントクラスを作成して、部分的なファイルの使い方をする場合だと、各コンポーネントに渡すプロパティに「{ …props }(或いは{…this.props})」という形にしておくことで、子コンポーネントにもデータ更新用のメソッド「setAttributes」を渡すことができ、親コンポーネントのデータを更新することができます。ReactのStateのリフトアップをsetAttributesを親コンポーネントから子コンポーネントに渡して使うことで簡単に解決されます。

実はブロックの追加方法は2種類存在する

ワードプレスではテーマやプラグインの殆どがPHPで書かれているように、実はこのブロックもPHP側で生成する方法が用意されています。

保存されるブロックのHTMLもJavaScript側で定義していく形になりますが、PHP側でフロントエンドに出力する際のHTMLを生成する関数を「register_block_type」のオプショナルの引数として用意されている「render_callback」に渡すことも可能です。その場合、合わせて保存されるべきデータを「attributes」として登録しておく必要がありますが、こちらもまたの機会に。とりあえず、関数の参照ページへのリンクは張っておきます。

Nora
Nora

いかがでしたでしょうか? ReactやJSX関連の詳細な話は殆どすっ飛ばしましたが、ブロックの追加方法の概要をざっくりと書いたつもりです。 次はJSXを自動変換する環境のセットアップ方法の他、PHPでのブロック追加についても紹介していきます。

3件のピン

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください