未来の自分のためのWEB制作メモ

WordPress REST APIでユーザー情報を取得してブロックエディタで利用する方法

この記事はブロックのサイドバーに任意にユーザーを選択するセレクトボックスを表示させる方法の覚書です。WordPressのブロックでREST APIを利用したい方には参考になると思います。

この記事で作るユーザー情報ブロックの仕様

事の発端はクライアントから「任意のユーザー情報を表示するブロックを作って欲しい」という依頼を受けたことです。完成品はこんな感じです。

ブロックの具体的な仕様は次のようになります。

  • ユーザーIDを設定してその名前やアバター画像、プロフィール情報を表示させる
  • ユーザー情報が編集されたら自動的に更新

データベースの内容に応じて中身が変わるので、表示部分はサーバーサイドレンダリングを利用することにしました。ブロックの設定項目はデザイン面で色々な装飾をする必要もないのでユーザーIDのみです。

簡単に作るならユーザーIDをテキストボックスに入力してもらえばOKですが、WordPressはユーザーIDがすぐに分かる仕様になっていないので使い勝手がよくありません。そもそも「どうやってユーザーIDを調べるの?」となる人もいると思います。
そう考えると理想は「ユーザー一覧を表示してそこから選べるようにする」ということになります。

では、どうやってブロックエディタ内にユーザー情報を表示させるかですが、次の2通りの方法があります。

  1. wp_localize_scriptsでHTLMのヘッダーに変数として書き出す
  2. REST APIでデータベースから直接取得する

1番の方法はPHPのスキルがあれば簡単に実装できますがイマイチスマートではありません。そこで、今回はREST APIを使うことにしました。

ユーザー情報を取得するエンドポイント

WordPressのREST APIでユーザー情報を取得するためのエンドポイントは次のようになります。

/wp/v2/users/

このままだとユーザー情報がいろいろ取得できますが、グローバルパラメータのfieldsを使うことで情報を絞り込むことができます。例えば次のように書くとユーザー名、ユーザースラッグ、ユーザーIDのみが返されます。

/wp/v2/users/?_fields=name,slug,id

ブロックエディタでのREST APIの実装

ブロックエディタ内でREST APIを利用したい場合、apiFetch()を使います。次のソースでは取得したユーザーのデータからユーザー名とIDを取り出してSelectControlで使うための配列を作っています。

const authors = [];
authors.push({ label: 'ユーザを選択してください', value: 0 });
wp.apiFetch({ path: '/wp/v2/users/?_fields=name,slug,id' }).then(
	users => {
		users.forEach(user => {
			authors.push({ label: user['name'], value: user['id'] });
		})
	}
);

このソースの内容を簡単に説明すると、usersの中に全ユーザーの情報が入っているので、それをforEachでユーザー毎にとりだしてauthorsに追加しています。

これを利用したブロック全体のソースコードは次のようになります。

import {
	InspectorControls,
} from '@wordpress/block-editor';
import {
	PanelBody,
	SelectControl,
} from '@wordpress/components';
const { registerBlockType } = wp.blocks;
const { serverSideRender: ServerSideRender } = wp;
const { Fragment } = wp.element;

//ユーザー情報を取得
const authors = [];
authors.push({ label: 'ユーザを選択してください', value: 0 });
wp.apiFetch({ path: '/wp/v2/users/?_fields=name,slug,id' }).then(
	users => {
		users.forEach(user => {
			authors.push({ label: user['name'], value: user['id'] });
		})
	}
);

