WordPressでREST APIから投稿させる機能を作る

筆者は自分のブログ高橋文樹.comでWordPressで管理画面以外から投稿させる機能を作るというエントリーを発表し、それはいまだに人気記事なのだが、そろそろ古くなってきたので、REST APIバージョンを発表することにした。

この記事の内容

さて、この記事はオリジナルバージョンがそうであったように小銭を要求するシステムになっている。

今回は掲示板を例にして、このフォームの作り方から説明しつつ、チュートリアルビデオとソースコードを300円で売りつけるという社会実験へと至る華麗な流れを見せたいと思います。

本題:REST API経由でコンテンツを投稿させる

さて、それでは本題に入ろう。今回は次のようなケースを想定する。

  • WordPressのユーザー(購読者)は投稿を行うことができる
  • 投稿できるのは「タイトル」と「本文」のみ

本来であれば、投稿一覧なども作成しなければならないのだが、今回は対象としない。

ステップ1. 投稿画面

まず、投稿を行うための画面を作成しよう。これはWooCommerceにならい、「フォームを出力するショートコード」を作成する。

add_shortcode( 'my-first-rest', function () {
  ob_start();
  if ( is_user_logged_in() ) :
  ?>
  <div>
    <p>
      <label>
        投稿タイトル
        <input type="text" id="post-title" value="" />
      </label>
    </p>
    <p>
      <label>
        本文
        <textarea id="post-content"></textarea>
      </label>
    </p>
    <p>
      <button class="button" id="post-submit">送信</button>
    </p>
  </div>
  <?php else : ?>
    <p>
      <?php wp_loginout( get_permalink() ) ?>してください。
    </p>
  <?php endif;
  $content = ob_get_contents();
  ob_end_clean();
  return $content;
} );

これで該当する固定ページにアクセスすると、フォームが表示される。ポイントとしては、 is_user_logged_in で判定を行い、もしログインしていない場合はログインを促すリンクを表示しているところだ。

また、ショートコードはその性質上、文字列を返さなければならないので、ob_start という出力制御機能を使っている。

フォームはこんな感じ

ステップ2. Javascriptを書く

筆者が以前書いたチュートリアルではformタグを利用したが、最近のJavascript界隈ではもうformを使わなくなってきている。もちろん、アクセシビリティなどに考慮するとformで書いた方がいいのかもしれないが。

今回はそうした流行にならい、formは使っていない。その代わり、Javascriptで代用する。次のようなコードを書き、Javascriptを読み込もう。

add_action( 'init', function() {
  wp_register_script( 'my-first-rest', plugin_dir_url( __DIR__ ) .'my-first-rest-script.js', ['jquery'], '1.0.0', true );
  wp_localize_script( 'my-first-rest', 'MyFirstRest', [
    'endpoint' => rest_url( '/my-first-rest/v1' ),
    'nonce' => wp_create_nonce( 'wp_rest' ),
  ] );
} );

// さっき書いたショートコード登録
add_shortcode( 'my-first-rest', function () {
  ob_start();
  if ( is_user_logged_in() ) :
    wp_enqueue_script( 'my-first-rest' );
  //...
  //...
  return $content;
} );

ポイントは以下の通り。

  • initフックでjavascriptを登録している。これは使う時になったらいつでも呼び出せるようにするため。呼び出しはショートコードの中で行なっている。
  • wp_localize_scriptという元々は翻訳のための関数を用い、WordPressごとに変わるREST APIのエンドポイントを渡している。また、同時に nonce というセキュリティ用のトークン渡している。
  • これらの値は指定した変数 MyFirstRest がグローバルに展開されるので、どこからでもアクセスできる。

さて、これであとはJavascriptの中身を書くだけだ。

/**
 * Description
 */

/*global MyFirstRest: true*/

(function ($) {

  'use strict';

  $(document).on('click', '#post-submit', function(event){
    event.preventDefault();
    var title = $('#post-title').val();
    var content = $('#post-content').val();
    $.post( MyFirstRest.endpoint + '/post', {
      title: title,
      content: content,
      _wpnonce: MyFirstRest.nonce
    } ).done(function(response){
      alert( response.message );
      window.location.href = response.url;
    }).fail(function(response){
      alert(response.responseJSON.message);
    });
  });
})(jQuery);

button#post-submit がクリックされたら、jQueryを利用して投稿タイトルと投稿本文を取得、それをREST APIのエンドポイントである /wp-json/ofuse/v1/post に投げるという動作である。成功したらメッセージを表示して該当する投稿にリダイレクトする。

