この記事は最後に更新してから1年以上経過しています。
説明
自分なりのGoogle Mapsブロックを作るべく、カスタムHTMLブロックをベースにいくつかのパラメータ要素をサイドバーに追加していく。Google Maps用のAPIキーについてはオプションデータ(wp_optionsテーブル)に格納して利用していこうと思ったのだが、その方法がさっぱりわからなかった。
core-dataのuseEntityProp
ブロックでオプションデータを使う方法がまったくわからなかったので、いろいろ検索してみるとGitHubに気になる投稿「Ability to get (and set) site options from wp_options table using useSelect()」が見つかった。このやり取りの中で「site title block」が紹介されていたので、見に行ってみる(以下は一部抜粋)。
import { useSelect } from '@wordpress/data';
import { useEntityProp, store as coreStore } from '@wordpress/core-data';
// 略
import { decodeEntities } from '@wordpress/html-entities';
// 略
export default function SiteTitleEdit( {
attributes,
setAttributes,
insertBlocksAfter,
} ) {
const { level, textAlign, isLink, linkTarget } = attributes;
const [ title, setTitle ] = useEntityProp( 'root', 'site', 'title' );
const { canUserEdit, readOnlyTitle } = useSelect( ( select ) => {
const { canUser, getEntityRecord } = select( coreStore );
const siteData = getEntityRecord( 'root', '__unstableBase' );
return {
canUserEdit: canUser( 'update', 'settings' ),
readOnlyTitle: decodeEntities( siteData?.name ),
};
}, [] );
赤字の部分がオプションデータのブログタイトルを取得しているようなので、作成中のコードに追加してみる。
import { useEntityProp, store as coreStore } from '@wordpress/core-data';
// 略
const [ title, setTitle ] = useEntityProp( 'root', 'site', 'title' );
console.log( title );
作成中のブロックを追加してコンソールを確認すると、undefinedが2回続いた後にブログタイトルが表示され、オプションデータを取得できることが確認できた。
useEntityPropの第3パラメータは、オプションデータのキーに対応する感じなので、本来の目的である'google_maps_api_key'キーに修正してみる。
const [ apiKey, setApiKey ] = useEntityProp( 'root', 'site', 'google_maps_api_key' );
console.log( apiKey );
投稿編集ページをリロードすると、コンソールにはundefinedが繰り返し出力されるばかり。'google_maps_api_key'キーのデータは取得できていない。PHP側で何かやることがあるのではと思い、「useEntityProp」をキーにして検索すると「WordPressのカスタムブロックで、wp_optionsのデータを使う」に register_setting関数を呼び出す必要がある旨の記載があった。ブロックで投稿メタを使用する際、 register_post_meta関数を呼び出すことと同じようだ。
ということで、PHP側に次のコードを追加した。
function myblock_setting() {
register_setting( 'options', 'google_maps_api_key', array(
'type' => 'string',
'sanitize_callback' => 'esc_attr',
'show_in_rest' => true,
) );
}
add_action( 'rest_api_init', 'myblock_setting' );
もう一度、投稿編集ページをリロード。コンソールに事前に登録していた内容が出力された。
オプションデータの更新
'google_maps_api_key'キーのデータを更新できるようにするため、サイドバー用にTextControlを追加。onChangeにはuseEntityPropから受け取ったsetApiKeyを指定した。
<InspectorControls key="setting">
<PanelBody
title={ __( 'Settings' ) }
className="maps-embed-settings"
>
<TextControl
type="text"
className="maps-embed-control__apikey"
label={ __( 'API Key' ) }
value={ apiKey }
onChange={ setApiKey }
/>
投稿編集ページをリロードし、サイドバーを確認する。
更新されるか確認するために、入力ボックスの内容を適当に編集してwp_optionsテーブルを確認。option_valueは書き換わっていない。
mysql> select * from wp_options where option_name='google_maps_api_key';
+-----------+---------------------+--------------+----------+
| option_id | option_name | option_value | autoload |
+-----------+---------------------+--------------+----------+
| 24485 | google_maps_api_key | 1234567890 | yes |
+-----------+---------------------+--------------+----------+
続いて「下書き保存」をクリックし、wp_optionsテーブルを確認。やはりoption_valueは書き換わっていない。
mysql> select * from wp_options where option_name='google_maps_api_key';
+-----------+---------------------+--------------+----------+
| option_id | option_name | option_value | autoload |
+-----------+---------------------+--------------+----------+
| 24485 | google_maps_api_key | 1234567890 | yes |
+-----------+---------------------+--------------+----------+
「公開」ボタンをクリックすると、サイドバーの上に確認用パネルが表示され、google_maps_api_keyのチェックボックスが表示される。
チェックされた状態のまま「保存」ボタンをクリック。wp_optionsテーブルを確認すると、option_valueが書き換わった。
mysql> select * from wp_options where option_name='google_maps_api_key';
+-----------+---------------------+---------------+----------+
| option_id | option_name | option_value | autoload |
+-----------+---------------------+---------------+----------+
| 24485 | google_maps_api_key | 1234567890ABC | yes |
+-----------+---------------------+---------------+----------+
無事オプションデータを更新することができたので、ブロックのほかの部分を作りこむ。これで完成かと思ったのだが、オプションデータの取り扱いには落とし穴があった。
投稿者はオプションデータを取得できない!?
ここまでは管理者アカウントで確認を行っていたので、投稿者アカウントに切り替えてみる。新規追加で投稿編集ページを開き、作っているブロックを追加すると、API Keyの入力ボックスは空となり、コンソールには次のようなエラーログが出力された。
GET http://localhost/wp-json/wp/v2/settings?_locale=user 403 (Forbidden)
REST APIの呼び出しのレスポンスが「403」になっており、オプションデータが取得できていないようだ。
この理由を調べるため、エンドポイントのソースコード(wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php)を見てみる。 register_rest_route関数のパラメータでは2つのメソッドが指定され、そのどちらのpermission_callbackも同じメソッドになっていた。
public function get_item_permissions_check( $request ) {
return current_user_can( 'manage_options' );
}
このエンドポイントはmanage_options権限を持っているユーザー、すなわち管理者のみが利用できることになる。このようなケース、いつもならフィルターが用意されているものだが、今回はそれが見当たらない。
しかたなくPHP側でインラインスクリプトを出力することを考えたが、register_rest_route関数で上書きすれば対応できるのではと思い、PHP側のrest_api_initアクションのコールバック関数を次のように変更した(赤字が追加した部分)。
function myblock_get_item_permissions_check( $request ) {
return current_user_can( 'publish_posts' );
}
function myblock_update_item_permissions_check( $request ) {
return current_user_can( 'manage_options' );
}
function myblock_setting() {
register_setting( 'options', 'google_maps_api_key', array(
'type' => 'string',
'sanitize_callback' => 'esc_attr',
'show_in_rest' => true,
) );
$controller = new WP_REST_Settings_Controller;
register_rest_route(
'wp/v2',
'/settings',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $controller, 'get_item' ),
'args' => array(),
'permission_callback' => 'myblock_get_item_permissions_check',
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $controller, 'update_item' ),
'args' => $controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
'permission_callback' => 'myblock_update_item_permissions_check',
),
'schema' => array( $controller, 'get_public_item_schema' ),
),
true // override
);
}
add_action( 'rest_api_init', 'myblock_setting', 199 );
※2022/08/23 rest_api_initアクションの優先度を199(99より大きい値)を指定し、'permission_callback'を青字の内容に変更した。
同時にJavaScript側も修正した(赤字が追加した部分)。
import { useSelect } from '@wordpress/data';
// 中略
const [ apiKey, setApiKey ] = useEntityProp( 'root', 'site', 'google_maps_api_key' );
const { canUserEdit } = useSelect( ( select ) => {
const { canUser } = select( coreStore );
return { canUserEdit: canUser( 'update', 'settings' ) };
}, [] );
// 中略
{ canUserEdit ? (
<TextControl
type="text"
className="maps-embed-control__apikey"
label={ __( 'API Key' ) }
value={ apiKey }
onChange={ setApiKey }
/>
) : (
<span>API Key: { apiKey }</span>
) }
ポイントはuseSelectを使ってオプションデータを更新できるかをcanUserEditに保存しているところ。これで管理者の場合はAPI Keyの入力ボックスを、投稿者以上の場合はspan要素でAPI Keyを表示するようにした。
今回はregister_rest_route関数で既存のエンドポイントを変更してみたが、PHP側のコードは少なくて済むが、セキュリティ面で懸念がないわけでもない。そのリスクを考慮するなら、PHP側でインラインスクリプトを出力し、JavaScript側はインラインスクリプトのデータを参照する方法が無難だろう。
最終更新 : 2022年08月23日 11:16
関連
お勧め
add_shortcode(2018年5月27日 更新)
get_user_setting(2022年1月31日 更新)
have_posts(2018年5月27日 更新)
in_category(2018年5月27日 更新)
add_feed(2024年6月24日 更新)