WooCommerceならびにWordPressのデフォルト機能では、「公開日の予約」はできるが「非公開の予約」はできない。
ので、自作した。カスタムフィールドに登録された日付に基づき、cronイベントをスケジューリングする、という方法になる。
カスタムフィールドを使う方法となるので、カスタムフィールドは自作なり「Advanced Custom Fields (ACF)」なりで、用意されたし。
下記で想定しているカスタムフィールドは、
「_公開開始日」「_公開終了日」。
入力ルールは、「20230418_10:00」とする。(「_」や「:」といった記号も必須とする)
(時間指定がなく、日付が変わった瞬間に公開・非公開を変えたい場合は、「_10:00」は不要)
結論:functions.phpに下記を記述
// 公開にするスケジュールを設定
add_action('woocommerce_new_product', 'on_product_save', 10, 1);
add_action('woocommerce_update_product', 'on_product_save', 10, 1);
function on_product_save($product_id)
{
if (get_post_meta($product_id, '_公開開始日', true)) {
//_公開開始日 の値をとってくる
$event_date = get_post_meta($product_id, '_公開開始日', true);
//_公開開始日 を日にち と 時間 で分ける
$event_date1 = substr($event_date, 0, 8);
$event_date2 = substr($event_date, 9);
if (empty($event_date2)) {
// 時間に入力なければ00:00にする
$event_date2 = '00:00';
};
//20230419のような値を DateTimeオブジェクトに
$date = new DateTime($event_date1 . ' ' . $event_date2 . ':00 JST');
//タイムスタンプに変換
$time_stamp = date_timestamp_get($date);
//すでにスケジュールされているものがあれば削除
wp_clear_scheduled_hook('my_save_to_publish', array($product_id));
//タイムスタンプに応じて my_save_to_publish を実施するスケジュールを設定
wp_schedule_single_event($time_stamp, 'my_save_to_publish', array($product_id));
} else {
// 値をブランクにしたらスケジュールを削除
wp_clear_scheduled_hook('my_save_to_publish', array($product_id));
}
};
//my_save_to_publish のアクション my_update_to_publish を設定
add_action('my_save_to_publish', 'my_update_to_publish');
function my_update_to_publish($product_id)
{
//投稿を公開にする
wp_update_post(array('ID' => $product_id, 'post_status' => 'publish'));
//カスタムフィールドを空にする
update_post_meta($product_id, '_公開開始日', '');
}
// 非公開にするスケジュールを設定
add_action('woocommerce_new_product', 'on_product_save', 10, 1);
add_action('woocommerce_update_product', 'on_product_save', 10, 1);
function on_product_save($product_id)
{
$product = wc_get_product($product_id);
if (get_post_meta($product_id, '_公開終了日', true)) {
//_公開終了日 の値をとってくる
$event_date = get_post_meta($product_id, '_公開終了日', true);
//_公開終了日 を 日にち と 時間 で分ける
$event_date1 = substr($event_date, 0, 8);
$event_date2 = substr($event_date, 9);
if (empty($event_date2)) {
// 時間に入力なければ00:00にする
$event_date2 = '00:00';
};
//20220814のような値を DateTimeオブジェクトに
$date = new DateTime($event_date1 . ' ' . $event_date2 . ':00 JST');
//タイムスタンプに変換
$time_stamp = date_timestamp_get($date);
//すでにスケジュールされているものがあれば削除
wp_clear_scheduled_hook('my_save_to_private', array($product_id));
//タイムスタンプに応じて my_save_to_private を実施するスケジュールを設定
wp_schedule_single_event($time_stamp, 'my_save_to_private', array($product_id));
} else {
// 値をブランクにしたらスケジュールを削除
wp_clear_scheduled_hook('my_save_to_private', array($product_id));
}
};
//my_save_to_private のアクション my_update_to_private を設定
add_action('my_save_to_private', 'my_update_to_private');
function my_update_to_private($product_id)
{
//投稿を非公開にする
wp_update_post(array('ID' => $product_id, 'post_status' => 'private'));
update_post_meta($product_id, '_公開終了日', '');
}
ざっくり流れを言うと、
1. 2~3行目:商品登録か、更新された時のアクション「on_product_save」を定義
2. 5~28行目:「on_product_save」の中では、カスタムフィールド値を使って、タイムスタンプで実行される「my_save_to_publish」をスケジュール
3. 31行目:「my_save_to_publish」で実行するアクション「my_update_to_publish」を定義
4. 32~38行目:「my_update_to_publish」で実施する処理(投稿を「公開」にする処理)を記述
としている。42行目以降は非公開の処理となり、書いている順番・内容は上記1~4と同じ。使ってるカスタムフィールド値と、投稿を「非公開」にする処理に書き換えてるだけでほぼ一緒。
個別にポイント解説。
新規商品登録 or 商品更新の際に処理が走るようにアクションフックを設置
// 公開にするスケジュールを設定
add_action('woocommerce_new_product', 'on_product_save', 10, 1);
add_action('woocommerce_update_product', 'on_product_save', 10, 1);
WooCommerceが用意している「woocommerce_new_product」(新規商品登録した時)と「woocommerce_update_product」(商品更新の時)のアクションが発生した時に、「on_product_save」が走るようにする。WordPressの用意しているアクションフック「save_post」じゃダメなの?と思われがちだが、これはNG。これ使うと、CSVインポートの時に処理が走らない。
on_product_saveの中身を設定
function on_product_save($product_id)
{
$product = wc_get_product($product_id);
if (get_post_meta($product_id, '_公開終了日', true)) {
//_公開終了日 の値をとってくる
$event_date = get_post_meta($product_id, '_公開終了日', true);
//_公開終了日 を 日にち と 時間 で分ける
$event_date1 = substr($event_date, 0, 8);
$event_date2 = substr($event_date, 9);
if (empty($event_date2)) {
// 時間に入力なければ00:00にする
$event_date2 = '00:00';
};
//20220814のような値を DateTimeオブジェクトに
$date = new DateTime($event_date1 . ' ' . $event_date2 . ':00 JST');
//タイムスタンプに変換
$time_stamp = date_timestamp_get($date);
//すでにスケジュールされているものがあれば削除
wp_clear_scheduled_hook('my_save_to_private', array($product_id));
//タイムスタンプに応じて my_save_to_private を実施するスケジュールを設定
wp_schedule_single_event($time_stamp, 'my_save_to_private', array($product_id));
} else {
// 値をブランクにしたらスケジュールを削除
wp_clear_scheduled_hook('my_save_to_private', array($product_id));
}
};
ここの記述の前半(6~18行目)でやっているのは、カスタムフィールドの数値をタイムスタンプに変換する処理だ。
19行目は、すでにスケジューリングされているものがあったら削除しておく、と言う処理だ。(これがないと1回登録した処理が消せない)
21~25行目で、タイムスタンプ化された情報を使って、その時間に実施されるアクション「my_save_to_publish」を作成してる。
my_save_to_publishのアクションmy_update_publishを定義
//my_save_to_publish のアクション my_update_to_publish を設定
add_action('my_save_to_publish', 'my_update_to_publish');
my_update_to_publishの処理を記述
function my_update_to_publish($product_id)
{
//投稿を公開にする
wp_update_post(array('ID' => $product_id, 'post_status' => 'publish'));
//カスタムフィールドを空にする
update_post_meta($product_id, '_公開開始日', '');
}
投稿を公開にする処理 と カスタムフィールド「_公開開始日」を空にする処理が書かれている。
大変だったこと
今回少し手間取ったのが、WooCommerceのアクションフックの利用だ。
WooCommerceのアクションフックの文献が英語ばっかりで調べるの大変。しかも書いてる内容がすげえざっくりしてることも多い。日本人でWooCommerceがっつり弄っている人少ない!だから日本語文献少ない!いやんなっちゃった。
が、WooCommerceは機能が膨大な分、WPのアクションフックだけの利用では正常に動かないことが多い。
WordPressサイト制作者がWooCommerceでつまづくポイントはここだと思う。
上記機能利用する際の注意
カスタムフィールド「_公開開始日」「_公開終了日」に過去日付を入れてはならない。入れたら更新した途端に処理が走ってしまうので、公開する予定じゃなかった時間に公開にされてしまう可能性が出るので、気をつけたし。
プラグイン入れればすぐできるかもだけど、WooCommerce関連プラグインは有料が多い
今回作った機能、実際はプラグインとして存在はしているのだ。しかし、まともに使えるのは有料プラグインばっかり。そして制約多し。
できるだけ、自分の知識をフル活用して自作するのが、良いかもしれない。それで良いものができたら、日本人向けのプラグイン化して、汎用性高く、かつ日本ユーザーに優しいものが作れたらベストだ。
次回予告
次回は、今回の機能実装を応用して、
WooCommerceでタイムセールを実施できる機能の自作方法を紹介しようと思う。
乞うご期待。