WordPress+Vue.JSで作るジョブボード(4)最終的に jQueryで完成する

SPONSORED LINK

さて、これまでの連載”Job Board with Vue.JS“を続けてきたのだが、前回からかなりの間が開いてしまったこと、そして、作成すべき機能(ジョブボード)の必要性の高まりに比してなかなか開発スピード&チュートリアル作成スピードが上がらないこと、それほどインタラクティブな要素がないということから、今回はjQueryで作成することになった。2018年の現時点においてもWeb制作現場で幾度となく繰り返されている光景だろうが、筆者もまた同じ光景を見せつけ、華麗にこの連載を終了する。

ジョブボードの作成

以前まではちまちまとダッシュボードを作成していたが、よく考えたらWordPressにはダッシュボードを作成するための必須要素がほとんど揃っている。そのため、カスタムなんちゃらを利用し、次のような機能を作成した。

Capital Pの新しいジョブボード
カスタム投稿タイプ、カスタムタクソノミー、カスタムフィールドを駆使して次のような編集画面を作成。

ポイントは下記の通り。

  • Captal Pの有料会員(寄稿者権限)はジョブボードの求人票を作成することができる採用者である。ただし、管理者によるレビューを経なければ公開することはできない。
  • ログインし、「申し込み先を見る」というボタンを押さなければユーザーは求人表を見ることができない。

これらの機能を実装するまでにしたことをざっくり説明する。

申し込みの定義

さて、ある特定の投稿タイプ(job)に対して申し込みをしたかどうかをどのように定義するかだが、今回は次のようなルールにしたがった。

  • 申し込みはsubmissionという投稿タイプにする。
  • submissionは親IDにjobの投稿IDを持ち、カスタムフィールド(_job_submitter)に申込者のユーザーIDを持つ
  • 一つのjobに対して一人のユーザーが複数の申し込みを行うことはできない

これらのルールを徹底するには、まずカスタム投稿タイプが新規登録できないようにしておく必要がある。この場合、緊急避難的な意味で、「管理者は管理画面から新規登録できるけれども、採用者はできない」というルールを貸しておくのがよいだろう。「新規投稿できなくする」というのはわりと使い勝手の良い方法なので、WordPress の新規追加の権限を切り分けてみるなどを参考にぜひ採用されたし。

<?php
// jobに紐づく投稿タイプsubmissionを作成
add_action( 'init', function(){
  $args = [
    'label' => '申し込み',
    'public' => false,
    'show_ui' => true,
    'capability_type' => 'post',
    'show_in_menu' => 'edit.php?post_type=job',
  ];
  if ( ! current_user_can( 'manage_options' ) ) {
    $args['capabilities'] = [
      'create_posts' => 'create_submissions',
    ];
  }
  register_post_type( 'submission', $args );
} );

これで、目的とする投稿タイプが作成された。show_in_menuは管理画面で投稿タイプ一覧を他の投稿タイプのメニューの下にぶらさげるときに使える。

関数の作成

あとは2つの関数を作成しよう。

まずは、ユーザーの申し込みを作成する関数。これは成功すればsubmissionの投稿オブジェクトを、失敗すればnullオブジェクトを返すようにする。

/**
 * Detect if user has submitted to the job.
 *
 * @param null|int|WP_Post $post
 * @param int $user_id
 * @return WP_Post|null
 */
function capitalp_get_submission( $post = null, $user_id = 0 ) {
  $post = get_post( $post );
  if ( ! $user_id ) {
    $user_id = get_current_user_id();
  }
  foreach ( get_posts( [
    'post_type'      => 'submission',
    'post_parent'    => $post->ID,
    'author'         => $post->post_author,
    'posts_per_page' => 1,
    'meta_query'     => [
      [
        'key'   => '_job_submitter',
        'value' => $user_id,
      ],
    ],
  ] ) as $submission ) {
    return $submission;
  }
  return null;
}

申し込みを作成する関数は、上記の関数 capitalp_get_submission を利用し、すでに申し込みが存在しているならばそれを利用し、なければ作成する。

/**
 * Submit to job
 *
 * @param null|int|WP_Post $post
 * @param int              $user_id
 * @return WP_Post|WP_Error
 */
function capitalp_submit_job( $post = null, $user_id = 0 ) {
  $post = get_post( $post );
  // Check existing.
  $submission = capitalp_get_submission( $post, $user_id );
  if ( $submission ) {
    return $submission;
  }
  // Create new one.
  $submission_id = wp_insert_post( [
    'post_type'   => 'submission',
    'post_parent' => $post->ID,
    'post_author' => $post->post_author,
    'post_status' => 'publish',
    'post_title'  => sprintf( '%s - %s', get_the_title( $post ), date_i18n( 'Y-m-d H:i:s' ) ),
  ], true );
  if ( is_wp_error( $submission_id ) ) {
    return $submission_id;
  } else {
    update_post_meta( $submission_id, '_job_submitter', $user_id );
    return get_post( $submission_id );
  }
}

