WordPressを使った会社サイトで、キャンペーン情報などを投稿しているとき、キャンペーン期間が終了したら自動的に投稿を削除したいことってありますよね?
プラグインでできる作業ではありますが、何かしら制限で導入できない場合は、自分でPHPで実装する必要があります。
ChatGPTに確認しながらコードを実装したので備忘録として残しておきます。
もし、同じ状況にある人でPHPで実装する必要がある場合は参考になると嬉しいです。
できること
- 投稿の編集画面に「キャンペーン終了日」を入力する欄が出ます。
- その日時になると自動で非公開(private)に切り替わります。
- ついでにタイトルの末尾へ「(YYYYMMDD非公開)」を一度だけ追記します。
例)多言語化について(20251227非公開)

前提と注意
テーマの子テーマの functions.php
に追記して使います
もし、親テーマに直書きする場合はアップデートで消えます。
特に理由がなければ子テーマで実装してください。
WordPressのタイムゾーンは「設定 → 一般」で正しく設定(日本なら東京)。
WP-Cronを使用。
これはあらかじめ予定していたイベントを「誰かがサイトへアクセスしたとき」に動かす仕組み。
確実にしたいならサーバーのcronで wp-cron.php
を定期実行する必要があるそうです。
キャッシュ系プラグイン(WP Super Cache)を使っていたのですが、一覧に古い表示が残ることがありました。
設定で記事の更新のタイミングでキャッシュ削除を行うように変更しました。

完成コード(コピペOK・子テーマの functions.php
に貼る)
/**
* キャンペーン投稿の自動非公開化 & タイトル自動追記
*
* 概要:
* - 投稿編集画面に「キャンペーン終了日(datetime)」メタボックスを追加します。
* - 保存時に終了日時を投稿メタへ保存し、WP-Cron の単発イベントをスケジュールします。
* - 終了日時に達したら自動的に該当投稿を「非公開 (private)」へ変更し、
* タイトル末尾に「(YYYYMMDD非公開)」を一度だけ付与します(重複追記を防止)。
* - 終了日時が過去の場合は保存直後に即時処理されます。
*
* 主なフック:
* - add_meta_boxes: 終了日時入力UI(メタボックス)を追加
* - save_post: 終了日時の保存/WP-Cron 単発ジョブの登録・置き換え
* - campaign_expire_post(カスタムフック): 期限到来時の非公開化&タイトル更新を実行
*
* 使い方:
* 1) 本コードをテーマの functions.php またはサイト専用プラグインに配置します。
* 2) 投稿編集画面のサイドバーに出る「キャンペーン終了日」を設定して更新します。
* 3) 期限に達すると自動で非公開化され、タイトル末尾に日付付きの注記が追加されます。
*
* 注意事項:
* - サイトのタイムゾーン(一般設定)に基づいて処理します。日本時間ならそのまま入力でOK。
* - WP-Cron を無効化している環境では、OSの cron から wp-cron.php を定期実行してください。
* - 対象は post(投稿)のみ。必要に応じて post_type 条件を調整してください。
*/
// 終了日時メタボックスを追加
add_action('add_meta_boxes', function () {
add_meta_box('campaign_expiry', 'キャンペーン終了日', function ($post) {
$ts = (int) get_post_meta($post->ID, 'campaign_expiry', true);
$val = $ts ? wp_date('Y-m-d\TH:i', $ts, wp_timezone()) : '';
wp_nonce_field('save_campaign_expiry', 'campaign_expiry_nonce');
echo '<p><label for="campaign_expiry_input">終了日時</label><br>';
echo '<input type="datetime-local" id="campaign_expiry_input" name="campaign_expiry" value="' . esc_attr($val) . '" /></p>';
echo '<p><small>この日時を過ぎると自動で<strong>非公開</strong>になります。</small></p>';
}, 'post', 'side', 'default');
});
add_action('save_post', function ($post_id, $post, $update) {
if (!isset($_POST['campaign_expiry_nonce']) || !wp_verify_nonce($_POST['campaign_expiry_nonce'], 'save_campaign_expiry')) return;
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if ($post->post_type !== 'post') return;
// 既存ジョブを一旦解除
if ($ts = wp_next_scheduled('campaign_expire_post', [$post_id])) {
wp_unschedule_event($ts, 'campaign_expire_post', [$post_id]);
}
// 入力 → サイトのタイムゾーンでタイムスタンプ化
$timestamp = 0;
if (!empty($_POST['campaign_expiry'])) {
$dt = date_create(wp_unslash($_POST['campaign_expiry']), wp_timezone());
if ($dt) $timestamp = $dt->getTimestamp();
}
if ($timestamp) {
update_post_meta($post_id, 'campaign_expiry', $timestamp);
if ($timestamp > time()) {
wp_schedule_single_event($timestamp, 'campaign_expire_post', [$post_id]); // 期限に実行
} else {
// すでに過去なら即時で非公開処理
campaign_expire_post_callback($post_id);
}
} else {
delete_post_meta($post_id, 'campaign_expiry');
}
}, 10, 3);
add_action('campaign_expire_post', 'campaign_expire_post_callback');
function campaign_expire_post_callback($post_id) {
$expiry = (int) get_post_meta($post_id, 'campaign_expiry', true);
if (!$expiry || time() < $expiry) return;
$p = get_post($post_id);
if (!$p) return;
// 1) タイトル末尾に(YYYYMMDD非公開)を付与(重複防止)
$date_str = wp_date('Ymd', $expiry, wp_timezone());
$title = preg_replace('/(\d{8}[^)]*)\s*$/u', '', $p->post_title); // 既存の似た括弧表記を除去
$title .= '(' . $date_str . '非公開)';
// 2) 非公開化 & タイトル更新(スラッグはそのまま)
wp_update_post([
'ID' => $post_id,
'post_status' => 'private',
'post_title' => $title,
]);
}
使い方
- 子テーマの
functions.php
に上のコードを貼る - 投稿の編集画面 → 右側に「キャンペーン終了日」が出るので、日時を入力して更新
- 期限が来ると自動で非公開になり、タイトル末尾に日付が付く


