I built an online booking form using WPForms that allowed customers to select from a list of available days/times.
There’s a few components you need:
- Client editable list of available days/times
- List of days already booked / manually added “unavailable” times
- Dynamically generate a list of available times by comparing the two.
I added a settings page to the form for specifying general availability (ex: Monday at 7:30am) and to edit a list of unavailable day/times (ex: Monday, January 28 at 7:30am). When the form is submitted, the selected date is automatically added to the unavailable day/times.

We’re using the wpforms_field_data
filter to update a dropdown field with the available options. We identify the correct select field by looking for a custom CSS class of dynamic-booking-times
.
We use the list of general availability to build a list of the actual days/times available from now until 60 days out. We then remove any day/times that are already booked.
<?php
/**
* Core Functionality Plugin
*
* @package CoreFunctionality
* @since 1.0.0
* @copyright Copyright (c) 2014, Bill Erickson & Jared Atchison
* @license GPL-2.0+
*/
function ea_booking_field_dropdown( $field, $form_data ) {
if( 'select' !== $field['type'] )
return $field;
$classes = explode( ' ', $form_data['fields'][ $field['id'] ]['css'] );
if( ! in_array( 'dynamic-booking-times', $classes ) )
return $field;
$field['choices'] = ea_get_available_booking_times( $form_data );
return $field;
}
add_filter( 'wpforms_field_data', 'ea_booking_field_dropdown', 20, 2 );
/**
* Get Available Booking Times
*
*/
function ea_get_available_booking_times( $form_data = array() ) {
$start = strtotime( $form_data['settings']['mct_book_before'], current_time( 'timestamp' ) );
$end = strtotime( $form_data['settings']['mct_book_after'], current_time( 'timestamp' ) );
$availability = explode( PHP_EOL, $form_data['settings']['mct_general_availability'] );
$days = array();
foreach( $availability as $option ) {
$option = explode( ' at ', $option );
$days[$option[0]][] = $option[1];
}
$options = array();
for( $current = $start; $current < $end; $current += DAY_IN_SECONDS ){
$day = date( 'l', $current );
if( isset( $days[$day] ) ) {
foreach( $days[$day] as $time ) {
$daytime = strtotime( date( 'l, F j, Y', $current ) . ' ' . $time );
if( $daytime < $end && $daytime > $start )
$options[] = array( 'label' => date( 'l, F j, Y', $daytime ) . ' at ' . date( 'g:ia', $daytime ), 'value' => $daytime );
}
}
}
$booked = explode( PHP_EOL, $form_data['settings']['mct_booked_times'] );
if( !empty( $booked ) ) {
foreach( $booked as $booked_time ) {
$booked_time = strtotime( str_replace( ' at ', ' ', $booked_time ) );
foreach( $options as $i => $option ) {
if( $option['value'] == $booked_time ) {
unset( $options[$i] );
}
}
}
}
return $options;
}
/**
* Update list of booked times on form completion
*
*/
function ea_update_list_of_booked_times( $fields, $entry, $form_data, $entry_id ) {
$booked = explode( PHP_EOL, $form_data['settings']['mct_booked_times'] );
$booked[] = $fields[2]['value'];
$form_data['settings']['mct_booked_times'] = implode( PHP_EOL, array_filter( $booked ) );
wpforms()->form->update( EA_BOOKING_FORM_ID, $form_data );
}
add_action( 'wpforms_process_complete_' . EA_BOOKING_FORM_ID, 'ea_update_list_of_booked_times', 10, 4 );
/**
* Delete past bookings
*
*/
function ea_delete_past_bookings() {
$last_updated = get_option( 'mct_updated_bookings_list' );
if( ! $last_updated || $last_updated < strtotime( '-24 hours' ) ) {
$form = wpforms()->form->get( EA_BOOKING_FORM_ID );
$form_data = wpforms_decode( $form->post_content );
$booked = explode( PHP_EOL, $form_data['settings']['mct_booked_times'] );
foreach( $booked as $i => $datetime ) {
if( !empty( $datetime ) ) {
$datetime = str_replace( ' at ', ' ', $datetime );
if( strtotime( $datetime ) < current_time( 'timestamp' ) ) {
unset( $booked[$i] );
}
}
}
$form_data['settings']['mct_booked_times'] = implode( PHP_EOL, array_filter( $booked ) );
wpforms()->form->update( EA_BOOKING_FORM_ID, $form_data );
update_option( 'mct_updated_bookings_list', time() );
}
}
add_action( 'shutdown', 'ea_delete_past_bookings' );