registerBlockType('my-blocks/author-info', {
	title: 'ユーザー情報ブロック',
	discription: 'ユーザー情報を表示',
	icon: 'id-alt',
	category: 'my-blocks',
	attributes: {
		id: {
			type: 'string',
			default: 0
		},
	},

	edit: (props) => {
		const {
			attributes: {
				id,
			},
			setAttributes,
			className,
		} = props;

		return (
			<Fragment>
				<InspectorControls>
					<PanelBody title={'ユーザー情報設定'}>
						<SelectControl
							label={'ユーザー選択'}
							value={id}
							options={authors}
							onChange={(value) => setAttributes({ id: value })}
						/>
					</PanelBody>
				</InspectorControls>
				<div className={className}>
					<ServerSideRender
						block="my-blocks/author-info"
						attributes={props.attributes}
					/>
				</div>
			</Fragment >
		);
	},

このコードによってサイドバーには次のように表示されます。ユーザー選択のセレクトボクスをクリックするとユーザーの一覧が表示されます。

ポイントはユーザー情報をREST APIで取得する部分をregisterBlockTypeより前に持ってきているところです。

最初はregisterBlockTypeedit:props内に書いていたのですが、REST APIがデータを取得する前にSelectBoxのレンダリングが終わってしまい、セレクトボックスの内容が正しく表示されませんでした。

しばらく悩んだのですが、このコードのようにregisterBlockTypeの外で定義しておけばAPIからデータを取得してからのレンダリングとなり意図通りにセレクトボックスが表示されます。下記の記事で同じ事に悩んでいる方がいて参考になりました。

Indeed! If you load it before the edit: part, it gets loaded with the page before the elements are rendered. The constants are the FIRST thing in your block js file. Before you even use registerBlockType you define the constants and give them value. – Chad Holden Nov 29 ’18 at 14:41

categories – How would I get a taxonomy/category list inside a Gutenberg block? – WordPress Development Stack Exchange

サーバーサイドレダリングの実装

サーバーサイドレダリングの部分は次のようなソースになります。今回は表示部分にショートコードを使っています。

register_block_type(
	'my-blocks/author-info',
	[
		'editor_style'    => 'my-blocks-editor-css',
		'editor_script'   => 'my-blocks-js',
		'attributes'      => [
			'id' => [
				'type' => 'string',
			],
		],
		'render_callback' => function( $attr, $content = '' ) {
			$attr = wp_parse_args(
				$attr,
				[
					'id' => 0,
				]
			);
			return do_shortcode( '[author id="' . $attr['id'] . '"]' );
		},
	]
);

/**
 * ユーザ情報ボックスを表示するショートコード
 */

add_shortcode( 'author', 's_code_author_box' );

function s_code_author_box( $atts, $content = null ) {
	$atts = shortcode_atts(
		array(
			'id' => '',
		),
		$atts
	);

	$author_data = get_userdata( intval( $atts['id'] ) );
	if ( ! $author_data ) {
		return '<p class="no_author">投稿者を指定してください。</p>';
	}

	$author_info   = $author_data->description;
	$author_name   = $author_data->display_name;

	$link_text = $author_name . 'が書いた記事一覧';

	ob_start();
	?>
	<div class="author_profile_box">
		<div class="author_profile_box-header">
			<div class="author_profile_box-face_image">
				<?php echo get_avatar( $author_data->ID, AUTHOR_AVATAR, '', $author_name ); ?>
			</div>
			<div class="author_profile_box-name"><?php echo esc_html( $author_name ); ?></div>
		</div>
		<div class="author_profile_box-description">
			<?php echo esc_html( $author_info ); ?>
		</div>
		<div class="author_profile_box-more_link">
			<a href="<?php echo esc_ulr( get_author_posts_url( $author_data->ID ) ); ?>"><?php echo esc_html( $link_text ); ?></a>
		</div>
	</div>
	<?php
	$src = ob_get_clean();

	return $src;
}

このソースのショートコードは分かりやすくするために必要最低限のことしかしていません。実際はエラーチェックや空白時に余計なソースを吐き出さないなど色々な処理を追加する必要があります。
また、この記事の趣旨から離れるのでCSSは掲載していません。

ショートコードとして作っておくとクラシックエディタ環境でも同じようにコンテンツを表示させることができるようになります。これは意外と便利で、ウィジェット内やクラシックエディタにしか対応していないカスタム投稿タイプでも使えます。それもあって私はサーバーサイドレンダリングを使う時は全てショートコードにしています。

あと、ショートコードにしておくとHTMLを大きく変えるような変更があってもブロックが壊れないというメリットもあります。

まとめ

WordPressのREST APIをつかってブロックエディタにデータを渡す方法、いかがだったでしょうか?簡単な記述で柔軟にデータを渡すことができるのでアイデア次第で色々なブロックが作れると思います。

是非参考にしてみてください。

関連記事

  1. no-image
    WordPressの管理画面に再利用ブロック一覧を表示させる
    Gutenberg
  2. no-image
    Gutenbergのデフォルトスタイルの読み込みを止める方法
    Gutenberg
  3. no-image
    GutenbergでServerSideRender を使う場合の注意点
    Gutenberg
  4. no-image
    Gutenbergで管理画面のタクソノミーをチェックボックスにする方法
    Gutenberg
  5. no-image
    GutenbergのRangeControlのリセットボタンで初期値に戻る方法
    Gutenberg
  6. no-image
    Gutenberg のRichTextの使い方・注意点
    Gutenberg
  7. no-image
    ダイナミックブロックの表示内容を管理画面とフロントエンドで切り替える方法
    Gutenberg
  8. no-image
    Gutenbergの国際化対応の手順(5.8対応)
    Gutenberg

コメント

この記事へのコメントはありません。

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)