レスポンスの変更

2020.03.24 2020.03.24

TOPICS

翻訳元記事はこちらです。

WordPress REST API のデフォルトのエンドポイントは、大多数のサイトやユースケースに対応したデータをデフォルトで返すように設計されていますが、様々なオブジェクトタイプのレスポンスにアクセスしたり、追加のデータを公開したりする必要がある状況がよくあります。

WordPress の他の部分と同様に、REST API はこれらのニーズに合わせて高度に拡張できるように設計されています。このガイドでは、register_rest_field 関数と register_meta 関数を使用してデフォルトのエンドポイントのレスポンスにデータを追加する方法を詳しく説明します。
これらの関数を使用して、REST API がサポートする任意のオブジェクト型にフィールドを追加することができます。
これらのカスタムフィールドは、取得と更新の両方をサポートすることができます。

レスポンスの変更にあたっての注意点

コアのREST APIのエンドポイントレスポンスからデータを変更したり削除したりすると、プラグインやWordPressのコアの挙動が崩れる可能性があるため、可能な限り回避する必要があります。

API は API レスポンスで多くのフィールドを公開していますが、その中には必要のないものや、サイトの動作に合わないものも含まれます。
REST API レスポンスからフィールドを修正したり削除したりしたくなりますが、標準レスポンスを期待する API クライアントでは問題が発生します。これには、モバイルクライアント、サイト管理に役立つサードパーティのツール、または wp-admin 自体が含まれます。

必要なデータ量は少ないかもしれませんが、APIはあなたが取り組んでいる機能だけでなく、すべてのクライアントにインターフェースを公開するものであることを念頭に置いておくことが重要です。レスポンスを変更するのは危険です。

フィールドを追加することは危険ではありませんので、データを修正する必要がある場合は、修正したデータとフィールドを複製する方がはるかに良いでしょう。フィールドを削除することは決して推奨されません。データのより小さなサブセットを取得する必要がある場合は、_fieldsパラメータを使用するか、代わりにコンテキストを使って作業してください。

既存のコンテキストからフィールドを削除する必要がある場合は、カスタムクエリパラメータを提供してフィールドの削除をトリガーするなど、動作が事前に許可されていることを確認する必要があります。

APIはレスポンスの変更を防ぐことはできませんが、コードはそれを強く推奨しないように構造化されています。内部的には、フィールドの登録はフィルタによって行われ、他に選択肢がない場合はこれらを使用することができます。

register_rest_field 関数かregister_meta 関数のどちらを使用するか

WordPress REST APIのレスポンスにデータを加えるには、register_rest_field 関数とregister_meta 関数を使う二通りの方法があります。

register_rest_field は、任意の REST API レスポンスに任意のフィールドを追加するために使用することができ、API を使用してデータの読み取りと書き込みの両方に使用することができます。新しい REST フィールドを登録するには、フィールドの値を取得または設定するための独自のコールバック関数を提供する必要があります。

register_meta は、既存のカスタムメタ値をホワイトリスト化して REST API からアクセスできるようにするために使用します。メタフィールドの show_in_rest パラメータを true に設定することで、そのフィールドの値がエンドポイントレスポンスの .meta キーで公開され、WordPress はそのメタキーへの読み書きのためのコールバックの設定を行います。これは register_rest_field よりもずっとシンプルですが、ひとつ注意点があります。

WordPress 4.9.8以前では、register_metaを使ってshow_in_restに設定したメタフィールドは、指定されたタイプのすべてのオブジェクトに登録されていました。1つのカスタム投稿タイプがメタフィールドを表示すると、すべてのカスタム投稿タイプがそのメタフィールドを表示します。WordPress 4.9.9.8では、特定の投稿タイプのメタキーの使用量を減らすために、object_subtype引数でregister_metaを使用することが可能になりました。

WordPress 5.3 より前のバージョンでは、register_meta はスカラ値(文字列、整数、数値、ブール値)のみをサポートしていました。WordPress 5.3 では、オブジェクト型と配列型のサポートが追加されました。

APIのレスポンスにカスタムフィールドを追加する

register_rest_field を使った方法

register_rest_field 関数は、REST API レスポンスオブジェクトにフィールドを追加する最も柔軟な方法です。これは3つのパラメータを受け付けます。

