1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WordPressのAdvancedCustomFieldsに「JavaScriptで」デフォルト値を設定する方法

Last updated at Posted at 2022-03-16

はじめに

以前、PHPでWordPressのデフォルト値を設定する方法を説明しました。
ですが、私の案件は、ページ読み込み時にデフォルト値を設定するのではなく、
特定のカスタムフィールドの値変更をトリガーにして、デフォルト値を設定するというものです。

$\tiny{…いる?}$

そのため、JavaScriptでinput changeイベントを取らなくてはなりません。
でも、ACFのデフォルト値を変更する方法は、PHPでのやり方ならネットにいくらでもありますが、
JavaScriptで書き換える方法が全然載ってないので、備忘録がてら書くことにしました。

仕様

  • ページ読み込み時は何もしない
  • 表示開始日付(open_date)の値が更新された時、表示終了日付(close_date)に一カ月後の値をセットする
  • 表示終了日付(close_date)に既に値がある場合、何もしない

下準備

まずは、管理画面でだけ使うJavaScriptファイルを用意します。
touchコマンドで生成しておきます

cd /path/to/wordpress/wp-content/themes/your_theme/
mkdir admin
cd $_
mkdir js
cd $_
touch my_admin_script.js

これで /path/to/wordpress/wp-content/themes/your_theme/admin/js/my_admin_script.js が生成されました。

次に、管理画面の記事編集画面でだけ、
任意のJavaScriptファイルを呼び出すために以下の記述をします

左サイドバー > 外観 > テーマエディター

右サイドバー > テーマファイル > テーマのための関数 (functions.php)
/path/to/wordpress/wp-content/themes/your_theme/functions.php

/**
 * 指定カテゴリの記事登録OR編集画面かどうかを判定する
 */
function isEventPostPage()
{
    $currentScreen = get_current_screen();

    $isPostPage = ($currentScreen->base == 'post'); // 管理画面 新規登録 または 編集ページか?
    $isValidPostType = ($currentScreen->post_type == 'events'); // 指定カテゴリの登録画面か?

    $isValid = ($isValidPostType && $isPostPage);

    return $isValid;
}

/**
 * WP管理画面 イベントページのみカスタムJSファイルを読み込ませる
 * JavaScriptでデフォルト値を設定するため
 */
function add_js_admin_scripts_post_events()
{
    if(! isEventPostPage()){ // 条件に一致しないページでは読み込ませない
        return false;
    }

    // 条件に一致する場合のみ対象JSファイルを読み込む(要JSファイル作成)
    $file = get_template_directory_uri() . '/admin/js/my_admin_script.js';
    wp_enqueue_script('js-admin-post-events', $file, array('jquery'));
}

add_action('admin_enqueue_scripts', 'add_js_admin_scripts_post_events');

上記の記述を追記したら、ファイルを更新を押します。

これで、WordPress管理画面でイベントカテゴリの記事投稿、記事編集画面でだけ
JavaScriptファイルが読み込まれるようになりました。

本当にそのページだけで読み込まれているか、以下の記述をして確認してみましょう

左サイドバー > 外観 > テーマエディター

右サイドバー > テーマファイル > admin > js > my_admin_script.js
/path/to/wordpress/wp-content/themes/your_theme/admin/js/my_admin_script.js

jQuery(function($){
  console.log("admin");
});

上記の記述を追記したら、ファイルを更新を押します。

WordPress管理画面でイベントカテゴリの記事投稿、記事編集画面で
Chromeの開発者ツールを開き、コンソール画面に「admin」の文字が出ること、
また、それ以外のページ(フロント含む)ではコンソール画面に文字がでなければOKです。

これで下準備が完了しました。
次に、JavaScriptでデフォルト値を設定する方法をご紹介します。

実装

左サイドバー > 外観 > テーマエディター

右サイドバー > テーマファイル > admin > js > my_admin_script.js
/path/to/wordpress/wp-content/themes/your_theme/admin/js/my_admin_script.js

var ACF_EL_DATE_SOURCE = 'open_date';
var ACF_EL_DATE_TARGET = 'close_date';

