API Fetch について
WordPressのブロック開発メモ その11 REST API へのアクセス
基本的な REST API
(σ・ω・)σ WordPressでREST APIを使う前編
WordPressでREST APIを使う後編 REST APIの登録と REST コントローラ
REST API コントローラの実装
WP_REST_Controller メソッド前編
WP_REST_Controller メソッド後編 追加フィールド
WordPress REST API Controllerを実装してみる。
WordPressでREST APIを使う前編
WordPressのREST APIのドキュメントを読んで分かりにくかった概念などがあったので調べてメモしておきます。
その前にREST APIアクセスするのにwp.apiFetch()
を使っている箇所があります。
投稿ページではwp.apiFetch
(JavaScriptファイル)がロードされていて使用することが出来ます。
以前の記事の続き的なページになってます。
WordPressのブロック開発メモ その11 REST API へのアクセス
WordPressが公開するREST API
WordPressはREST API
を公開してます。
もしWordPressを以下のURLで運用しているとしたら、
https://kurage
投稿一覧を取得するには以下のようにアクセスします。
https://kurage/wp-json/wp/v2/posts
するとレスポンスで以下のJSONが返ってきます。
デフォルトでは投稿データが最大10個返ってきます。
展開すると行数が多くなるのですべて閉じてます
クエリ
クエリで取得を制御することが出来ます。
一度に取得する最大数を指定
per_page
を指定すると取得する最大数を制限できます。
https://kurage/wp-json/wp/v2/posts?per_page=5
per_page
を5
にすると最大5
件まで取得します。
ちなみにドキュメントには最大100件みたいなことが書いてありましたが・・・。
ページを指定
page
を指定するとページネーションが実現出来ます。
1
から数えます。
https://kurage/wp-json/wp/v2/posts?per_page=5&page=1
page
を2
にしているので2ページ目を取得(per_pageも指定しているので最大5件までの条件も含む)
開始位置を指定
offset
を指定すると、何件目から取得するかを指定できます。
https://kurage/wp-json/wp/v2/posts?per_page=5&offset=3
offset
を3
にすると、4
件目から取得するようになります。
どうやら0
から数えるみたいです。
per_page
も指定しているので0から数えて4件目から7件目までを取得します。
ソートを指定
order
を指定するとソート出来ます。
昇順の場合はasc
, 降順の場合はdesc
を使用します。
またorderby
でソートするカラムを指定できます。
order
をasc
, orderby
をid
に指定すると
IDが小さい順で取得出来ます。
https://kurage/wp-json/wp/v2/posts?per_page=5&order=asc&orderby=id
order
をdesc
, orderby
をid
に指定すると
IDが大きい順で取得出来ます。
https://kurage/wp-json/wp/v2/posts?per_page=5&order=desc&orderby=id
投稿1件分のデータ
ところでこれまで投稿のデータのツリーノードを閉じていましたが、一つだけ展開してみましょう。
タイトルやスラグ、本文などのデータが確認出来ます。
フィールドを限定する
欲しいデータはIDとタイトルと本文だけでいいんだわ、という場合、_fields
を指定して取得するフィールドを絞ることが出来ます。
https://kurage/wp-json/wp/v2/posts?per_page=5&_fields=id,title,content
取得するフィールドをid
とtitle
とcontent
限定しました。
大分すっきりしたので全部の件のノードを展開しました。
フィールドの階層
フィールドが階層化している場合はドットで区切ります。
https://kurage/wp-json/wp/v2/posts?per_page=1&_fields=id,title,content.protected
_fields
にid
,title
, content.protected
を指定するとこうなります。
毎回5件も取得すると多すぎるので1件に絞ってます。
content
にはrendered
とprotected
の二つのフィールドがありましたが、今回はprotected
の方のみ取得出来てます。
関連情報?
https://kurage/wp-json/wp/v2/posts?per_page=1&_embed=1
_embed
を1
にすると_embedded
にauthor
とwp:term
が追加されるようです。
author
は投稿した管理者、wp:term
は未分類
などのターム。
_embed
をauthor
にするとauthor
だけ、
_embed
をwp:term
にするとwp:term
だけ取得出来るようです。
ただし、_fields
との併用は出来なかった(よくわかりません、面倒なので飛ばします)。
REST API のレスポンスにちゃっかり追加する
独自にREST APIを提供することもできます。
が、投稿などに項目を一つ二つ追加すれば済むようなこともあります。
例えば投稿にメモを追加したい場合、わざわざ独自のREST APIを公開しなくても投稿のフィールドにmemo
などを追加すれば済むことがあります。
投稿に独自のフィールドを追加する方法は二つあるようで、一つはregister_rest_field()
, もう一つはregister_meta()
があります。
前者が永続化を自分で行う、後者はメタデータに保存してくれるようです。
メタデータについては知っているものとして進めます。
その前にナンスを取得しておきます。
ブラウザのコンソールからJavaScriptを打って試していこうと思いますが、
その前にナンスを取得します。
以下のコードが成功すればナンスを取得出来ます。
await (await fetch('/wp-admin/admin-ajax.php?action=rest-nonce')).text()
register_rest_field()
投稿(post)にフィールドを追加するサンプルです。
add_action('rest_api_init', function(){
register_rest_field(
'post',
'kurage_memo',
[
// 取得時
'get_callback' => function($attr)
{
return get_post_meta($attr['id'], 'kurage_memo', true);
},
// 編集時
'update_callback' => function($value, $post)
{
update_post_meta($post->ID, 'kurage_memo', $value);
}
]
);
});
register_rest_field()
でフィールドを追加します。
第一引数は追加する対象(投稿の場合はpost
)を指定します。
固定ページならpage
、添付ファイルならattachment
を指定します。
ちなみに複数の対象に追加するなら配列で指定します。
register_rest_field( ['post', 'attachment', 'page'], ... );
第二引数で追加するフィールド名を指定します。
今回はkurage_memo
を指定しました。
第三引数でオプションを指定します。
get_callback
はREST APIから取得する時(及び更新時)、
update_callback
はREST APIから更新する時に行う処理を指定します。
get_callback
はREST APIから取得する時(及び更新時)、
update_callback
はREST APIから更新する時に行う処理を指定します。
$post
はWP_Post
のインスタンス、$attr
は連想配列(JSONようのデータ?)で送られてきます。
早速実験。
投稿(ID=614)をREST APIから取得します。
await wp.apiFetch({path: '/wp/v2/posts/614'})
コールバック(get_callback
)の戻り値がkurage_memo
がフィールドに追加してありますが値は空文字になってます。
今度は投稿(ID=614)を編集します。
kurage_memo
に値を設定して送ります。
await wp.apiFetch({path: '/wp/v2/posts/614', method: 'POST', data: { kurage_memo: 'めもめもめーも!' } });
編集するとコールバック(update_callback
)が実行されDBに追加されます。
追加されたメタ情報はwp_postmeta
テーブルを見てみます。
select * from wp_postmeta where meta_key like "kurage%";
もう一度投稿(ID=614)を見るとkurage_memo
メタ情報が追加されていることが確認出来ます。
DBに値が設定してあると、REST APIのほうにもkurage_memo
に値が設定してあることが確認出来ます。
スキーマ
スキーマを追加することもできます。
add_action('rest_api_init', function(){
register_rest_field(
'post',
'kurage_memo',
[
// 取得時
'get_callback' => function($attr)
{
return get_post_meta($attr['id'], 'kurage_memo', true);
},
// 編集時
'update_callback' => function($value, $post)
{
update_post_meta($post->ID, 'kurage_memo', $value);
},
// スキーマを追加
'schema' => [
'description' => 'Kurage Attributes!',
'type' => 'string'
]
]
);
});
この場合、kurage_memo
をstring型に制限出来ます。
うっかり数値123
を送ったりするとrest_invalid_param
が発生したりします。
スキーマについては割合します。
register_meta()
さっきは自分でメタの追加しましたが、register_meta()
を使うと自動的にメタとのマッピングをやってくれます。
REST API
のリクエスト/レスポンスの際にメタ情報を追加、取得すように登録します。
まず管理ページから投稿を新規作成しました。
IDは今回は614
になってます。
ではregister_meta()
で登録してみます。
add_action('rest_api_init', function(){
register_meta(
'post',
'kurage_funny',
[
'type' => 'string',
'description' => '笑って!',
'single' => true,
'show_in_rest' => true
]
);
register_meta(
'post',
'kurage_sad',
[
'type' => 'string',
'description' => '泣いて!',
'single' => true,
'show_in_rest' => true
]
);
});
register_meta()
の第一引数はオブジェクトタイプです。
post
やcomment
, user
, term
があります。
今回は投稿に追加する例なのでpost
を指定します。
第二引数はメタキー(今回はkurage_funny, kurage_sadの二つ)を指定します。
今回の設定ではpost(投稿や固定ページ、メディアなど)
にメタキー(kurage_funnyとkurage_sad
)を追加するように設定します。
REST APIから投稿を取得してみましょう。
await wp.apiFetch({path: '/wp/v2/posts/614'})
青線の部分を見てください。
meta
の下にkurage_funny
とkurage_sad
が追加されてます。
ただし、値は空文字になってます。
何故なら投稿の場合はデータベースのwp_postmeta
テーブルに対応するメタ情報が無いからです。
では次は投稿を編集するリクエストをしてみましょう。
await wp.apiFetch({
path: '/wp/v2/posts/614',
method: 'POST',
data:
{
meta:
{
kurage_funny: '電話にだれも出んわ!',
kurage_sad: '財布なくした・・・'
}
}
});
meta
に二つのメタキーを追加して更新します。
これを実行すると、まずwp_postmeta
が更新されます。
どのように追加されたかテーブルを見てみます。
select post.ID, post.post_title, meta.meta_key, meta.meta_value
from wp_posts as post left join wp_postmeta as meta
on post.ID = meta.post_id
where meta.meta_key like "%kurage%";
IDが614
の投稿に2つのメタ情報が追加されているのが確認出来ます。
さてもう一度投稿を取得してみましょう。
await wp.apiFetch({path: '/wp/v2/posts/614'})
今度はwp_postmeta
に二つのキーが存在するので取得出来るようになりました。
register_meta()
はREST APIで取得、編集時にそのメタ情報を追加しますよと宣言しているだけで、この関数を実行してもそのままデータベースに反映されるわけではないんですね。
オブジェクトタイプ
オブジェクトタイプって何!?
今回のpost
は投稿(/wp/v2/posts)や固定ページ(/wp/v2/pages)、メディア(/wp/v2/media)などのアクセスの際にメタ情報を追加するようにします。
ということは、
以下の何れのレスポンスにもメタ情報が追加されてます。
await wp.apiFetch({path: '/wp/v2/posts/605'})
await wp.apiFetch({path: '/wp/v2/media/469'})
await wp.apiFetch({path: '/wp/v2/pages/10'})
投稿も固定ページもメディアもデータベース上(wp_posts)に入っていて同じようなものですからね。
オブジェクトタイプを
term
に指定するとカテゴリ(/wp/v2/categories)やタグ(/wp/v2/tags)にメタ情報を追加します。
user
だとユーザー(/wp/v2/user)にメタ情報を追加します。
comment
は省略します。
オブジェクトタイプをterm
にすると以下のリクエストなどに追加されます。
await wp.apiFetch({path: '/wp/v2/categories/5'})
await wp.apiFetch({path: '/wp/v2/tags/11'})
オブジェクトタイプをuser
にすると以下のリクエストなどに追加されます。
await wp.apiFetch({path: '/wp/v2/users/1'})
メタデータのサブタイプ
おいおい、post
を指定したら投稿だけでなく固定ページやメディアにまで追加されるのはちょっと・・・
といった時にサブタイプを指定します。
add_action('rest_api_init', function(){
register_meta(
'post',
'kurage_funny',
[
'type' => 'string',
'description' => '笑って!',
'single' => true,
'show_in_rest' => true
]
);
register_meta(
'post',
'kurage_sad',
[
'type' => 'string',
'description' => '泣いて!',
'single' => true,
'show_in_rest' => true,
// 追加
'object_subtype' => 'post'
]
);
});
register_meta()
のオプションでobject_subtype
を指定すると特定のサブタイプにしかメタは追加されません。
今回はkurage_sad
に追加しているので、kurage_sad
メタキーは以下の例の場合/wp/v2/posts/605
のみに追加されます。
await wp.apiFetch({path: '/wp/v2/posts/605'})
await wp.apiFetch({path: '/wp/v2/media/469'})
await wp.apiFetch({path: '/wp/v2/pages/10'})
オブジェクトタイプがpost
ならサブタイトルは投稿(post
), 固定ページ(page
), メディア(media
)、
オブジェクトタイプがterm
ならサブタイトルはカテゴリ(category
), タグを(post_tag
)
などが指定出来ます。
オブジェクトタイプがpost
の場合、register_post_meta()
を使うと一部省略出来ます。
/*
register_meta(
'post',
'kurage_sad',
[
'type' => 'string',
'description' => '泣いて!',
'single' => true,
'show_in_rest' => true,
'object_subtype' => 'page'
]
);
*/
// ↑と同じ
register_post_meta(
'page',
'kurage_sad',
[
'type' => 'string',
'description' => '泣いて!',
'single' => true,
'show_in_rest' => true,
]
);
この場合オブジェクトタイプがpost
に固定され、第一引数がサブタイプになります。
register_post_meta()
のソースコードを覗くと以下のようになってます。
function register_post_meta( $post_type, $meta_key, array $args ) {
$args['object_subtype'] = $post_type;
return register_meta( 'post', $meta_key, $args );
}
term版にregister_term_meta()
もあります。
register_rest_field()とregister_meta()の違い
register_rest_field()
- フィールドはレスポンスのJSONオブジェクトに直接追加される
- フィールドの値の更新と取得は自分で実装する必要がある。
register_meta()
- フィールドは
meta
プロパティの子として追加される。 - フィールドの値の更新は
update_metadata()
で、取得はget_metadata()
でいい感じに行われる。
register_rest_field()とregister_meta()の内部動作
どうしても内部が気になる人向けです。
この項は読む必要ありません。
単にregister
が何を意味するのか分かりにくかったのでソースコードを読んでみました。
その前にREST コントローラ
について知る必要があります。
REST コントローラ
WordPressのREST APIは取得するタイプによってREST コントローラ
が定義されています。
例えば投稿を取得する/wp/v2/posts
はWP_REST_Posts_Controller
で定義してあります。
タクソノミーであればwp/v2/taxonomies
はWP_REST_Taxonomies_Controller
に定義してあります。
コメントならWP_REST_Comments_Controller
といったように。
そしてこれらクラスの基底になっているのがWP_REST_Controller
抽象クラスです。
CRUD系のメソッドや追加フィールド系のメソッドなどが定義されてはいます。
register_rest_field()の内部
この関数(register_rest_field()
)は追加フィールドの設定情報をグローバル変数の$wp_rest_additional_fields
に追加しているにすぎません。
この値はREST コントローラから使用されます。
WP_REST_Controller
CRUD系ではアイテム(投稿やタクソノミーなど)の一覧を取得するget_items()
や、IDを指定して一つだけアイテムを取得するget_item()
、
新しく作成するcreate_item()
、更新するupdate_item()
、削除するdelete_item()
などが定義してあります。
ただし、これらのメソッドは抽象メソッドではなくWP_Error
を返す実体として定義されています。
追加フィールド系のメソッドでは
get_additional_fields()
, add_additional_fields_to_object()
, update_additional_fields_for_object()
などが定義してあります。
今回省略しますが、追加フィールドのスキーマを追加するadd_additional_fields_schema()
もあります。
WP_REST_Controller::get_additional_fields()
対象のオブジェクトタイプからグローバル変数の$wp_rest_additional_fields
にアクセスして追加フィールドの設定情報を取得する。
WP_REST_Controller::add_additional_fields_to_object()
アイテムを取得時(get_item(), get_items())、
JSONオブジェクトにフィールドを追加、とその値を設定する。
その値は(get_callbackで指定したコールバック
)から取得される。
WP_REST_Controller::update_additional_fields_for_object()
アイテムの作成時や更新時(create_item(), update_items())、追加フィールドの更新をコールバック関数(update_callback
)を実行することでを行う。
WP_REST_Posts_Controller
投稿用のREST コントローラにはWP_REST_Posts_Controller
が用意されています。
CRUD系のメソッドがオーバーライドされています。
そしてこれらのメソッドから追加フィールドの設定情報が利用されます。
get_item()やget_items()らは追加するフィールドの値を取得する必要があります。
内部でコールバック(get_callback
)を実行し、フィールドの値を取得してます。
create_item()やupdate_item()らは追加フィールドを変更しないといけません。
内部でコールバック(update_callback
)を実行するようになってます。
register_meta()をより深く
この関数(register_meta()
)は単に引数の内容をグローバル変数の$wp_meta_keys
に引数の内容を登録してるだけです。
ここで登録した設定はREST API コントローラから使用されます。
厳密にはメタキーを扱う抽象クラスWP_REST_Meta_Fields
が主にメタキーを永続化します。
クラス図にするとこんな感じになってます。
型とか引数などはだいぶ省略してます。
Global
Globalはそのまま関数とグローバル変数を意味してます。
get_registered_meta_keys()
はregister_meta()
で登録したメタ情報を取得します。
WP_REST_Meta_Fields
このクラスはメタ情報を扱う抽象クラスです。
WP_REST_Meta_Fields::get_registerd_fields()
がget_registered_meta_keys()
の登録情報を取得してごにょごにょしてます。
そしてWP_REST_Meta_Fields::get_value()
は
WP_REST_Meta_Fields::get_registered_fields()
を参照したうえで、
get_metadata()
から値を取得します。
WP_REST_Meta_Fields::update_value()
は
WP_REST_Meta_Fields::get_registered_fields()
を参照したうえで、
がでデータベースに変更(update_metadata()
)を加えてます。
値が複数ある場合はadd_metadata()
とdelete_metadata()
でいい感じにやってくれてます。
WP_REST_Ppst_Meta_Fields
POST用のメタ情報はWP_REST_Meta_Fields
を継承したこのクラスが使用されます。
WP_REST_Posts_Controller
REST API の投稿用コントローラです。
メタ情報の処理はWP_REST_Post_Meta_Fields
のインスタンスを使用して行われます。
このインスタンスはコンストラクタで作成されmeta
プロパティで保持されます。
作成、更新時、もしregister_meta()
で登録された内容に適合してればWP_REST_Post_Meta_Fields
を使ってメタ情報を操作(WP_REST_Meta_Fields::get_value()
やWP_REST_Meta_Fields::update_value()
)します。
発見?
https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/
本家ドキュメントの意味がいまいち理解出来なかったのでいろいろいじって見つけた結果だけずらずら書いていきます。
リンクヘッダ
Link: ; rel="https://api.w.org/"
フロントエンドのページにリンクヘッダーを追加する・・・
どういう意味やねん!?
フロントページアクセスするとヘッダーのLink
にREST APIのアドレスのっけてるでってこと?
エレメント
Linkヘッダーと同等のものがフロントエンド ページのhead
に・・・。
なるほど、DOMにlink
要素があるからそれ使ってJavaScriptからREST APIのアドレス拾えるでってこと?
認証の検出、拡張機能の検出
WordPressのドメインから/wp-json
にアクセスするとauthentication
やnamespace
を発見できた。
routes
もある。
つづく。
ちょっと長くなり過ぎたので次に。