前回はブロックの変換についてでした。
WordPressのブロック開発メモ その13 ブロックの変換
今回はウィジェットと、ブロックへの変換についてです。
これまでのウィジェット
WordPressにはウィジェットという機能があります(外観→ウィジェット)。
以前はウィジェットはPHPで書かれていました。
しかし、そんなウィジェットにもブロックの波がやってきてオワコン化してしまいました。
だって投稿と同じくウィジェットもブロック追加していけばすむ話だよね!
ということでウィジェットのエディタでも段落ブロックなどのブロックを追加していく式に変わりました
段落ブロックやクラゲブロック、詩ブロックなどを追加してます。
じゃー昔のPHP製ウィジェットはどうなるの!?
で、それをホストするブロックがレガシーウィジェットブロック('core/legacy-widget')
です。
リストには従来のウィジェット
なんて項目になってますね。
仕組みはREST API
を通じてウィジェットにアクセスします。
前半では従来のPHPでのウィジェットの作り方を、
後半ではPHP製ウィジェットをブロックに変換する方法を紹介します。
後半は新しく作ったブロックを作って設定しなおせばいいわけでけど、
配布物に旧ウィジェットが含まれるような場合以外は必要性にかけるんですけど
まぁ興味がある人用です。
従来のウィジェット
まずは従来でのウィジェットを作成、追加する方法です。
add_action('widgets_init', function(){
$widget = new class extends WP_Widget
{
public function __construct()
{
parent::__construct(
'kikurage',
'キクラゲ ウィジェット',
[
]
);
}
public function widget($args, $instance)
{
$title = $instance['title'] ?? '-';
$title = apply_filters('widget_title', $title);
$message = $instance['message'] ?? '-';
$befWidget = $args['before_widget'];
$aftWidget = $args['after_widget'];
$befTitle = $args['before_title'];
$aftTitle = $args['after_title'];
$titles = $title ? [$befTitle, $title, $aftTitle] : [];
echo join([$befWidget, ...$titles, $message, $aftWidget]);
}
public function form($instance)
{
// Title values
[$tid, $tName, $tValue] = [
$this->get_field_id('title'),
$this->get_field_name('title'),
$instance['title'] ?? 'NO TITLE'
];
// Message values
[$mid, $mName, $mValue] = [
$this->get_field_id('message'),
$this->get_field_name('message'),
$instance['message'] ?? 'NO MESSAGE'
];
echo join([
// Title Field
sprintf('<label for="%s">%s</label>', $tid, __('Title:')),
sprintf('<input type="text" id="%s" name="%s" value="%s" />', $tid, $tName, $tValue),
// Message Field
sprintf('<label for="%s">%s</label>', $mid, 'メッセージ:'),
sprintf('<input type="text" id="%s" name="%s" value="%s" />', $mid, $mName, $mValue),
]);
}
public function update($new_instance, $old_instance)
{
return $new_instance;
}
};
// select * from wp_options where option_name like '%kikurage%' limit 3;
register_widget($widget);
});
まずWP_Widget
を継承したインスタンスを生成します。
親コンストラクタに識別ID(今回はkikurage
)とウィジェット名(今回はキクラゲ ウィジェット
)を渡します。
widget()
メソッドは表示用のHTMLを、form()
メソッドは入力フォームのHTMLを出力します。
では従来のウィジェットの追加方法を見てみましょう。
外観
からウィジェット
を開く。
追加(+
)をクリック
従来のウィジェット
を選択
今回作成したウィジェット名のキクラゲウィジェット
を選択
form()
メソッドで出力したHTMLが表示。
タイトルとメッセージの入力欄が現れる
適当に入力する。
フォーカスを外すとwidget()
メソッドで出力したHTMLが表示。
入力しただけでは保存されないので保存ボタン
を押す。
そしてフロントエンドを開いてみると、ウィジェットがサイドバーに表示されている。
ところでデータはどこに保存してあるのか!
答えはデータベースのwp_options
である。
select * from wp_options where option_name like '%kikurage%';
オプション名は命名規則で widget_
とその後に識別IDがくっついた名前になる。
今回の場合はwidget_kikurage
にデータがシリアライズして保存してある。
もう一個同じウィジェットを追加してみよう。
データベースを見るとこうなってる。
同じカラム名(widget_kikurage
)にマージされてますね。
さて、フォームを見てみよう。
public function form($instance)
{
// Title values
[$tid, $tName, $tValue] = [
$this->get_field_id('title'),
$this->get_field_name('title'),
$instance['title'] ?? 'NO TITLE'
];
// Message values
[$mid, $mName, $mValue] = [
$this->get_field_id('message'),
$this->get_field_name('message'),
$instance['message'] ?? 'NO MESSAGE'
];
echo join([
// Title Field
sprintf('<label for="%s">%s</label>', $tid, __('Title:')),
sprintf('<input type="text" id="%s" name="%s" value="%s" />', $tid, $tName, $tValue),
// Message Field
sprintf('<label for="%s">%s</label>', $mid, 'メッセージ:'),
sprintf('<input type="text" id="%s" name="%s" value="%s" />', $mid, $mName, $mValue),
]);
}
DOMを覗くとよくわかる。
コメントに出力結果を書いているとおり、以下の規則でIDや名前を作成するメソッドのようです。
// widget-kikurage-5-title
$this->get_field_id('title')
// widget-kikurage[5][title]
$this->get_field_name('title')
保存したデータから引っ張ってくるときは以下のようにします。
$instance['title']
ところでウィジェットのデータはどこに保存してある?
wp_options
テーブルのoption_name
カラムがsidebars_widgets
の行にシリアライズしてから保存してある。
select * from wp_options where option_name like '%sidebar%';
また、REST APIで以下のように取得することもできる。
await wp.apiFetch({ path: '/wp/v2/widgets' });
旧ウィジェットをブロックへ変換
旧ウィジェットをブロックに変換する方法です。
その前にPHP側を変更します。
show_instance_in_rest を true に
まずウィジェット側の設定です。
ウィジェットのコンストラクタにオプション(show_instance_in_rest
をtrue
)を追加する。
public function __construct()
{
parent::__construct(
'kikurage',
'キクラゲ ウィジェット',
[
'show_instance_in_rest' => true
]
);
}
ドキュメントにも書いてあるけどどういうこと!?
ウィジェットを編集(入力するたびに)すると以下のREST API
を叩いているようだ。
https://kurage/wp-json/wp/v2/widget-types/kikurage/encode?_locale=user
ちなみにPOST
でのアクセス。
そしてshow_instance_in_rest
の真偽で結果に違いがでる。
false
、あるいは何も指定しない時
true
の時
違いはinstance
にraw
があるかどうかのようです。
ウィジェットの変換にはこのraw
を取得する必要があるようです。
ただ注意する必要があるようです。
ちょっと意味が分かりにくいですけど、
このデータがJSONで表すことが出来、他の権限付きユーザにもれて困るデータが入ってないならshow_instance_in_rest
をtrue
に出来るということでしょうか?
ブロックへの変換
まず旧ウィジェットに対応するブロックを作成します。
これまでのクラゲブロックを変更します。
属性にmessage
とtitle
を設定します。
block.json
"attributes": {
"title": {
"type": "string",
"default": ""
},
"message": {
"type": "string",
"default": ""
}
},
props.ts
export default interface KurageExampleBlockProps
{
title: string;
message: string;
}
index.tsx
const blockConf: BlockConfiguration<KurageExampleBlockProps> =
{
...metadata,
edit: Edit,
save,
transforms: {
from: [
{
type: 'block',
blocks: ['core/legacy-widget'],
// @ts-ignore
isMatch: ( { idBase, instance } ) =>
{
return instance?.raw && idBase === 'kikurage';
},
// @ts-ignore
transform: ( { instance } ) =>
{
const { title, message } = instance.raw;
return createBlock(
'create-block/kurage-worker',
{
title,
message
}
)
}
}
]
}
};
blocks: ['core/legacy-widget']
レガシーウィジェットブロックのブロック名はcore/legacy-widget
です
レガシーウィジェットブロックからの変換なのでfrom
に追加します。
isMatch: ( { idBase, instance } ) =>
変換可能かをチェックします。
instance.raw
が存在し、なおかつidBase
がkikurage
(旧ウィジェットの名前)であれば変換可能です(ツールバーで変換用のボタンが追加される)
後はtransform
でinstance
からデータを取ってきてcreateBlock()
でクラゲブロックを作成して返します。
editor.tsx
import React from 'react';
import { useBlockProps } from '@wordpress/block-editor';
import { BlockEditProps } from '@wordpress/blocks';
import { TextControl } from '@wordpress/components';
import KurageExampleBlockProps from './props';
import './editor.scss';
export default (props: BlockEditProps<KurageExampleBlockProps>) =>
{
const { title, message } = props.attributes;
const { setAttributes } = props;
return (
<div {...useBlockProps()}>
<h2>Kikurage Widget!</h2>
<label>
タイトル:
<TextControl value={title} onChange={title => setAttributes({ title })} />
</label>
<label>
メッセージ:
<TextControl value={message} onChange={message => setAttributes({ message })} />
</label>
</div>
);
}
タイトルとメッセージを編集出来るようにしました。
save.tsx
import React from 'react';
import { useBlockProps } from '@wordpress/block-editor';
import { BlockEditProps } from '@wordpress/blocks';
import { TextControl } from '@wordpress/components';
import KurageExampleBlockProps from './props';
import './editor.scss';
export default (props: BlockEditProps<KurageExampleBlockProps>) =>
{
const { title, message } = props.attributes;
const { setAttributes } = props;
return (
<div {...useBlockProps()}>
<h2>Kikurage Widget!</h2>
<label>
タイトル:
<TextControl value={title} onChange={title => setAttributes({ title })} />
</label>
<label>
メッセージ:
<TextControl value={message} onChange={message => setAttributes({ message })} />
</label>
</div>
);
}
旧ウィジェットを開いてます。
ツールバーの左ボタンからKurage Worker
に変換をクリック。
クラゲブロックに変換されました。
ここで注目すべきはリストにある従来のウィジェット
がKurage Worker
に変わっていることです。
では、変換後のクラゲブロックでも編集しましょうかね。
編集して更新
ボタンを押します。
フロントエンドを開くと、右サイドバー(環境によりことなる)にウィジェットが表示されています。