/**
 * 指定月をDateインスタンスに加算して返す
 *
 * @param  Date dt Dateインスタンス
 * @param  Integer monthVal 加算する月数 1 -> 1ヶ月加算
 * @return Date nヶ月を加算したDateインスタンス
 */
function addMonth(dt, monthVal)
{
  var newDate = new Date(dt.setMonth(dt.getMonth() + monthVal));
  return newDate;
}

/**
 * ACFインスタンスをカスタムフィールド名から取得する
 *
 * @param  String name カスタムフィールド名 "close_date" など
 * @return Object instance ACFインスタンス
 */
function getAcfInstanceByName(name)
{
  var instance = acf.getFields({name: name}).shift();
  return instance;
}

/**
 * ACFカスタムフィールドの値をカスタムフィールド名から取得する
 *
 * @param  String name カスタムフィールド名 "close_date" など
 * @return mixed val カスタムフィールドの値 "2022-03-16" など
 */
function getAcfValByName(name)
{
  var val = (val = acf.getFields({name: name}).shift()) ? val.val() : '';
  return val;
}

/**
 * ACFカスタムフィールドのデフォルト値(input hiddenの値=POSTされる値)を設定する
 *
 * @param  String name カスタムフィールド名 "close_date" など
 * @param  mixed val カスタムフィールドの値 "2022-03-16" など
 * @return void
 */
function setAcfValByName(name, val)
{
  acf.set(name, val);
}

/**
 * 日付文字列からDateインスタンスを生成する
 *
 * @param  String dateStr 日付文字列 YYYY-MM-DD 形式 "2022-03-16" など
 * @return Date dt Dateインスタンス
 */
function createDateFromYmd(dateStr)
{
  var date = Date.parse(dateStr);
  var dt = new Date(date);
  return dt;
}

/**
 * Dateインスタンスから日付文字列を生成する
 *
 * @param  Date dt Dateインスタンス
 * @return String dateStr 日付文字列 YYYY-MM-DD 形式 "2022-03-16" など
 */
function formatDateToYmd(dt)
{
  var y = dt.getFullYear();
  var m = ('00' + (dt.getMonth()+1)).slice(-2);
  var d = ('00' + dt.getDate()).slice(-2);
  var dateStr = (y + '-' + m + '-' + d);
  return dateStr;
}

/**
 * ACFカスタムフィールドの親要素(div)にクラスを付与する
 * デイトピッカーの表示値を変えるために使用
 *
 * @param  Object instace ACFインスタンス
 * @param  String className クラス名
 * @return void
 */
function addClassToAcfFieldParent(instance, className)
{
  instance.$el.addClass(className);
}

/**
 * ACFカスタムフィールドのデフォルト値(デイトピッカーの表示値)を設定する
 *
 * @param  String name カスタムフィールド名 "close_date" など
 * @param  String dateStr 日付文字列 YYYY-MM-DD 形式 "2022-03-16" など
 * @return void
 */
function setAcfDatePickerValByName(name, dateStr)
{
  var instance = getAcfInstanceByName(name);
  var className = 'tmp_acf_parent_' + name; // e.g. "tmp_acf_parent_close_date"
  addClassToAcfFieldParent(instance, className);

  var target = jQuery('.' + className).find('input[type=text]');
  jQuery(target).addClass('tmp_acf_target_' + name); // e.g. "tmp_acf_target_close_date"
  jQuery(target).datepicker('setDate', dateStr); // set default value to datepicker
}

/**
 * デイトピッカーのデフォルト値を変更する
 *
 * @return boolean
 */
function updateDispCloseDate()
{
  var source = ACF_EL_DATE_SOURCE;
  var target = ACF_EL_DATE_TARGET;

  var srcDate = getAcfValByName(source);
  var tgtDate = getAcfValByName(target);

  if(tgtDate){ // if target value is already set, then cancel
    return false;
  }

  if(! srcDate){ // if source value is empty, then cancel
    return false;
  }

  srcDate = createDateFromYmd(srcDate);

  // add one month to date
  var monthVal = 1;
  var newDate = addMonth(srcDate, monthVal);
  var dateStr = formatDateToYmd(newDate);

  // update hidden value
  setAcfValByName(target, dateStr);

  // Check that the value change worked correctly
  //tgtDate = getAcfValByName(target);
  //console.log(tgtDate);

  // update visible date picker value
  setAcfDatePickerValByName(target, dateStr);

  return true;
}

