[WordPress] はじめてのGutenberg Custom Block Type

[前回の記事](https://qiita.com/ryo511/items/81d0b3c199c3a7725443)で、Gutenbergの開発環境を作り、中心的な概念であるブロック(Block)の仕組みを解説しました。

今回は、独自のブロックを作成する方法を解説します。

なお、本記事のサンプルコードは下記リポジトリで公開しています。
https://github.com/ryo-utsunomiya/my-gutenberg-ext

## 開発環境セットアップ

手元に手頃なWordPress環境がある方は、これにGutenbergを入れ、さらに適当なプラグインを追加してください。

本記事では、Gutenbergプロジェクトの公式開発環境を利用する方式でセットアップします。

まず、以下の2つのディレクトリを用意します。

- gutenberg: https://github.com/WordPress/gutenberg をcloneしたディレクトリ
- my-gutenberg-ext: 今回の実装に使用するディレクトリ

my-gutenberg-extはWordPressプラグインとして認識させる必要があるので、`my-gutenberg-ext.php`というファイルを配置します。中身は適当で良いです。

```php
<?php
/*
Plugin Name: my-gutenberg-ext
Plugin URI: https://github.com/ryo-utsunomiya
Description: Sample implementation of Gutenberg customize.
Version: 0.1
Author: Ryo Utsunomiya
Author URI: https://ryo511.info
License: GPLv2
*/
```

2018/2/19追記: Gutenberg 2.2で開発環境セットアップが変わったので、↓の設定だと動かないです。。。

次に、Docker設定を一部変更します。`docker/docker-compose.yml`を以下のように変更してください。
ここでは、my-gutenberg-extディレクトリをDockerコンテナ内のWordPressのプラグインとして追加するよう設定しています。

```diff
    volumes:
      - ../:/var/www/html/wp-content/plugins/gutenberg
+      - ../../my-gutenberg-ext:/var/www/html/wp-content/plugins/my-gutenberg-ext
```

↑だけだとボリュームは追加されないので、`bin/setup-local-env.sh`にも変更を加えます。

```diff
# Activate Gutenberg
docker run -it --rm --volumes-from wordpress-dev --network container:wordpress-dev wordpress:cli plugin activate gutenberg

+# Activate my-gutenberg-ext
+docker run -it --rm --volumes-from wordpress-dev --network container:wordpress-dev wordpress:cli plugin activate my-gutenberg-ext
```

この状態で`bin/setup-local-env.sh`を実行すると、my-gutenberg-extディレクトリがDockerにマウントされ、開発環境からプラグインが見えるようになります。

<img width="524" alt="Screen Shot 2017-12-09 at 19.32.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/33450/d4438dcb-820b-d3fd-ab6f-082614cfbce9.png">

次に、JavaScriptのJSXを使えるようにするため、ビルド環境を作ります。GutenbergでJSXの使用は必須ではないので、ここは飛ばしても構いません。

はじめに、my-gutenberg-extディレクトリの中で、`npm init`してpackage.jsonを作成します。次に、以下のコマンドを実行しましょう。

```
npm i babel-core babel-loader babel-plugin-transform-react-jsx babel-preset-env cross-env webpack
```

これで、ESNextとJSXのビルドに必要なライブラリが一式入ります。

次はwebpack.config.jsを以下の内容で作成しましょう。

```js
module.exports = {
    entry: './block.js',
    output: {
        path: __dirname,
        filename: 'block.build.js',
    },
    module: {
        loaders: [
            {
                test: /.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
            },
        ],
    },
};
```

ここでは、block.jsというファイルを起点にしてビルドを行い、ビルド結果はblock.build.jsに書き出す、という設定を行っています。

次に、Babelの設定ファイル(.babelrc)を作成します。

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

ここでのポイントは、transform-react-jsxプラグインのpragmaにwp.element.createElementを指定していることです。こうすると、JSXのコンパイル時にGutenbergのcreateElementメソッドが使用されるようになります。

最後に、package.jsonにスクリプトを追加します。

```js
  "scripts": {
    "build": "cross-env BABEL_ENV=default NODE_ENV=production webpack"
  },
```


これで、`npm run build`コマンドでビルドが行われるようになります。
ためしに、以下のコードをビルドしてみましょう。

```block.js
const hw = <h1>Hello World</h1>;
```

ビルド後のjsファイルには以下のようなコードが含まれるはずです。

```block.build.js
var hw = wp.element.createElement(
  "h1",
  null,
  "Hello World"
);
```

これでJSXのビルド環境は完成です。

## BlockとBlock Type

Gutenbergにはブロック(Block)の他に、ブロック型(Block Type)という概念があります。Block Typeを元にBlockが作成されるイメージです。Block Typeの定訳があるかは不明ですが、個人的には「ブロック型」と訳すのが良いのでは、と思ってます。

独自のブロック型を登録すると、Gutenbergエディタでそのブロックを利用できるようになります。

ブロック型を登録するには、(1) `registerBlockType`関数を使ってブロック型を定義するJavaScriptを書く (2) (1)のスクリプトをenqueするPHPコードを書く の2ステップが必要です。

まずは(1)からいきましょう。以下のように記述した上で、`npm run build`でビルドを行います。

```block.js
const { registerBlockType } = wp.blocks;
const blockStyle = { backgroundColor: '#900', color: '#fff', padding: '20px' };

registerBlockType('my-gutenberg-ext/hello-world', {
    title: 'Hello World',
    icon: 'universal-access-alt',
    category: 'layout',
    edit: () => <p style={ blockStyle }>Hello editor.</p>,
    save: () => <p style={ blockStyle }>Hello saved content.</p>,
});
```

Gutenbergは`wp`というグローバル変数を通して様々な機能を利用可能にしています。`wp.blocks`に`registerBlockType`関数があるので、これを利用しましょう。

`registerBlockType`関数の第1引数はブロックの名前です。'namespace/block-name'というフォーマットになっています。第2引数はブロック型の定義です。titleとcategoryが必須で、他にも重要なオプションがいくつかあります。

titleはGutenbergのUI上での表示名、categoryはUI上の分類です。`edit`関数はエディタの編集時の動作、`save`関数には記事を保存する際の動作を記述します。

ブロック型の定義はできたので、次にblock.build.jsをenqueしましょう。
以下のコードを追加します。

```my-gutenberg-ext.php
function my_gutenberg_ext_enqueue_block_editor_assets()
{
    wp_enqueue_script(
        'my-gutenberg-ext',
        plugins_url('block.build.js', __FILE__),
        array('wp-blocks', 'wp-element')
    );
}

add_action('enqueue_block_editor_assets', 'my_gutenberg_ext_enqueue_block_editor_assets');
```

この状態でエディタを開くと、以下のようにLayout Blocksの中に先ほど定義したブロック「Hello World」が表示されます。

<img width="373" alt="Screen Shot 2017-12-09 at 20.42.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/33450/4d54822e-260d-f8b3-e9f1-232bf3f88633.png">

このブロックを追加すると、以下のように`edit`関数の中で定義した文字列が表示されます。

<img width="435" alt="Screen Shot 2017-12-09 at 20.43.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/33450/83bedaa4-a67e-47de-9992-783d0d95da13.png">

この状態で記事を保存して公開すると、以下のようになります。

<img width="418" alt="Screen Shot 2017-12-09 at 20.45.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/33450/820ef902-81fe-90be-831b-54b4fd443219.png">

編集中の画面で出ていた文言（Hello editor.）ではない点に注目してください。最終的に記事として公開されるデータは、`save`関数によって決まります。今回は、`save`関数で「Hello saved content.」という文字列を静的に指定していました。

post_contentの中も見てみましょう。gutenbergのDockerを使っているなら、以下のコマンドでMySQLにログインできます。

```bash
docker exec -it docker_mysql_1 mysql -u root -pexample wordpress
```

最新記事のpost_contentがほしいので以下のクエリを実行します。

```sql
select post_content from wp_posts order by id desc limit 1;
```

post_contentは以下のようになっていました。

```html
<!-- wp:my-gutenberg-ext/hello-world -->
<p style="background-color:#900;color:#fff;padding:20px" class="wp-block-my-gutenberg-ext-hello-world">Hello saved content.</p>
<!-- /wp:my-gutenberg-ext/hello-world -->
```

このように、GutenbergのCustom Block Typeは、通常のWordPressプラグインと同様の枠組みで開発を進めることができます。

本記事ではセットアップに多くの分量を割いてしまったため、本格的なCustom Block Typeの作り方については別の記事に書こうと思います。