Before Gurenberg – ブロックの情報保存先、特にカスタムフィールド

SPONSORED LINK

前回まででブロックをいくつか作ってみたのだが、今回は情報の保存先という点について詳しく触れてみたい。

HTMLの属性・中身に情報を保存する

インタラクティブなHTML要素というのはそれほど多くない。テーマと管理画面で同じinput要素を表示する必要性はほぼないだろうが、ブロックの情報保存先としてわかりやすいので、まずこれから触れてみよう。

inputというブロックを用意し、inputタグをテーマ側にそのまま表示することとしよう。とりあえずブロックcapital-block-inputをPHPで登録し……

// Register block.
add_action( 'init', function() {
	// Register JS
	wp_register_script( 'capital-block-input', CAPITAL_BLOCK_ASSET_URL . 'assets/js/inputs.js', [ 'wp-element', 'wp-blocks' ], CAPITAL_BLOCK_VERSION, true );
	// Register block.
	if ( defined( 'GUTENBERG_VERSION' ) ) {
		// Alert Block.
		register_block_type( 'capital-block/input', [
			'editor_script' => 'capital-block-input',
		] );
	}
});

続いて、UIを提供するJSを記述する。

const {registerBlockType} = wp.blocks;
const inputStyle = {
  width: '100%',
};

registerBlockType('capital-block/input', {

  title: 'Input',

  icon: 'edit',

  category: 'formatting',

  attributes: {
    string: {
      type: 'string',
      source: 'attribute',
      selector: 'input',
      attribute: 'value'
    },
  },

  edit({attributes, setAttributes}) {
    return (
      <input type='text' value={attributes.string} style={inputStyle}
             onChange={(event) => setAttributes({string: event.target.value})}/>
    );
  },

  save({attributes}) {
    return (
      <input type='text' value={attributes.string} />
    );
  },

});
入力UI
保存されるコード。
実際の表示。

この場合、ポイントとなるのは、attribute > stringsourceselectorである。selectorに指定された場合、該当するタグが選択対象となる。ではそのタグの何を利用するのかというと、sourceattributeを指定しているので属性を使う。今回はinputタグを利用しているので、そのvalue属性を利用するというわけだ。

なお、この記述では、saveeditメソッドにまったく同じinputタグが使われているためこのような記述になっている。入力UIではinputを使いつつも、表示するものが例えばpタグならば、selectorpとする必要がある。要するに、attributesourceとは、「保存されたタグを編集用に復元するための方法」を記述するプロパティなのだ。

もしtextareaを使いたいのであれば、sourcehtmlにする必要がある。textareaはその他のinput要素と異なり、value属性を持たないからだ。

add_action( 'init', function() {
	// Register JS
	wp_register_script( 'capital-block-textarea', CAPITAL_BLOCK_ASSET_URL . 'assets/js/textarea.js', [ 'wp-element', 'wp-blocks' ], CAPITAL_BLOCK_VERSION, true );
	// Register block.
	if ( defined( 'GUTENBERG_VERSION' ) ) {
		// Alert Block.
		register_block_type( 'capital-block/textarea', [
			'editor_script' => 'capital-block-textarea',
		] );
	}
} );
const {registerBlockType} = wp.blocks;
const inputStyle = {
  width: '100%',
  padding: '10px',
  'box-sizing': 'border-box'
};

registerBlockType('capital-block/textarea', {

  title: 'TextArea',

  icon: 'edit',

  category: 'formatting',

  attributes: {
    string: {
      type: 'string',
      source: 'text',
      selector: 'textarea',
    },
  },

  edit({attributes, setAttributes}) {
    return (
      <textarea style={inputStyle} rows='3' onChange={(event) => setAttributes({string: event.target.value})}>{attributes.string}</textarea>
    );
  },

  save({attributes}) {
    return (
      <textarea style={inputStyle} rows='3'>{attributes.string}</textarea>
    );
  },

});
編集UI
実際の表示

ちなみにstyle属性を利用すると、CSSを管理画面で保存することができてしまうが、ブラウザ互換性などを考えると結局外部CSSを登録する羽目になるだろう。また、テーマのデザインが変更になった場合はこのstyle属性が邪魔になる。Scoped CSSなどを使う場合、「テーマの変更」という問題は避けて通れないだろう。

リッチテキスト

Gutenbergのようなエディタを利用する場合、当然だがデザインを施されたコンポーネントを直接編集できるようにしたいだろう。その場合は前回と同じくリッチテキストコンポーネントを登録する必要がある。リッチテキストコンポーネントが他と異なるのは、typearraysourcechildrenを指定する点だ。前回書いた通り、リッチテキストは複数のDOMの配列と解釈される。

const { registerBlockType } = wp.blocks;
const { RichText }          = wp.editor;