リクエストに nonce を渡しているのだが、これはREST APIの仕様による。旧日本語ドキュメントによると……

マニュアルで Ajax リクエストを送る場合、nonce は各リクエストで毎回送信する必要があります。 送信された nonce は wp_rest にセットされたアクションと一緒に利用されます。 API への nonce の受け渡しは、POST のデータや GET のクエリの _wpnonce パラメータ、 あるいはX-WP-Nonce ヘッダーにセットして行います。

ということなので、wp_create_nonce('wp_rest') で作成された nonce がないと「ログインしている」という判定が行われない。逆にいえば、nonceとCookieによりCSRFなどの脆弱性を防いでいる。

※ちなみに、ビデオを見ていただくと、実際に確認しながらコーディングしている様子がわかる。

ステップ3. APIを書く

あとはREST APIを作成するだけだ。REST APIは rest_api_init で登録する。

add_action( 'rest_api_init', function() {
  register_rest_route( 'my-first-rest/v1', 'post', [
    [
      'methods' => 'POST',
      'callback' => function( WP_REST_Request $request ) {
        // 実際の処理.
        $user = wp_get_current_user();
        $post_id = wp_insert_post( [
          'post_type' => 'post',
          'post_status' => $request['status'],
          'post_title' => $request['title'],
          'post_content' => $request['content'],
          'post_author' => get_current_user_id(),
        ], true );
        if ( is_wp_error( $post_id ) ) {
          return $post_id;
        }
        return new WP_REST_Response( [
          'success' => true,
          'url' => get_permalink( $post_id ),
          'message' => '投稿ありがとうございます。投稿に移動します。',
        ] );
      },
      'permission_callback' => function() {
        return current_user_can( 'read' );
      },
      'args' => [
        'title' => [
          'required' => true,
          'description' => '投稿のタイトルになります。',
          'validation_callback' => function( $var ) {
            return ! empty( $var );
          },
        ],
        'content' => [
          'required' => true,
          'description' => '投稿の本文になります。',
          'validation_callback' => function( $var ) {
            return ! empty( $var );
          },
        ],
        'status' => [
          'default' => 'publish',
          'validation_callback' => function( $var ) {
            return in_array( $var, [ 'publish', 'draft', 'private' ], true );
          },
        ],
      ],
    ],
  ] );
} );

それぞれのパラメータの詳細は以下の通り。

  • methods: GET, POST, PUT, DELETEなどのHTTPメソッド
  • callback: 実際に行う処理
  • permission_callback: 誰がこのエンドポイントを利用できるか。今回は read という購読者以上なら誰でも持つ権限を利用。
  • args: エンドポイントに渡されるパラメータ。

さらに、argsのそれぞれに設定できるパラメータはこちら。

  • required: true になっている場合、この値がなかった時点でエラーとなる。
  • default: デフォルト値。
  • validation_callback: 値を検証するための関数。たとえばタクソノミーなら taxononmy_exists() などなど、複雑なバリデーションも行える。
  • sanitize_callback: 無害化のための関数。

ポイントとしては、これらのバリデーションをきちんと行うことで、実際のcallbackのロジックがシンプルになること。callbackの中身が if-else の連打になっていたら、バリデーション・サニタイゼーションがうまくできていないのでは、と疑おう。

また、APIは極力意味のあるメッセージ、それも商用環境で利用可能なものにしよう。要するに、ユーザーにそのまま見せても問題のないメッセージを返すということだ。

そうしないと、Javascriptの中で色々と書かなければいけなくなるし、なにより翻訳しなければならない場合に辛い。

ステップ4. 最終確認

さて、以前の記事がステップ10ぐらいまであったことを考えると、大変な省力化が進んだものだ。実際に動くかどうかは、ビデオをご覧いただきたい。

Club Capital Pのライセンスを購入していただくと、以下のものが提供される。

  1. 1時間におよぶライブコーディングビデオ
  2. ソースコードの全体

ビデオを見ながらソースコードを眺めていただくだけでも、かなり有益な情報が得られるだろう。

ビデオでは「そもそもRESTとは?」「最近のJavascript事情」などのオフトピックも充実している。

続きの 5% を読み、添付されたファイルにアクセスするには、Gumroadでライセンスキーを取得してください!

“WordPressでREST APIから投稿させる機能を作る”への9件の反応

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください