WPダイナミックブロック作成|記事への表示|WordPress|GutenBerg

前の記事でブロック実装まで書いた。

この記事では記事への表示を試す。

プラグインphpへの表示関数の追加

前記事にてプラグインのphpファイル(ここではblock-sample.php)に表示のためのざっくりコードを書いた。

function create_block_block_sample_block_init() {
	register_block_type(
		__DIR__,
		array(
			'render_callback' => 'block_sample_render',
		)
	);
}
add_action( 'init', 'create_block_block_sample_block_init' );

function block_sample_render($attr, $content) {
  return $attr['myTitle'];
}

レンダリングのための関数は2つの値を取る。

$attrattributeで設定した値
$contentネストされたブロック。省略可能

$attr

$attr には保存された属性のみが渡される。属性が保存されてない場合は設定があってもPHP側に値は読み込まれない。そのためjs側でattribute設定はあるが保存はされていない値についてはPHP側で呼び出すとエラーとなってしまうので、値チェックをするか、attribute設定をPHP側で行うかが必要。

attributeをPHP側に設定。

function create_block_block_sample_block_init() {
	register_block_type(
		__DIR__,
		array(
			'render_callback' => 'block_sample_render',
			'attributes' => [
				'myTitle' => [
					'type' => 'string',
					'default' => ''
				],
				'myContent' => [
					'type' => 'string',
					'default' => ''
				],
			]
  		)
	);
}

この場合JS側で書いたattributeは不要となり、消してしまってもedit.jsからPHPで設定したattributeにアクセスする感じで動作する。ただし編集画面のインスペクタなどのデフォルト値はJS側で設定したattributeの値が使われるとのことで、そのあたりはケースバイケースでよしなに。

HTMLの出力

ob_startなどを使ってHTMLで出力する。

インスペクターで入力された追加CSSクラスはclassNameで取得できるのでattributeを追加して表示側でクラス名として追加。

デフォルト値として設定されるセレクタは、今回のパッケージでつけたフォルダ名から自動生成されているやつ(おそらくwp-block-[フォルダ名]-[プラグインファイル名]というclass名になる)のようで、それに対してstyleをつけるとプレビュー画面と実際の表示でほぼ同じスタイルがあたるようにeditor.scssにて定義する。
※今回はインストール後にフォルダ名を手動で変えたので実際の構成とは異なっている。

function create_block_block_sample_block_init() {
	register_block_type(
		__DIR__,
		array(
			'render_callback' => 'block_sample_render',
			'attributes' => [
				'myTitle' => [
					'type' => 'string',
					'default' => ''
				],
				'myContent' => [
					'type' => 'string',
					'default' => ''
				],
				'className' => [
					'type' => 'string',
					'default' => ''
				],
			]
  		)
	);
}
add_action( 'init', 'create_block_block_sample_block_init' );

function block_sample_render($attr, $content) {
	ob_start(); // バッファリングを開始
?>
<div class="wp-block-create-block-block-sample <?php echo $attr['className']; ?>">
	<h3 class="myRichTextTitle"><?php echo $attr['myTitle']; ?></h3>
	<div class="myRichTextContent"><?php echo $attr['myContent']; ?></div>
</div>
<?php
	$output = ob_get_contents(); // バッファの内容(4〜7行目)を $output に取得
	ob_end_clean();  // バッファをクリアしてバッファ制御をオフに
	return $output;  //バッファの内容を出力
}
.wp-block-create-block-block-sample {
	border: 1px dotted #f00;
}

プレビュー

編集画面はjsにより生成されたDOMだが、実際のPHPによるレンダリングをプレビューすることができる。

編集画面を定義するedit.jsにServerSideRenderコンポーネントをimportして、returnにServerSideRenderを記述。

パラメータとして必ずブロック名が必要で、props.nameで取得したブロック名を入れる。

〜前略〜
import { ServerSideRender } from '@wordpress/editor';
〜中略〜
			<ServerSideRender
				block={props.name}
				attributes={{
					myTitle: myTitle,
					myContent: myContent,
				}}
				className='my-custom-ssr'
			/>

これで、編集ブロックの下にPHPレンダリングされたプレビューが追加される。style.scssとeditor.scssに同じCSSを当てていれば同じ見た目なのでプレビューの意味は無いが、両者が異なる場合は有効なのでは。

classNameにはPHPレンダリングされたプレビューを編集画面側でラップする要素のclassとして機能するので、編集画面においてのプレビュー固有のスタイルをあてられる。ボーダーとか。

でもこれだけだとあんまり意味ない感じ&見づらかったりもするので、切り替えたりもできる。

bloc-sample.phpに状態管理のフラグを追加。

register_block_type( 'wdl/dynamic-block-sample', array(
〜中略〜
			'isEditMode' => [
				'type' => 'boolean',
				'default' => true
			],
  		)
	);
}

edit.jsに切り替えのロジックを追加、およびそれらに必要なコンポーネントをimport。

import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';
import { RichText, BlockControls } from '@wordpress/block-editor';//BlockControlsを追加
import { ServerSideRender } from '@wordpress/editor';
import { Button, Toolbar } from '@wordpress/components';//追加
import { Fragment } from '@wordpress/element';//追加

export default function Edit(props) {
	const { attributes: { myTitle, myContent, isEditMode}, setAttributes } = props;//isEditModeを追加

//ブロックコントロールに新たなボタンを追加
	const getBlockControls = () => {
		return (
			<BlockControls>
				<Toolbar>
					<Button
						label={isEditMode ? "Edit" : "Preview"}
						icon={isEditMode ? "edit" : "format-image"}
						className="my-custom-button"
						onClick={() => setAttributes({ isEditMode: !isEditMode })}
					/>
				</Toolbar>
			</BlockControls>
		);
	};

	return (
//returnを配列に変更
		[
			getBlockControls(),//ボタン追加の関数を呼び出し
			<div { ...useBlockProps() }>
//フラグによる表示の分岐を追加
				{ isEditMode &&  // isEditMode が true(デフォルト) の場合 = 編集モード
					<Fragment>//要素をまとめるためのラッパーコンポーネント。それ自体は出力されない
						<RichText
							className="myRichTextTitle"
							value={myTitle}
							onChange={(newTitle) => setAttributes({ myTitle: newTitle })}
							tagName="h3"
							placeholder="タイトルを入力"
							keepPlaceholderOnFocus={true}
						/>
						<RichText
							className="myRichTextContent"
							value={myContent}
							onChange={(newContent) => setAttributes({ myContent: newContent })}
							tagName="div"
							multiline="div"
							placeholder="文章を入力"
						/>
					</Fragment>
				}
				{ !isEditMode &&   // isEditMode が false の場合 = プレビュー モード
					<ServerSideRender
						block={props.name}
						attributes={{
							myTitle: myTitle,
							myContent: myContent,
						}}
						className='my-custom-ssr'
					/>
				}
			</div>
		]
	);
}

ここまでで基本的な機能はある程度抑えているような気がするので、参考記事はまだ先があるけど本記事では一旦ここまで。

参考記事

全体的に以下の記事を踏襲して学習を進めました。

コードをある程度はコピペで自分の環境にあわせてちょこちょこ変更して動かしてみて、無事動いたものを本記事に書いています。

なので変数名等は流用のままになっている部分が少なからずありますがご容赦ください。

ぼやき

こういう仕様は常に更新されていて、最近はすぐに書き方が最新ではなくなるから厄介。

これを再び見るときにはどうせまた新たに学習が必要なんだろうな…と思うときりがなくて嫌になっちゃう。

最先端を常にキャッチアップされている方々はなんかもう違う世界の人みたいだ。