registerBlockType( 'capital-block/richtext', {

  title: 'RichText',

  icon: 'edit',

  category: 'formatting',

  attributes: {
    content: {
      type: 'array',
      source: 'children',
      selector: 'div',
    }
  },

  edit({attributes, setAttributes}) {

    return (
      <RichText
        tagName="div"
        onChange={ (content) => setAttributes({content: content}) }
        value={attributes.content}
      />
    );
  },

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

} );
編集UI
実際の表示
保存されるデータ

カスタムフィールド

さて、これまでのブロックはすべて、投稿本文に保存するデータとそのUIを提供するブロックだった。しかし、カスタムフィールドにデータを保存したい場合はどうすればよいのだろうか? たとえば、日本のご当地ネタあるあるをまとめているサイトを考えてみよう。「その都道府県にしかない体操あるある」という記事を作成し、あるあるが10件登録されているとする。そのうち1つは「千葉県のなのはな体操」というブロックだったとしよう。そのブロックの記事内容はともかくとして、ブロックに「千葉県」というカスタムフィールドが付与されていたら、meta_queryを使った検索などで「千葉県について書いている記事一覧」を提供できるだろう。

前置きが長くなったが、このためにはブロック属性のsourcemetaを指定する必要がある。

const {registerBlockType} = wp.blocks;
const prefectures = ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県','茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県','新潟県','富山県','石川県','福井県','山梨県','長野県','岐阜県','静岡県','愛知県','三重県','滋賀県','京都府','大阪府','兵庫県','奈良県','和歌山県','鳥取県','島根県','岡山県','広島県','山口県','徳島県','香川県','愛媛県','高知県','福岡県','佐賀県','長崎県','熊本県','大分県','宮崎県','鹿児島県','沖縄県'];

registerBlockType('capital-block/meta', {

  title: 'Meta',

  icon: 'generic',

  category: 'formatting',

  attributes: {
    prefecture: {
      type: 'string',
      source: 'meta',
      meta: 'prefecture' // ここの値はカスタムフィールド名
    },
  },

  edit({attributes, setAttributes}) {
    return (
      <select onChange={(event) => setAttributes({prefecture: event.target.value})}>
        {prefectures.map((pref) => {
          return (
            <option value={pref} selected={pref === attributes.prefecture}>{pref}</option>
          )
        })}
      </select>
    );
  },

  save() {
    return null;
  },
});

これだけではダメで、register_metaという関数を用い、PHP側でそのカスタムフィールドをアクセス可能にする必要がある。また、今回はrender_callackを用い、その都道府県の地図を表示してみよう。

add_action( 'init', function() {
	// Register JS.
	wp_register_script( 'capital-block-meta', CAPITAL_BLOCK_ASSET_URL . 'assets/js/meta.js', [ 'wp-element', 'wp-blocks' ], CAPITAL_BLOCK_VERSION, true );
	if ( defined( 'GUTENBERG_VERSION' ) ) {
		// Register block.
		register_block_type( 'capital-block/meta', [
			'editor_script'   => 'capital-block-meta',
			'render_callback' => function( $attributes, $content = '' ) {
				// Get post meta.
				$pref = get_post_meta( get_the_ID(), 'prefecture', true );
				$src  = add_query_arg( [
					'key'  => GOOGLE_MAP_EMBED_KEY,
					'q'    => rawurlencode( $pref ),
					'zoom' => 16,
				], 'https://www.google.com/maps/embed/v1/place' );
				return <<<HTML
<iframe class="event-map-iframe" src="{$src}" frameborder="0" style="width: 100%" height="300"></iframe>
HTML;
			},
		] );
		// Register meta
		register_meta( 'post', 'prefecture', array(
			'show_in_rest' => true,
			'single'       => true,
			'type'         => 'string',
		) );
	}
} );
編集UI
保存されるデータ。カスタムフィールドなので空っぽ。
DBに保存されるデータ。きちんと保存されている!
Google Mapのembed APIで都道府県の地図を表示。もちろん、カスタムフィールドなので、検索もできる。

ちなみに、カスタムフィールドのsingle => trueを設定すると、何個ブロックを追加しても自動的に内容が同期されるようになる。これはReactを一度でも書いたことがある人なら、「Gutenbergやるじゃん!」という感想を抱くはずだ。

まとめ

というわけで、カスタムフィールドを含めた高度な内容について今回は触れてみた。他にあると便利そうな機能としては、インクリメンタルサーチがある。たとえば、他の投稿やタクソノミー、ユーザーを選ぶときに、ある程度いい感じのインクリメンタルサーチがあると便利だというのは想像に難くないだろう。しかし、このAPIはまだ未整備でドキュメントが作られていないので、おいおい触れていきたい。

最後になるが、有料会員向けにソースコードを提供して終わりとする。

続きの 4% を読み、添付されたファイルにアクセスするには、Gumroadでライセンスキーを取得してください!

Club Capital P

Club Capital PはCapital Pのファンクラブです。有料会員制となっており、Gumroad経由でサブスクリプションをご購入いただき、ライセンスキーを登録いただくことで、会員特典を受け取ることができます。