どう動いているか
- 終了日を保存するたびに、「〇月〇日〇時になったらこの投稿を処理してね」という1回きりの予約を入れます。
- 時間が来ると、WordPressの「WP-Cron」がそれを見つけて非公開へ変更し、タイトルに日付を追記します。
- 予約を入れ直す時は、前の予約を必ずキャンセルしてから新しい予約を入れるので、二重実行になりません。
- 不正な保存(外部からの勝手送信)を防ぐために、nonce(1回限りの合言葉)をチェックしています。
ちょいカスタマイズ
- 非公開ではなく下書きに戻す:
'post_status' => 'draft'
に変更 - 固定ページやカスタム投稿にも使う:
add_meta_box()
とsave_post
の投稿タイプ条件を追加 - タイトル追記の表記を変える:
'(' . $date_str . '非公開)'
を好みの文言に
免責事項
自己責任でお願いします。
利用する場合も、まずは検証環境で試してから本番へどうぞ。
各自のサイト状況(プラグインやキャッシュ構成)で動作が変わることがあります。
まとめ
- functions.phpだけで期日で自動非公開は十分実現できました。
- 仕組みは「期日の単発予約 → 到達したら非公開+タイトル追記」。
- 実運用ではキャッシュとWP-Cronだけ意識しておけばOK。
サイト制作に関するご相談・お見積りなどお気軽にご相談ください。
新規サイト制作では、提案・制作から公開後の保守・運用・更新までトータルで、あなたのお力になります。
既存のサイトでも、簡易な修正や機能追加、更新のお手伝いやレッスンなど、幅広く対応させていただきます。
まずは、お気軽に今お持ちのお悩みをお聞かせください。
コメント