STUDY

WordPress Gutenbergブロック開発

こんにちは!3児のパパエンジニアの笹垣です。弊社では、WordPressをベースにしたサイト制作についてもよくご相談をいただくのですが、今回はWordPressバージョン5から導入された「Gutenberg」のカスタムブロック開発について備忘録も兼ねてご紹介します。

はじめに

WordPressバージョン5が正式リリースされてから4か月。
最新機種の登場はもちろん、Mac OSやiOSのバージョンアップが大好物な自分にとって、WordPress v5リリース時はそこそこ興奮したのを覚えています。

WordPress 5.0 “ベボ”

v4以前のWysiwygエディター(Classic Editor)は“ブログ”という側面が強く、エディターのツールバーと改行を駆使して記事を書く形で、行間の調整に苦労したり編集者ごとのトンマナの統一が難しく、案件によってはカスタムフィールドを複数作成して回避するなどの対応が必要でした。

一方v5で採用されたGutenbergは、“ブロック”単位で記事を書いていくスタイルで、記事を書く上で最低限必要なブロック(パーツ)はデフォルトで備わっているため、上述のような何十個ものカスタムフィールドを作成することなく、トンマナが保たれた綺麗な記事・ページの作成が可能です。

Gutenbergについては賛否両論ありますが、個人的にはサイト制作側・コンテンツ制作者(記事を書く人)双方にとってとても有用な機能だと思います!

そんなGutenbergですが、案件によっては標準のブロックでは足りない場面が出てきます。
そんな時は、独自のブロックを開発しさらに編集画面の使い勝手を良くしましょう!

独自のブロックを追加するには、

  • プラグインとして追加する必要がある
  • メインの言語はJavaScript

というのが大きなポイントになると思うのですが、従来のWordPress開発とは少し違った知識が必要になるため、はじめてブロック開発をする方にとっては若干取っ付きにくさがあると思います。
ブロック開発については色々なサイトで説明されていますが、本記事では先日実際に案件で必要になった「定義リスト(dl,dt,dd)ブロック」の追加方法についてご紹介します。

ブロック開発

必要な知識

Gutenbergには、新しめのフロントエンド技術が使われており、以下の知識が必要になります。

  • React.js
  • ES6(ES2015)
  • Babel
  • webpack

環境構築

まずはターミナルでプラグインディレクトリに移動します。

$ cd ~/Documents/develop/samplesite.com/htdocs/cms/wp-content/plugins

カスタムブロックプラグイン用のディレクトリを作成します。

$ mkdir custom-gutenberg-block

npm initでpackage.jsonを生成します。

$ cd custom-gutenberg-block
$ npm init

package.jsonを更新します。

package.json{
  "name": "custom-gutenberg-block",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
    "dev": "cross-env BABEL_ENV=default webpack --watch",
    "lint": "eslint .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {},
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-eslint": "^8.0.3",
    "babel-loader": "^7.1.2",
    "babel-plugin-transform-object-rest-spread": "^6.26.0",
    "babel-plugin-transform-react-jsx": "^6.24.1",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.6.1",
    "cross-env": "^5.1.3",
    "eslint": "^4.13.1",
    "eslint-config-wordpress": "^2.0.0",
    "eslint-plugin-jest": "^21.5.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-react": "^7.5.1",
    "webpack": "^3.10.0"
  }
}

パッケージをインストールします。

$ npm i

続いて、Babelによるトランスパイルとwebpackの設定を行います。
すべてのブラウザがES6に対応しているわけではないため、Babelを使ってES5に変換する必要があります。
プラグインディレクトリ直下に .babelrc を作成し内容を編集します。

.babelrc{
  "presets": [
    [
      "env",
      {
        "modules": false
      }
    ]
  ],
  "plugins": [
    [
      "transform-react-jsx",
      {
        "pragma": "wp.element.createElement"
      }
    ]
  ]
}

webpackを使うことで、トランスパイル後のJSのバンドル(ひとまとめにすること)とES6のモジュール機構が使えるようになります。
プラグインディレクトリ直下に webpack.config.js を作成し内容を編集します。

webpack.config.jsmodule.exports = {
  entry: './src/block.js',
  output: {
      path: __dirname + '/build',
      filename: 'block.js',
  },
  module: {
      loaders: [{
        test: /.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      }, ],
  }
};

ここまでで、Gutenbergブロック開発の準備が整いました。

ブロックタイプの定義