// run when page loaded
//window.onload = function(){
//  updateDispCloseDate();
//};

// run when source input changed
jQuery(function($){
  var source = ACF_EL_DATE_SOURCE;
  var srcInstance = getAcfInstanceByName(source);

  srcInstance.on('change', 'input[type="text"]', function(e){
    var srcVal = $(this).val();
    //console.log(srcVal);

    updateDispCloseDate();
  });
});

上記の記述を丸ごと上書きしたら、ファイルを更新を押します。

イベント記事の新規登録画面に行き、「表示開始日付」を変更して動作確認します。

動作確認

Gotcha!!

2022-03-16-11-23-43.gif

注意1 ACFのバージョンは5.7以上にアップデートすること

ACF JavaScript APIを使用するには、ACFのバージョンが「5.7」以上である必要があります。
筆者の環境はもともと 4.4.12 でしたが、 2022年3月時点の最新である 5.12 にアップデートしました。

JavaScript APIの書き方はjQueryに似ていますが、
微妙に異なるので公式ドキュメントを読んでください

公式ドキュメントの一番上に
「ACF JavaScript APIはバージョン5.7以上で使用できます」とか書いといて欲しかった…

注意2 ACFをバージョンアップすると ACF Date and Time Pickerの表示がおかしくなる

「Advanced Custom Fields: Date and Time Picker」を使用していると、
プラグインのアップデートをしたら表示がおかしくなります。
時刻の表示がUnixタイムになるのです。 「2022-03-16 11:00」が「2022-03-16 1647396000」になるみたいな。

そうなったときは、以下の手順で直せます

左サイドバー > カスタムフィールド > フィールドグループ > {任意のフィールドグループ名}

タイプがDate Time Pickerになっているものを、以下のように修正

フィールドタイプ
Date Time Picker -> Time Picker

表示フォーマット
Custom H:i

返り値のフォーマット
Custom H:i

注意3 jQueryを読み込ませる時は「$」ではなく「jQuery」インスタンスを使うこと

これはACF関係なくWordPressのお約束ですが…

$(function(){ doSomething(); }); // ダメ

jQuery(function($){ doSomething(); }); // OK

注意4 Wordpressで無条件のtouchコマンドは書かないこと

絶対に、以下のようなコマンドでファイルを生成してはいけません
WEBサーバーが壊れます

// このコマンドを使わないでください!サーバーが壊れます!
// Do NOT use this!!! Your Server will be broken.
//add_action('after_setup_theme', function() {
	//$file = get_template_directory_uri() . '/admin/js/my_admin_script.js';
	//touch($file);
//});

※本文を読まずにネットからソースコードをコピペしてやらかす人(私)がいると思うので、全文コメントアウトしておきます

これを書くと、5秒毎?にadmin-ajax.phpが動いてファイル作成を行おうとし、
CPU使用率がグングンあがってメモリ不足になり、ページの閲覧も、
FTP接続も、ファイルの操作も「ほぼ」受け付けない状態になります。
vimコマンドでfunction.phpを直接編集しようにも、
「cannot allocate memory」エラーが出てファイルが表示されません。

万が一、上記コマンドを使ってしまった場合、以下の手順で解消できます。

① ブラウザで管理画面を開いていれば全員閉じる

上記アクションはHTTPリクエストがある度に発火します。
つまり、誰かが管理画面ページを開き続けている限り、
ずっとメモリ不足の状況が続きます。
以下の作業をするにも重たくてどうしようもなくなるので、まずはブラウザを閉じましょう。

② WEBサーバーを停止する

sudo service httpd stop

③ function.phpから問題のある記述を削除する

cd /path/to/wordpress/wp-content/themes/your_theme/
sudo vim function.php

上のtouchコマンドに関する記述を削除して、上書き保存します

④ WEBサーバーを再開する

sudo service httpd start

⑤ 動作確認する

参考URL

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?