WordPressで定員と申込期限のあるフォームを作る

WordPressで、定員と申込期限のあるフォームを作る方法を紹介します。

機能要件

  • 架空の料理教室イベントの申込フォーム
  • 定員に達したイベントには申し込めない
  • 申込期限が過ぎたイベントには申し込めない
  • 残席を表示する

画面イメージ

空席あり
残席僅か
満席
申込期限終了

デモページ

実装

イベント情報の登録

まず、「イベント」のカスタム投稿タイプを作成して、「定員」と「申込期限」のカスタムフィールドを用意します。今回はCustom Post Type UIとAdvanced Custom Fieldsを使用しました。

申込フォームの作成と申込データの保存

次に、Contact Form 7で申込フォームを作ります。
イベントの選択肢は申込可能なものに絞る必要があるため、管理画面では登録せず、後ほどフックを使って表示します。


申込データを保存するために「イベント申込データ」のカスタム投稿タイプを作成して、「イベント」のカスタムフィールドを用意します。
フロントエンドでは使わないので、publicly_queryableとshow_in_restはfalseにしましょう。

フォームが送信されたら、wpcf7_submitフックを使ってデータを保存します。

function my_wpcf7_register_applied_data() {
  $event_id = '';
  if ( !empty($_POST['event']) && !is_array($_POST['event']) && is_numeric($_POST['event']) ) {
    $event_id = sanitize_text_field($_POST['event']);
  }
  if ( $event_id ) {
    $is_available_event = my_is_available_event($event_id);
    if ( $is_available_event ) {
      $args = array(
        'post_type' => 'event-demo-applied',
        'post_status' => 'publish',
        'post_title' => 'イベント申込データ',
        'post_author' => 1,
      );
      $id = wp_insert_post($args, TRUE);
      if ( !is_wp_error($id) ) {
        update_field('event', $event_id, $id);
      }
    }
  }
}
add_filter('wpcf7_submit', 'my_wpcf7_register_applied_data', 11);
// 申込可能なイベントかどうか判定
function my_is_available_event( $event_id ) {
  $is_available_event = FALSE;
  
  do {
    if ( empty($event_id) ) break;
    
    $event = get_post($event_id);
    if ( empty($event) ) break;
    
    $deadline = get_field('deadline', $event_id);
    if ( $deadline < wp_date('Ymd') ) break;
    
    $available_seats_num = my_get_available_seats_num($event_id);
    if ( $available_seats_num <= 0 ) break;
    
    $is_available_event = TRUE;
  }
  while ( FALSE );
  
  return $is_available_event;
}
// イベントの残席数を取得
function my_get_available_seats_num( $event_id ) {
  $available_seats_num = 0;
  
  do {
    $capacity = get_field('capacity', $event_id);
    if ( empty($capacity) ) break;
    
    $args = array(
      'post_type' => 'event-demo-applied',
      'posts_per_page' => -1,
      'meta_query' => array(
        array(
          'key' => 'event',
          'value' => $event_id,
          'type' => 'CHAR',
          'compare' => '=',
        ),
      ),
    );
    $applied_data = get_posts($args);
    $available_seats_num = $capacity - count($applied_data);
  }
  while ( FALSE );
  
  return $available_seats_num;
}

wpcf7_form_tagフックを使って、フォームにイベントの選択肢を追加します。GETパラメータを使って初期値をセットするようにしておきましょう。

