Ah Woocommerce Bookings from Woothemes, how long did we wait for a really good bookings extension for Woocommerce, and when it finally came it didn’t disappoint - robust, stable, versatile and of course tying in with all the other great Woothemes extensions.

But alas, there is no way that any WordPress plugin developer could ever encapsulate every use-case from the thousands (is it millions now?) of e-commerce stores powered by Woocommerce, and so when the plugin came out so did hundreds of requests for slight tweaks or customisations to its core functionality.

Thankfully as with every Woothemes plugin there are plenty of hooks to play with and everything else is incorporated in the 100% WP way making it very easy to extend without having to ever touch a core file. I will cover o

DatePicker to Available Dates Dropdown

The date/time picker you get straight out of the box with the Woocommerce Bookings plugin is great, but for some stores having a list of available dates in a dropown is a more user-friendly interface. Here is how you can do it:

As I will be sharing this code in a Gist link at the end of the article I better be honest. While the first main objective in this plugin is to change out the datepicker field for a dropown, the first line of code is actually $wswp_dates_built = false; - this sets a global variable (Google PHP variable scope if you don’t know what I mean by global) to record if we have built the options for the dropdown. If I remember correctly this prevented getting multiple sets of the same dates in the dropdown, because the actual date-picker field is split into two/three actual fields in the bookings form.

So… on to the first objective - turning our date-picker field from type=date-picker to a dropown and complete with options in the form of available dates.

As I said the date-picker is actually split accross multiple fields, clicking on any of them sprouts the picker, and the values of all of them go into the cost calculations. A dropdown is only 1 field, so what we need to do is keep the original fields, hide them and show our new dropdown in their place. We can then take the selection from the dropdown and put it into the original fields on the fly to give Woo Bookings what it needs to give a price and allow a purchase of the given block(s). Thankfully we can do all of that using only one wonderful filter.


add_filter('booking_form_fields','wswp_booking_form_fields');

function wswp_booking_form_fields($fields) {
    global $wswp_dates_built;
    $i = 0;
    foreach($fields as $field) {
        $new_fields[$i] = $field;
        if ($field['type'] == "date-picker" && $wswp_dates_built === false)
        {
            $s = $i;
            $new_fields[$s]['class'] = array('picker-hidden');
            $i++;
            $new_fields[$i] = $field;
            $new_fields[$i]['type'] = "select";
            $new_fields[$i]['options'] = wswp_build_options($field['availability_rules'][0]);
            $new_fields[$i]['class'] = array('picker-chooser');
        }
        $i++;
    }
    return $new_fields;
}

So… first we bring in the global $wswp_dates_built as we need it here. Then (as the original array is associative) we initiate a numeric counting variable that we can use to keep track of the array items as we make changes to its values.

Then we go into the actual array of form fields passed into the filter as $fields and build a new array of fields to pass back out - $new_fields. Firstly as we are keeping all fields we store every field into the $new_fields array. We check if it is a date-picker field and if we haven’t built the dates yet we go into action. We add the class “picker-hidden” to the original field stored in the array at the current value of $i, and then we add 1 to $i with $i++ to add a clone of that same field into the array at the new value of $i. We then make the modifications to that field to turn it into a dropdown.

We change the type to “select”, add the class picker-chooser for targetting it within the DOM (Document Object Model), and we also add an item ‘options’ to the field, in here we store our dates, which come from a function and make up the options for the dropdown -- I will show you that function next. The rest of that code simply iterates on $i and repeats the cycle for the remaining fields and returns $new_fields as the set of fields now making up your bookings form.

So now the function that is the hingepin of this code wswp_built_options, into which we have passed the availability rules for the original picker field. In case you are wondering the availability rules is a very complex array of rules created from the settings used when creating the bookable product. In our function we parse into that array to get the available dates.


function wswp_build_options($rules,$building = false) {
    global $wswp_dates_built;
    $dates = array();
    foreach($rules as $dateset) {
        if ($dateset[0] == "custom") {
            $year = reset(array_keys($dateset[1]));
            $month = reset(array_keys($dateset[1][$year]));
            $day = reset(array_keys($dateset[1][$year][$month]));
            $dtime = strtotime($year."-".$month."-".$day);
            $dates[$dtime] = date("d/m/Y",$dtime);
        }

    }
    ksort($dates);
    foreach($dates as $key => $date) {
        $dates[date("Y-m-d",$key)] = $date;
        unset($dates[$key]);
    }
    $wswp_dates_built = true;
    return $dates;
}

I won’t bore you with a line by line on that function because I would then have to go into the structure of the array. For those that want to know more Google the functions, ask in the comments, and play around with print_r on the $rules variable.

Now finally we just have to move our dropdown to the correct place in the DOM and attach an event handler to the change event on the dropdown so that when a date is chosen it populates the original picker fields with the needed values. As it is a fairly short piece of javascript I decided to simply place it in the footer using the wp_footer hook:


add_action('wp_footer','wswp_css_js');

function wswp_css_js() {
    //adding in footer as not enough to justify new stylesheet and js file
    ?><style type="text/css">
        .picker-hidden .picker,.picker-hidden legend {
            display:none;
        }
        </style>
        <script type='text/javascript'>
            jQuery(function($) {
//Move .picker chooser up in the DOM
                $(".picker-chooser").insertBefore('.wc-bookings-date-picker-date-fields');
//Attach handler to insert chosen date into picker fields
                $("select#wc_bookings_field_start_date").on('change', function() {
                var selectedDate = $(this).val()
                var selectedDateBreakdown = selectedDate.split("-");

                $( "input[name*='wc_bookings_field_start_date_year']" ).val( selectedDateBreakdown[0] );
                $( "input[name*='wc_bookings_field_start_date_month']" ).val( selectedDateBreakdown[1] );
                $( "input[name*='wc_bookings_field_start_date_day']" ).val( selectedDateBreakdown[2] );
            });
            });

            </script>
            <?php
}

And voila - now load up the page of any bookable product and where you once saw the picker fields you will now see a nice dropdown.

As promised here is the Gist for this: https://gist.github.com/LiamBailey/417e8bf233c7cd7c0fb8 - and if you use Resources i.e. to have different locations for courses etc, I made an update to this script to handle that you can find that here: https://gist.github.com/LiamBailey/a33dcf9fedac2229fcdb.

Note: To anyone expecting the next post from me to be a guide on importing Yahoo Miva (store platform) products from its xlsx export files into Woocommerce using WP All Import, there have been some problems with that and if I can resolve them it will be covered at a later date.