テーマに組み込まれた記事目次を表示させる機能をブロック対応するためにダイナミックブロックを使ったのですが、どうしても管理画面とフロントエンドで処理内容を変える必要があり苦労したのでそのメモです。
ダイナミックブロックとは?
ダイナミックブロックというのは、例えば更新記事一覧のようにコンテンツの内容がその時の状況によって変化するブロックのことです。詳しい説明は公式サイトに書いてあります。
ダイナミックブロックでは実際に記事内に保存されるのは下記のようなHTMLコメントだけとなります。
<!-- wp:my-blocks/toc {"close":false,"numbering":true,"numberingHierarchy":false} /-->
このコメントに保存されたパラメータを利用してPHPで動的にコンテンツを生成して表示するのですが、これってよく考えるとショートコードと同じですよね。これを利用するとJavaScriptでコードを書かなくても既存のショートコードを手軽にブロック化できます。
ただし、このままでは管理画面には何も表示されません。管理画面にもフロントエンドと同じものを表示させる場合は次の2通りの方法があります。
- JavaScriptで管理画面表示用のコードを書く
ServerSideRender
を使ってPHPを流用する
WordPress公式の推奨はもちろん1番ですが、すでにPHPで同じことができているのにJavaScriptで書き直すのはちょっと無駄手間な気がします。
ただ、1番にすると今回私が困ったようなフロントエンドと管理画面で表示内容を少し変えたい場合でも柔軟に対応できるのでJavaScriptが得意な方はこちらの方が良いかもしれません。
この記事では2番目のServerSideRender
を使いつつフロントエンドと管理画面で内容を変えたい場合の説明になります。既存のPHPのコードをそのまま使いたい!という方には役に立つと思います。
is_admin() は使えない
単純に考えるとPHPでis_admin()
を使って管理画面かどうか判定すればよさそうですがこれだと上手くいきません。
ServerSideRender
で呼び出されるコードの処理はWordPress全体の処理とは別物扱いのようで(そりゃそうか?)既存の条件分岐関数では判定ができないです。
いろいろ試してみましたがServerSideRender
で呼び出されたときにWordPressの関数を利用して管理画面かフロントエンドかを判定する方法は見つけることができませんでした。
attributes
を上手く利用する
そこで目を付けたのが“ブロックの属性を保存するときデフォルト値は投稿内に保存されない”という点です。
ブロックで定義するattributes
の初期値とPHPのコールバック関数でのattributes
の初期値を変えておくことで管理画面内かフロントエンドかを条件分岐させることに成功しました。
文章で説明しても分かりにくいのでとりあえずサンプルコードを。まずはブロックを次のように定義します。ポイントはattributes
の定義にあるisAdmin
です。ここではboolean型としましたが区別できればstringでも何でもよいです。
registerBlockType('my-blocks/toc', {
title: '目次',
discription: '記事の目次を表示するブロック',
icon: 'list-view',
category: 'my-blocks',
attributes: {
title: {
type: 'string',
default: '目次'
},
close: {
type: 'boolean',
default: 0
},
// 中略
isAdmin: { // 管理画面かどうか判定するための属性。ここでは初期値は1にしておく
type: 'boolean',
default: 1,
},
},
edit: (props) => {
const {
attributes: {
title,
close,
// 中略
},
setAttributes,
className,
} = props;
return (
<Fragment>
<InspectorControls>
<PanelBody title={'目次設定'}>
<TextControl
label={'タイトル'}
value={title}
onChange={(value) => setAttributes({ title: value })}
/>
<ToggleControl
label={'最初は目次を非表示にする'}
checked={close}
value={close}
onChange={(value) => setAttributes({ close: value })}
/>
// 中略
</PanelBody>
</InspectorControls>
<div className={className}>
<ServerSideRender
block="my-blocks/toc"
attributes={props.attributes}
/>
</div>
</Fragment >
);
},
});
次にPHPのコードです。
register_block_type(
'my-blocks/toc',
[
'editor_style' => 'my-block-editor-css',
'editor_script' => 'my-block-js',
'attributes' => [
'title' => [ 'type' => 'string' ],
'close' => [ 'type' => 'boolean' ],
// 中略
'isAdmin' => [ 'type' => 'boolean' ],
],
'render_callback' => function( $attr, $content = '' ) {
$attr = wp_parse_args(
$attr,
[
'title' => '目次',
'close' => '',
// 中略
'isAdmin' => 0, // ここでの初期値を0にしておく
]
);
if ( $attr['isAdmin'] ) {
return admin_mokuji( $attr ); // 管理画面表示用の処理
} else {
return frontend_mokuji($attr); // フロントエンド表示用の処理
}
},
]
);
render_callback
の$attr
というのはブロックの属性値で、wp_parse_args
で初期値との比較をして未定義の属性値があれば初期値に置き換えられます。
管理画面で呼び出された場合
管理画面側で呼び出されたときはattributes={props.attributes}
と書いてある通り、ブロックで定義・変更された属性値がコールバック関数に渡されます。つまりこのときisAdmin
の値は1です。
フロントエンドで呼び出された場合
フロントエンドでは投稿内に保存された属性値を読み取りそれを$attr
としてコールバック関数に渡します。冒頭にも書いたようにブロックエディタでは下記のようなコメント形式で属性値が投稿内に保存されています。
<!-- wp:my-blocks/toc {"close":false,"numbering":true,"numberingHierarchy":false} /-->
ここでのポイントは管理画面で変更されなかった属性はここに書き込まれないということです。isAdmin
は管理画面では値を変更することができない属性なので投稿内にその値が書き込まれることはありません。
そのためwp_parse_args
の処理の際にisAdmin
は未定義となり、PHP側で設定してる初期値(0)が設定されることになります。
こうしてコールバック関数の中で管理画面とフロントエンドで処理を分岐させることができるようになります。
今回は既存のテーマに組み込まれた目次表示機能をそのままブロックに流用しようとしたため、フロントと管理画面で処理を変えたいということになりました。
恐らくこれはかなり希なケースなので同じようなことを必要とする人が居るかどうかわかりませんが、もし居たら参考にしてみてください。
あと、もっとスマートな方法があれば是非コメントに書いていただけると嬉しいです。
コメント
この記事へのコメントはありません。