function my_wpcf7_custom_form_tag( $tag ) {
  do {
    if ( empty($tag) ) break;
    if ( $tag['name'] != 'event' ) break;
    
    $available_events = my_get_available_events();
    if ( empty($available_events) ) break;
    
    $url_param = '';
    if ( !empty($_GET['event_id']) && !is_array($_GET['event_id']) && is_numeric($_GET['event_id']) ) {
      $url_param = sanitize_text_field($_GET['event_id']);
    }
    
    $i = 1;
    foreach ( $available_events as $available_event ) {
       $tag['values'][] = $available_event->ID;
       $tag['labels'][] = $available_event->post_title;
       if ( $available_event->ID == $url_param ) {
         $tag['options'][] = 'default:' . $i;
       }
       $i++;
    }
  }
  while ( FALSE );
  
  return $tag;
}
add_filter('wpcf7_form_tag', 'my_wpcf7_custom_form_tag', 10);
// 申込可能なイベントを取得
function my_get_available_events() {
  $available_events = array();
  
  do {
    $args = array(
      'post_type' => 'event-demo',
      'posts_per_page' => -1,
      'meta_query' => array(
        array(
          'key' => 'deadline',
          'value' => wp_date('Ymd'),
          'type' => 'DATE',
          'compare' => '>=',
        ),
      ),
    );
    $events = get_posts($args);
    if ( empty($events) ) break;
    
    foreach ( $events as $event ) {
      $available_seats_num = my_get_available_seats_num($event->ID);
      if ( 0 < $available_seats_num ) {
        $available_events[] = $event;
      }
    }
  }
  while ( FALSE );
  
  return $available_events;
}

個別ページのテンプレート作成

残席と申込ボタンは、状況に応じて表示内容を切り替えます。

状況残席申込ボタン
空席あり
残席僅か△(あとx名!)
満席××
申込期限終了非表示×
<div class="p-e-demo__columns">
  <div class="p-e-demo__column p-e-demo__column--s">
<?php
  if ( has_post_thumbnail() ) {
    the_post_thumbnail();
  }
?>
  </div>
  <div class="p-e-demo__column p-e-demo__column--l">
<?php
  $deadline = get_field('deadline');
?>
    <div class="p-e-demo__row">
      <div class="p-e-demo__ttl">申込期限</div>
      <div class="p-e-demo__cont"><?php echo $deadline; ?></div>
    </div>
    <div class="p-e-demo__row">
      <div class="p-e-demo__ttl">定員</div>
      <div class="p-e-demo__cont"><?php the_field('capacity'); ?>名</div>
    </div>
<?php
  $event_id = get_queried_object_id();
  $available_seats_num = my_get_available_seats_num($event_id);
  if ( 3 < $available_seats_num ) {
    $available_seats_output = '◯';
  } elseif ( 0 < $available_seats_num ) {
    $available_seats_output = '△(あと' . $available_seats_num . '名!)';
  } else {
    $available_seats_output = '×';
  }
?>
<?php
  if ( wp_date('Ymd') <= $deadline ) :
?>
    <div class="p-e-demo__row">
      <div class="p-e-demo__ttl">残席</div>
      <div class="p-e-demo__cont"><?php echo $available_seats_output; ?></div>
    </div>
<?php
  endif;
?>
  </div>
</div>
<?php
  $is_available_event = my_is_available_event($event_id);
  if ( $is_available_event ) :
?>
<div class="p-e-demo__btn">
  <a class="c-btn c-btn--black" href="<?php echo esc_url(add_query_arg('event_id', $event_id, get_permalink(get_page_by_path('event-form-demo')->ID))); ?>">申込む</a>
</div>
<?php
  else :
?>
<div class="p-e-demo__btn">
  <span class="c-btn c-btn--black c-btn--disabled">申込みできません</span>
</div>
<?php
  endif;
?>

バリデーションの追加

最後に、複数のユーザーが同時にフォームにアクセスしている場合、定員を超えないように申込を制御する必要があります。
wpcf7_validateフックを使ってフォームにバリデーションを追加しましょう。

function my_wpcf7_custom_validate( $result, $tags ) {
  foreach ( $tags as $tag ) {
    if ( $tag['name'] == 'event' ) {
      $event_id = '';
      if ( !empty($_POST['event']) && !is_array($_POST['event']) && is_numeric($_POST['event']) ) {
        $event_id = sanitize_text_field($_POST['event']);
      }
      if ( $event_id ) {
        $is_available_event = my_is_available_event($event_id);
        if ( !$is_available_event ) {
          $result->invalidate($tag['name'], 'このイベントには申込できません');
        }
      }
    }
  }
  return $result;
}
add_filter('wpcf7_validate', 'my_wpcf7_custom_validate', 11, 2);

以上で実装完了です。

最後に

今回は、セキュリティの観点から名前とメールアドレスは保存しませんでした。これらの個人情報を扱う場合は、REST APIを含めて外部からアクセスできないよう細心の注意を払いましょう。

コメント