Selling Events as Products in WordPress - Without a Plugin!
There's a reason tailors are still around
Recently, a client wanted to use WooCommerce to sell training events alongside the powerful blogging features of a WordPress installation, without affecting their existing use of WooCommerce to sell merchandise. While there are many plugins available to handle selling events, the client found they either had too much bloat, or were not designed quite as they wished, and had spent literally hours looking through the plugin repository. Luckily, we are wizard-like WordPress developers!
Client Requirements
- The ability to filter events by type / category and date
- Be able to add events individually as separate products.
- Have a shortcode or widget that can show upcoming events as a list, sorted by date
- Be able to display a calendar with upcoming events, again by widget or shortcode
- To be able to use a default product description prior to editing.

Creating an event WooCommerce product
The first step was to create a custom product type, with a price, event date, and event category. This is a simple step we would recommend for any eCommerce shop that sells multiple types of items, or items that require a fair amount of customisation. Later we would go on to add extra information such as the location of the training, the exact time, the trainer's name, and so on.

As with any product in WooCommerce, these products can also be put into categories, meaning the client was able to easily create category pages selling events along with the appropriate apparatus and merchandise. That means we have already knocked off the first two requirements!
Creating the list of events
The client was already using Elementor to build their pages, so we would build them a shortcode that could also be used as a widget in Elementor, with all the customisation that comes with it.
The first step was building a query to retrieve an array of event products from WooCommerce, but only those that aren't scheduled before today's date. WooCommerce wc_get_products() is perfect for this:
$today = date('Y-m-d');
$args = array(
'type' => 'event',
'category' => $cat_array,
'limit' => $limit,
'order' => 'ASC',
'orderby' => 'meta_value',
'meta_key' => 'tbd_date',
'tbd_date' => $today
);
$products = wc_get_products( $args );
PHP
For each event in any categories in $cat_array (which is supplied by the shortcode attributes), those newer than $today will be held in our $products variable, making it a cinch to create a list item (or grid row) for each. We also added a category class to each element so we could style individual event categories differently. By adding classes to every element, we make sure that when we create our Elementor Widget we have full control over each element without having to write custom CSS.
Now that we have a working shortcode, our Elementor Widget really only has to do_shortcode('eventslist'), and we can add attributes and styling through the Elementor interface without hassle. We can provide default styling in the theme, so that Elementor can then override our defaults whenever individual pages call for it.

What about the calendar?
Not much to say here! Adding the items to the calendar was fairly simple using the FullCalendar JavaScript library, and as each element also has a relevant HTML class, it was easy to hook up with Elementor as its own separate widget. The only problem is that as it is by default a table view, it was not particularly responsive. There were two easy ways around this, and one more difficult one. Instead of rewriting the library, we opted to include the list of items wherever the calendar is displayed, and hide one or the other depending on viewport size. Both easy options actually use this method, one through Elementor's own interface, or one by our bespoke responsive CSS.
Default product descriptions per category
We did foolishly try to insert default product descriptions upon creation, but as the website offers multiple types of products, this was not at all ideal. The much easier solution was to use WordPress's admin AJAX and a little bit of jQuery to add buttons on the General tab for Event products, and then the author can insert default copy which is set on the TBD events admin options page.

Afterthoughts
If the wheel they're selling you isn't round enough, maybe it is time to reinvent it. Once adding in the order meta data to show the dates, times, trainers (etc.) on the emails and basket/checkout pages, the event products were working perfectly and had a significant positive impact on the conversion rate. There is scope here to integrate the bookings with user calendars which may help the client retain customers, as well as to introduce a recurring event product once they have scaled the business up a bit.
If you are interested in custom work for your WordPress environment, get in touch or learn more about our WordPress development options