はじめに
以前、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!!
注意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