サイトアイコン Capital P

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

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

この記事の内容

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

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

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

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

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

ステップ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;
} );

ポイントは以下の通り。

さて、これであとは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 );
          },
        ],
      ],
    ],
  ] );
} );

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

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

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

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

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

ステップ4. 最終確認

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

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

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

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

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

この投稿の続きを読むためには、Capital Pでログインする必要があります。

続きを読む
モバイルバージョンを終了