1.$object_type

オブジェクトの名前を文字列で指定するか、フィールドが登録されているオブジェクトの名前を配列で指定します。これは、”post”、”terms”、”meta”、”user”、”comment “のようなコアタイプですが、カスタム投稿タイプの文字列名にすることもできます。

2.$attribute

フィールドの名前です。この名前は、レスポンスオブジェクトのキーを定義するために使用されます。

3.$args

フィールドの値の取得 (‘get_callback’)、フィールドの値の更新 (‘update_callback’)、スキーマの定義 (‘schema’) に使用するコールバック関数を定義するキーを持つ配列。

args配列の各キーはオプションですが、使用しない場合はその機能は追加されません。つまり、値を読み込むためのコールバック関数を指定し、 必要であればそのフィールドを読み取り専用にするために更新コールバックを省略することができます。

フィールドの登録はrest_api_initアクションで行う必要があります。initではなくこのアクションを使うことで、REST APIを使わないWordPressへのリクエスト中にフィールドの登録が発生するのを防ぐことができます。

コメントのレスポンスデータに、他のフィールドを読み書きする

<?php
add_action( 'rest_api_init', function () {
    register_rest_field( 'comment', 'karma', array(
        'get_callback' => function( $comment_arr ) {
            $comment_obj = get_comment( $comment_arr['id'] );
            return (int) $comment_obj->comment_karma;
        },
        'update_callback' => function( $karma, $comment_obj ) {
            $ret = wp_update_comment( array(
                'comment_ID'    => $comment_obj->comment_ID,
                'comment_karma' => $karma
            ) );
            if ( false === $ret ) {
                return new WP_Error(
                  'rest_comment_karma_failed',
                  __( 'Failed to update comment karma.' ),
                  array( 'status' => 500 )
                );
            }
            return true;
        },
        'schema' => array(
            'description' => __( 'Comment karma.' ),
            'type'        => 'integer'
        ),
    ) );
} );

この例では、投稿に対するレスポンスに karma というフィールドを追加しています。comment_karmaフィールドは存在しますが、コアでは使用されていないため、これは機能します。コメントカルマを実際に実装するには、別のエンドポイントを使う必要があることに注意してください。

これは基本的な例です。特定のフィールドに必要なパーミッションチェックやエラー処理を慎重に検討してください。

訳者注:register_rest_fieldの実例は、Qiitaの記事で紹介しているものがあるので、そちらも参考にしてみてください。

訳者注:コメントのカルマってなに?と思いましたが、値は常に0だそうです。
関数リファレス:get_comments

register_rest_fieldはどのように機能するの?

グローバル変数$wp_rest_additional_fieldsは、各オブジェクトタイプに追加するレスポンスフィールドを保持するために、REST APIインフラストラクチャによって内部的に使用されます。
REST API は、このグローバル変数に追加するためのユーティリティ関数として register_rest_field を提供しています。グローバル変数に直接追加することは、前方互換性を確保するために避けなければなりません。

それぞれのオブジェクトタイプ – 投稿、ユーザー、ターム、コメントなど – $wp_rest_additional_fields には、フィールドの値を取得または更新するために使用されるコールバックを含む、フィールド定義の配列が含まれています。

REST APIで登録されたメタ情報を扱う

register_meta 関数は、特定のオブジェクトタイプのメタフィールドを定義する際の処理を簡素化します。新しいメタキーを登録する際に 'show_in_rest' => true を設定することで、そのキーに REST API からアクセスできるようになります。

postのレスポンスで、postのメタフィールドを読み書きする

<?php
// The object type. For custom post types, this is 'post';
// for custom comment types, this is 'comment'. For user meta,
// this is 'user'.
$object_type = 'post';
$meta_args = array( // Validate and sanitize the meta value.
    // Note: currently (4.7) one of 'string', 'boolean', 'integer',
    // 'number' must be used as 'type'. The default is 'string'.
    'type'         => 'string',
    // Shown in the schema for the meta key.
    'description'  => 'A meta key associated with a string meta value.',
    // Return a single value of the type.
    'single'       => true,
    // Show in the WP REST API response. Default: false.
    'show_in_rest' => true,
);
register_meta( $object_type, 'my_meta_key', $meta_args );