ではこれをどこで実行するか。この場合は以前紹介したREST APIを利用する。

/**
 * Register job board endpoint
 */
add_action( 'rest_api_init', function() {
  register_rest_route( 'capitalp/v1', 'job/(?P<job_id>\d+)', [
    [
      'methods' => 'POST',
      'args'    => [
        'job_id' => [
          'required' => true,
          'validate_callback' => function( $var ) {
            return 'job' === get_post_type( $var );
          },
        ],
      ],
      'permission_callback' => function() {
        return current_user_can( 'read' );
      },
      'callback' => function( WP_REST_Request $request ) {
        $job = get_post( $request->get_param( 'job_id' ) );
        if ( 'publish' !== $job->post_status || ! capitalp_job_is_open( $job ) ) {
          return new WP_Error( 'job_is_closed', 'この求人は現在募集をしていません。', [
            'status'   => 403,
            'response' => 403,
          ] );
        }
        $response = capitalp_submit_job( $job, get_current_user_id() );
        return is_wp_error( $response ) ? $response : new WP_REST_Response( $response );
      },
    ],
  ] );
} );

/wp-json/capitalp/v1/job/10 にPOSTすると、そのユーザー専用のsubmission投稿タイプが一つ作成されるということだ。

それでは、次のようなフローでボタンを押させるようにしよう。

  • ユーザーは求人情報の詳細を見る。企業情報、申し込み先などはログインしないと見ることができない。
  • ログインしたユーザーは求人に応募するつもりになったら、ボタンをクリックする。
  • submission投稿オブジェクトがREST API経由で作成され、ユーザーは同じページにリダイレクト。晴れて求人に応募することができるようになる。

ボタンの表示は割愛するが、Javascriptはおさらいをかねて、読み込み方法を書いておこう。

// Enqueue Javascript.
wp_enqueue_script(
  'capitalp-job-board',
  get_stylesheet_directory_uri() . '/assets/js/job-board.js',
  [ 'jquery' ],
  wp_get_theme()->get( 'Version' ),
  true
);
// Register variables.
wp_localize_script( 'capitalp-job-board', 'JobBoard', [
  'endpoint' => rest_url( '/capitalp/v1/job/' . get_the_ID() ),
  'nonce'    => wp_create_nonce( 'wp_rest' ),
] );

ポイントは wp_localize_script を利用して必要な変数(URL, nonceなど)を渡すこと。もちろん、ボタン自体に変数を持たせる(e.g. data-post-id="10")なら、もっと柔軟なパーツを作成することができる。

それでは、このJavascriptの中身を紹介しよう。申し込み完了後、おしゃれなインタラクションを行いつつ画面がそのまま書き変わるのが理想だが、時間がないこと、筆者にUXの才能がないことの2点の理由により、alertを出してリロード、以上。

(function ($) {

  'use strict';

  $(document).on('click','#capitalp-job-submit-button', function(e){
    e.preventDefault();
    var $btn = $(this);
    if ( $(this).hasClass('disabled') ) {
      return;
    }
    $(this).attr('disabled', true).addClass('disabled');
    $.post(JobBoard.endpoint, {
       _wpnonce: JobBoard.nonce
    }).done(function(response){
      alert('求人情報の申し込み先を表示いたします。画面をリロードしますので、"OK"を押してください。');
      window.location.reload();
    }).fail(function(response){
      var msg = '申し込みに失敗しました。またあとでやり直してください。';
      if ( response.responseJSON && response.responseJSON.message ) {
        msg = response.responseJSON.message;
      }
      alert( msg );
    }).always(function(){
      $btn.removeClass('disabled').attr('disabled', null);
    });
  });


})(jQuery);

最終的に、このようなページになる。

求人ページの完成形

以上、簡単ではあるが、ジョブボード作成の連載を終えることにする。筆者は筆者でVueJSを利用しているのだが、どうも「ジョブボード」程度の要件だとわざわざVueJSを利用するまでもないようだ。もう少し、ユーザーのインタラクティブな動作が多い案件の方が適しているように思われる。

Vue.JSの連載として始まったものが最後jQueryで終わるとなると、Vue好きの人には「ふざけんな!」と声を荒げる向きもあるかもしれないが、どうかご寛恕いただきたい。時間がなかったのだ。VueもjQeuryもどちらも優れたフレームワークであり、その用途さえ間違えなければ有用ということは申し添えておきたい。

有料会員向け特典

以下は有料会員専用の特典として次のものをお届けする。

  • 1時間20分程度のライブコーディングビデオ
  • Github上での具体的なコミットの指摘(e.g. このコミットがカスタムナンチャラ、このコミットがREST API)

実際の作業量としては、Vue以外が大半を占めていることからもわかっていただけると思う。

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

Club Capital P

Club Capital PはCapital Pのファンクラブです。有料会員制となっており、Gumroad経由でサブスクリプションをご購入いただき、ライセンスキーを登録いただくことで、会員特典を受け取ることができます。