[WordPress] ACFのJavaScript APIを使ってselect2フィールドをカスタマイズする

WordPress の カスタムフィールドのプラグインである ACF (Advanced Custom Fields) には、多数のフックや JavaScript API が用意されています。

ACF | Resources, Documentation, API, How to & Tutorial Articles
Discover code, documentation and ideas in this comprehensive resource section. Find everything from Getting Started, Fie...
ACF | JavaScript API
Learn how to use ACF's JavaScript library including functions, actions and filters and models.

やること

今回は、JavaScript API とフックの組み合わせで select2 フィールドをカスタマイズしてみます。具体的には、投稿のプルダウンをラジオボタンで選択したカテゴリーのみに絞って表示させる、ということをやってみます。

カテゴリーのラジオボタンと投稿オブジェクトのプルダウンが表示されている。ラジオボタンで選択したカテゴリーの投稿だけプルダウンに表示するようにする、という説明が入っている

準備

ACF でカテゴリーのラジオボタンと投稿のプルダウンを用意します。表示する条件は固定ページにしておきます。

ACFのフィールド。カテゴリーのラジオボタンと投稿の投稿オブジェクト

このときの各フィールドのキー (field_667d1f74494c8field_667d1d6343e57) を控えておきます。

また、初期データとして、投稿とカテゴリーをいくつか用意しておきます。

固定ページ編集ページで表示されるカテゴリーと投稿のカスタムフィールド

投稿のプルダウンをクリックすると、全ての投稿が選択肢として表示されます。

投稿のプルダウンクリック時に全ての投稿が選択肢として表示されている

コーディング

select2 フィールドのプルダウンはクリックの都度、Ajax 通信で表示する選択肢を取得しています。この Ajax のリクエストに介入する JavaScript のフックに select2_ajax_data というのが用意されています。

ACF | JavaScript API
Learn how to use ACF's JavaScript library including functions, actions and filters and models.
getAjaxData: function (params) {
  // vars
  var ajaxData = {
    action: this.get('ajaxAction'),
    s: params.term || '',
    paged: params.page || 1
  };

  // field helper
  var field = this.get('field');
  if (field) {
    ajaxData.field_key = field.get('key');
    if (field.get('nonce')) {
      ajaxData.nonce = field.get('nonce');
    }
  }

  // callback
  var callback = this.get('ajaxData');
  if (callback) {
    ajaxData = callback.apply(this, [ajaxData, params]);
  }

  // filter
  ajaxData = acf.applyFilters('select2_ajax_data', ajaxData, this.data, this.$el, field || false, this);

  // return
  return acf.prepareForAjax(ajaxData);
},

このフックを使ってラジオボタンで選択されたカテゴリーの情報を POST に追加するようにします。適当な JS ファイルに以下の記述をします。

// 素の JS
acf.add_filter('select2_ajax_data', function (data, args, $input, field, instance) {
    if ('field_667d1d6343e57' !== data.field_key) {
        // 投稿のプルダウンでなければ何もしない
        return data;
    }

    // カテゴリーのラジオボタンで選択された value を取得する
    const selectedCat = document.querySelector('input[name="acf[field_667d1f74494c8]"]:checked');
    if (selectedCat) {
        data.cat = selectedCat.value;
    }
    return data;
});

// jQuery
jQuery(document).ready(function ($) {
    acf.add_filter('select2_ajax_data', function (data, args, $input, field, instance) {
        if ('field_667d1d6343e57' !== data.field_key) {
            return data;
        }
        const $selectedCat = $('input[name="acf[field_667d1f74494c8]"]:checked');
        if ($selectedCat) {
            data.cat = $selectedCat.val();
        }
        return data;
    });
});

フィールド作成時に控えておいたキーはユニークになっているので、セレクタとして使用しています。

次に、PHP 側のフックを使って、作成した JS ファイルの enqueue を行います。注意点として、ACF の JavaScript API を利用するには、ハンドル名 acf-input があらかじめ読み込まれている必要があるので、引数に追加するのを忘れないようにします。

add_action('admin_enqueue_scripts', function ($hook_suffix) {
    if ('post.php' === $hook_suffix || 'post-new.php' === $hook_suffix) {
        wp_register_script('acf-js-api-usage', plugin_dir_url(__FILE__) . '/acf-js-api-usage.js', ['acf-input']);
        wp_enqueue_script('acf-js-api-usage');
    }
});

PHP 側では POST したカテゴリー情報をハンドリングする処理も必要です。ACF で、acf/fields/post_object/query/key={$key} というフックが用意されているので、これを利用します。

ACF | acf/fields/post_object/query
Filters the $args used to query posts in the Post Object field.
add_filter('acf/fields/post_object/query/key=field_667d1d6343e57', function($args, $field, $post_id) {
    $args['cat'] = $_POST['cat'];
    return $args;
}, 10, 3);

このフィルターフックで返却する $args は最終的に WP_Query の引数になります。今回はカテゴリーで絞り込むので、POST した値を $args['cat'] に代入します。

画面を確認してみると、ラジオボタンで選択したカテゴリーの投稿のみが表示されていることがわかります。

まとめと感想

今回、ACF のコードを数年ぶりに読みました。私にとって、WordPress のプラグインの中では読みやすい部類だと感じました。ドキュメントもしっかりメンテされている (と思っている) ので、迷子になることもなかったです。

ACF の JavaScript API とフックは、設定画面では出来ないようなフィールドの複雑なカスタマイズやフィールドの表示条件を動的に変更することが出来るので、わりと便利な機能だと思っています。ただ、認知度が低い気がします。日本語の記事もほとんど見当たらなかったですし。だいぶニッチな機能なので仕方ないと言えばそうなのですが。。

タイトルとURLをコピーしました