この例では、投稿のメタフィールドの読み書きを許可する方法を示しています。これにより、このフィールドは wp-json/wp/v2/posts/<post-id> への POST リクエストで更新されたり、 wp-json/wp/v2/posts/への POST リクエストで投稿と一緒に作成されたりするようになります。

カスタム投稿タイプに登録されたメタフィールドについては、投稿タイプがカスタムフィールドをサポートしている必要があることに注意してください。そうでない場合、メタフィールドはREST APIに表示されません。

投稿タイプ固有のメタ情報

WordPress 4.9.8 では、register_post_metaregister_term_meta 関数を使って、特定の投稿タイプやタクソノミーのメタを登録できるようになりました。これらの関数は register_meta と同じルールに従いますが、最初のパラメータとしてオブジェクトタイプの代わりに投稿タイプやタクソノミーを受け取ります。次のコードは上のmy_meta_keyの例を登録しますが、pageの投稿タイプのみに登録します。

$meta_args = array(
    'type'         => 'string',
    'description'  => 'A meta key associated with a string meta value.',
    'single'       => true,
    'show_in_rest' => true,
);
register_post_meta( 'page', 'my_meta_key', $meta_args );

register_post_metaのコードリファレンス

register_term_metaのコードリファレンス

オブジェクトメタタイプ

WordPress 5.3ではメタを登録する際にオブジェクト型を使用できるようになりました。重要なのは、object は JSON オブジェクトを指し、これは PHP の連想配列と同等です。

オブジェクトメタを登録する際には、typeobjectに設定するだけでは不十分で、どのようなプロパティを許可するかをWordPressにも伝える必要があります。これは、メタデータを登録する際にJSONスキーマを記述することで行います。

例えば、以下のコードサンプルでは、与えられたJSONデータを受け付ける “release “というポストメタフィールドを登録しています。

{
  "meta": {
    "release": {
      "version": "5.2",
      "artist": "Jaco"
    }
  }
}
register_post_meta(
    'post',
    'release',
    array(
        'single'       => true,
        'type'         => 'object',
        'show_in_rest' => array(
            'schema' => array(
                'type'       => 'object',
                'properties' => array(
                    'version' => array(
                        'type' => 'string',
                    ),
                    'artist'  => array(
                        'type' => 'string',
                    ),
                ),
            ),
        ),
    )
);

show_in_resttrueではなく配列になり、schemaキーにJson Schemaが指定されていることに注目してください。そして、各プロパティはプロパティ配列にリストされます。
最低限、各プロパティは型を指定しなければなりませんが、rest_validate_value_from_schemaが理解できるJSON Schemaキーワードも同様に使用できます。

追加のプロパティ

デフォルトでは、プロパティのリストは厳密なホワイトリストになっています。リストにないプロパティがリクエストで送信された場合、REST API はエラーを返します。
送信されたプロパティは、有効なプロパティではありません、のように。
事前にプロパティ名がわからない場合は、 additionalProperties キーワードを使用することができます。例えば、additionalPropertiesが数字であることを強制するには、以下のコードを使用できます。

{
  "meta": {
    "release": {
      "version": "5.2",
      "artist": "Jaco",
      "unknown_field": 5.3
    }
  }
}
register_post_meta(
    'post',
    'version',
    array(
        'single'       => true,
        'type'         => 'object',
        'show_in_rest' => array(
            'schema' => array(
                'type'                 => 'object',
                'properties'           => array(
                    'version' => array(
                        'type' => 'string',
                    ),
                    'artist'  => array(
                        'type' => 'string',
                    ),
                ),
                'additionalProperties' => array(
                    'type' => 'number',
                ),
            ),
        ),
    )
);

additionalPropertiestrueに設定して、任意のフォーマットの未知のプロパティを許可することができますが、これは推奨されません。

配列メタタイプ

WordPress 5.3では、配列型の使用にも対応しています。重要なのは、arrayはJSON配列を指し、これはPHPの数値配列と同等です。

配列メタを登録する際に、型を配列に設定するだけでは不十分なので、配列内の項目の期待される形式をWordPressに伝える必要があります。これは、メタデータを登録する際にJSON Schemaのエントリを記述することで行います。