Gutenbergはwpというグローバル変数を通して様々な機能を利用可能にしています。wp.blocksにあるregisterBlockType関数を利用しブロックタイプを登録します。

今後、定義リスト以外のブロックも作成予定のため、ブロックの定義は各ファイルに分割しblock.jsでブロックをまとめて登録します。

./src/block.js// definition list
import Dl from './blocks/definition-list/dl.js';
import Dt from './blocks/definition-list/dt.js';
import Dd from './blocks/definition-list/dd.js';

const { registerBlockType } = wp.blocks;

registerBlockType( Dl.name, Dl );
registerBlockType( Dt.name, Dt );
registerBlockType( Dd.name, Dd );

続いて、各ブロックの中身を作成します。
dlブロックは、dt,dd要素の親となるため、InnerBlocksだけを保持します。

./src/blocks/definition-list/dt.jsconst { InnerBlocks } = wp.editor;
export default {
  name: 'custom-gutenberg-block/dl',
  title: '定義リスト(dl)',
  icon: 'editor-alignleft',
  category: 'common',
  keywords: [],
  edit({attributes, className}){
    // 許可されるブロックを登録
    const allowedBlocks = [ 'custom-gutenberg-block/dt', 'custom-gutenberg-block/dd' ];
    return (
      <div className={className}>
        <InnerBlocks allowedBlocks={allowedBlocks} templateLock={false} />
      </div>
    )
  },
  save({className}){
    return (
      <dl className={className}>
        <InnerBlocks.Content />
      </dl>
    )
  }
};

allowedBlocks属性で、中に入れられるブロックを指定します。

dtブロック、ddブロックは、parent属性によって、dlブロックの子ブロックであることを明示的に宣言します。これにより、定義リストを選択したときのみ表示されるようになります。

./src/blocks/definition-list/dt.jsconst { RichText } = wp.editor;
export default {
  name: 'custom-gutenberg-block/dt',
  title: 'dt',
  icon: 'editor-alignleft',
  category: 'common',
  parent: [ 'custom-gutenberg-block/dl' ],
  attributes: {
    content: {
      source: 'html',
      selector: 'dt'
    }
  },
  edit({attributes, setAttributes, className}){
    return (
      <RichText className={className} tagName='div' value={attributes.content} onChange={(content)=>setAttributes({content})}/>
    );
  },

  save({attributes}){
    return (
      <RichText.Content tagName='dt' value={attributes.content} />
    )
  }
};
./src/blocks/definition-list/dd.jsconst { RichText } = wp.editor;
export default {
  name: 'custom-gutenberg-block/dd',
  title: 'dd',
  icon: 'editor-alignleft',
  category: 'common',
  parent: [ 'custom-gutenberg-block/dl' ],
  attributes: {
    content: {
      source: 'html',
      selector: 'dd'
    }
  },
  edit({attributes, setAttributes, className}){
    return (
      <RichText className={className} tagName='div' value={attributes.content} onChange={(content)=>setAttributes({content})}/>
    );
  },

  save({attributes}){
    return (
      <RichText.Content tagName='dd' value={attributes.content} />
    )
  }
};

最後にnpm run buildします!

ブロック用JSの読み込み

作成したブロックを編集画面で使用するために、プラグインを作成します。

index.php/**
 * Plugin Name: Custom Gutenberg Block
 * Plugin URI: 
 * Description: This is a custom block for Gutenberg editor by ARCHETYP Inc.
 * Version: 1.0.0
 * Author: ARCHETYP Inc.
 *
 * @package custom-gutenberg-block
 */

defined( 'ABSPATH' ) || exit;

define('VIRSION', '1.0.0');

/**
 * Registers all block assets so that they can be enqueued through Gutenberg in
 * the corresponding context.
 *
 * Passes translations to JavaScript.
 */
function custom_gutenberg_register_block() {

  if ( ! function_exists( 'register_block_type' ) ) {
    // Gutenberg is not active.
    return;
  }

  wp_register_script(
    'custom-gutenberg-block',
    plugins_url( 'build/block.js', __FILE__ ),
    array( 'wp-blocks', 'wp-editor', 'wp-element' ),
    VIRSION
  );

  register_block_type( 'custom-gutenberg-block/custom-gutenberg-block', array(
    'editor_script' => 'custom-gutenberg-block',
  ) );
}
add_action( 'init', 'custom_gutenberg_register_block' );

プラグインを有効化します。

完成

以上で定義リストブロックの追加は完了です!
他のブロックを開発する機会があったらまたご紹介します!