この値を指定しなかった場合、register_metafalseを返し、次のような警告を発します。

REST APIで表示するメタタイプ「配列」を登録する際には、「show_in_rest.schema.items」で配列項目ごとにスキーマを指定する必要があります。

以下のコードサンプルでは、プロジェクト名のリストを含む “projects “という投稿のメタフィールドを登録しています。与えられたJSONデータを受け取ります。

{
  "meta": {
    "projects": [
      "WordPress",
      "BuddyPress"
    ]
  }
}
register_post_meta(
    'post',
    'projects',
    array(
        'single'       => true,
        'type'         => 'array',
        'show_in_rest' => array(
            'schema' => array(
                'type'  => 'array',
                'items' => array(
                    'type' => 'string',
                ),
            ),
        ),
    )
);

再びshow_in_resttrueではなく配列になり、schemaキーにJSONスキーマが指定されていることに注目してください。
items キーワードは、各配列のメンバーを検証するための JSON スキーマを定義するために使用されます。文字列のようなスカラー型やオブジェクトのような複雑な型を指定することができます。

例えば、以下の与えられたJSONデータを受け入れるには、以下のようなメタ登録が必要になります。

{
  "meta": {
    "projects": [
      {
        "name": "WordPress",
        "website": "https://wordpress.org"
      },
      {
        "name": "BuddyPress",
        "website": "https://buddypress.org"
      }
    ]
  }
}
register_post_meta(
    'post',
    'projects',
    array(
        'single'       => true,
        'type'         => 'array',
        'show_in_rest' => array(
            'schema' => array(
                'items' => array(
                    'type'       => 'object',
                    'properties' => array(
                        'name'    => array(
                            'type' => 'string',
                        ),
                        'website' => array(
                            'type'   => 'string',
                            'format' => 'uri',
                        ),
                    ),
                ),
            ),
        ),
    )
);

配列型は、配列のキーが 0 から始まる連続した整数であることを強制します。 配列は array_values を用いて再インデックス化されます。

単一でないメタデータ

単一ではないメタ・フィールドは、オブジェクトごとに1つの値ではなく、オブジェクトごとに値の配列を持ちます。これらの値はそれぞれメタテーブルの別の行に格納されます。

配列型とオブジェクト型は、シングルではないメタフィールドでも使用できます。例えば、先ほどの “release “メタキーにsinglefalseに設定されていた場合、以下のようなJSONデータを受け付けることができます。

{
  "meta": {
    "release": [
      {
        "version": "5.2",
        "artist": "Jaco"
      },
      {
        "version": "5.1",
        "artist": "Betty"
      }
    ]
  }
}

これにより、2つのメタデータベースの行が生成されます。
1つ目は、 { "version": "5.2", "artist": "Jaco" }
2つ目は、 { "version": "5.1", "artist": "Betty" }

同様に、”projects “の例では、singlefalseに設定した場合、以下のデータが受け入れられます。

{
  "meta": {
    "projects": [
      [
        "WordPress",
        "BuddyPress"
      ],
      [
        "bbPress"
      ]
    ]
  }
}

これにより、2つのメタデータベースの行が生成されます。
1つ目は、 [ "WordPress", "BuddyPress" ]
2つ目は、[ "bbPress" ]

無効な保存値

メタフィールドの既存の値が登録された型およびスキーマに対して検証されない場合、そのメタフィールドの値はnullとして返されます。
更新リクエストを行う際にその null 値が API に渡されると、rest_invalid_stored_value エラーが発生します。
「○番目のプロパティは無効な値です。nulでは更新できません。」

これを修正するには、メタキーを有効な値で更新するか、リクエストからそのプロパティを省略してください。

リンクをAPIのレスポンスに追加する

WordPress は、関連するリソースへのナビゲートを容易にするために、クエリされたリソースに関連付けられたリンクのリストを生成します。

{
 "_links": {
    "self": [
      {
        "href": "https://make.wordpress.org/core/wp-json/wp/v2/posts/28312"
      }
    ],
    "collection": [
      {
        "href": "https://make.wordpress.org/core/wp-json/wp/v2/posts"
      }
    ],
    "author": [
      {
        "embeddable": true,
        "href": "https://make.wordpress.org/core/wp-json/wp/v2/users/8670591"
      }
    ],
    "replies": [
      {
        "embeddable": true,
        "href": "https://make.wordpress.org/core/wp-json/wp/v2/comments?post=28312"
      }
    ],
    "wp:term": [
      {
        "taxonomy": "category",
        "embeddable": true,
        "href": "https://make.wordpress.org/core/wp-json/wp/v2/categories?post=28312"
      },
      {
        "taxonomy": "post_tag",
        "embeddable": true,
        "href": "https://make.wordpress.org/core/wp-json/wp/v2/tags?post=28312"
      }
    ]
  }
}

Make.WordPress.orgの投稿からのリンク例

これらのリンクは JSON レスポンスオブジェクトの _links プロパティの下に表示されますが、WP_REST_Response::$data には保存されず、WP_REST_Response::get_data() でアクセスすることもできません。その代わり、サーバーはレスポンスデータをエコーする直前にリンクデータをレスポンスに追加します。

WP_REST_Response::add_link() メソッドを使用して、カスタムリンクをレスポンスに追加することができます。このメソッドは、リンク関係、URL、オプションでリンク属性のリストの3つのパラメータを受け取ります。例えば、authorwp:term のリンクを追加するには、以下のようにします。

<?php
$response->add_link( 'author', rest_url( "/wp/v2/users/{$post->post_author}" ) );
 
$response->add_link( 'https://api.w.org/term', add_query_arg( 'post', $post->ID, rest_url( "/wp/v2/{$tax_base}" ) ) );

リンク関係は、IANAから登録されたリンク関係か、自分の管理下にあるURIのいずれかでなければいけません。

authorは「コンテキストの著者」と記述された登録リンク関係で、投稿を書いたWordPressユーザーを指すために使用しています。
投稿に関連する用語を記述するリンク関係が存在しないため、WordPressではhttps://api.w.org/term のURLを使用しています。これをCURIE(Compact URIs)を使ってレスポンスを生成する際にwp:termに変換しています。

add_link() の 3 番目のパラメータは、カスタム属性のリストです。
embeddable属性を使用すると、_embedクエリーパラメータを使用したときに、リンクされたリソースがレスポンスの_embeddedセクションに表示されることを含めることができます。同じリレーションで複数のリンクが追加された場合、埋め込まれたレスポンスはリンクが追加された順番と同じになります。

<?php
$response->add_link( 'author', rest_url( "/wp/v2/users/{$post->post_author}" ), array(
    'embeddable' => true,
) );
$response->add_link( 'author', rest_url( "/wp/v2/users/{$additional_author}" ), array(
    'embeddable' => true,
) );

複数の著者の投稿へのリンクの実装例

{
  "_links": {
    "author": [
      {
        "embeddable": true,
        "href": "https://yourwebsite.com/wp-json/wp/v2/users/1"
      },
      {
        "embeddable": true,
        "href": "https://yourwebsite.com/wp-json/wp/v2/users/2"
      }
    ]
  },
  "_embedded": {
    "author": [
      {
        "id": 1,
        "name": "Primary Author"
      },
      {
        "id": 2,
        "name": "Secondary Author"
      }
    ]
  }
}

リンクが追加された順番を維持しています。

CURIEの登録

WordPress のバージョン 4.5 では、Compact URIs、つまり CURIEs のサポートが導入されました。これにより、リンクを参照する際には、長くなりがちなフル URL よりもずっとシンプルな識別子で参照することが可能になりました。

CURIEは、以下のようにrest_response_link_curiesフィルターを使って登録することができます。

<?php
function my_plugin_prefix_register_curie( $curies ) {
 
    $curies[] = array(
        'name'      => 'my_plugin',
        'href'      => 'https://api.mypluginurl.com/{rel}',
        'templated' => true,
    );
 
    return $curies;
}

この例では、APIレスポンスの中のリンクURLを https://api.mypluginurl.com/my_link から my_plugin:my_link に変換します。
WP_REST_Response::add_link を使用してリンクを追加する場合でも、フル URL を使用する必要があります。