Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ajlowndes/7b7ba278815051c9d5a9b3b75e5d7dc9 to your computer and use it in GitHub Desktop.

Select an option

Save ajlowndes/7b7ba278815051c9d5a9b3b75e5d7dc9 to your computer and use it in GitHub Desktop.
<?php
/*
Plugin Name: WooCommerce Schema.org Course Data
Description: Adds schema.org course and courseInstance data to WooCommerce products
Version: 1.3
Author: Aaron Lowndes
*/
if (!defined('ABSPATH')) exit; // Exit if accessed directly
class WC_Schema_Course_Data {
public function __construct() {
add_action('woocommerce_after_single_product', array($this, 'output_schema_data'));
}
public function output_schema_data() {
global $product;
if (!$this->is_course($product)) return;
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'Course',
'name' => $product->get_name(),
'description' => $product->get_short_description(),
'provider' => array(
'@type' => 'Organization',
'name' => 'Melbourne Climbing School',
'url' => home_url()
),
'offers' => $this->get_course_offers($product),
'hasCourseInstance' => $this->get_course_instances($product)
);
echo '<script type="application/ld+json">' . json_encode($schema) . '</script>';
}
private function is_course($product) {
return $product->get_type() == 'registrations';
}
private function get_course_offers($product) {
return array(
'@type' => 'Offer',
'price' => $product->get_price(),
'priceCurrency' => get_woocommerce_currency(),
'availability' => $product->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
'url' => get_permalink($product->get_id()),
'category' => $this->get_product_category($product),
'hasMerchantReturnPolicy' => array(
'@type' => 'MerchantReturnPolicy',
'applicableCountry' => 'AU',
'returnPolicyCategory' => 'https://schema.org/MerchantReturnFiniteReturnWindow',
'returnWindow' => array(
'@type' => 'MonetaryAmount',
'value' => '7',
'unitCode' => 'DAY'
),
'refundType' => 'https://schema.org/PartialRefund',
'returnMethod' => 'https://schema.org/ReturnAtKiosk'
)
);
}
private function get_product_category($product) {
$categories = get_the_terms($product->get_id(), 'product_cat');
if ($categories && !is_wp_error($categories)) {
return $categories[0]->name;
}
return 'Climbing Course'; // Default category if none is set
}
private function get_course_instances($product) {
$instances = array();
$variations = $product->get_children();
foreach ($variations as $variation_id) {
$variation = wc_get_product($variation_id);
$variation_data = $variation->get_data();
$instance = array(
'@type' => 'CourseInstance',
'name' => $variation->get_name(),
'url' => $variation->get_permalink(),
'sku' => $variation->get_sku(),
'description' => $variation->get_description(),
'courseMode' => ['onsite'],
'eventAttendanceMode' => 'https://schema.org/OfflineEventAttendanceMode',
'courseWorkload' => 'PT8H', // Assumes 8 hours per day, modify as needed
'offers' => array(
'@type' => 'Offer',
'price' => $variation->get_price(),
'priceCurrency' => get_woocommerce_currency(),
'availability' => $variation->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
'url' => $variation->get_permalink(),
'category' => $this->get_product_category($product)
)
);
// Extract dates from attributes
$attributes = $variation->get_attributes();
if (isset($attributes['dates'])) {
$dates_json = $attributes['dates'];
$dates_data = json_decode($dates_json, true);
if ($dates_data && isset($dates_data['dates']) && is_array($dates_data['dates']) && !empty($dates_data['dates'])) {
$start_date = $dates_data['dates'][0]; // First date in the array
$end_date = end($dates_data['dates']); // Last date in the array
// Get start and end times from meta data
$start_time = $variation->get_meta('_event_start_time', true) ?: '09:00';
$end_time = $variation->get_meta('_event_end_time', true) ?: '17:00';
// Combine dates and times
$instance['startDate'] = date('c', strtotime("$start_date $start_time"));
$instance['endDate'] = date('c', strtotime("$end_date $end_time"));
}
}
// Add course schedule based on SKU
if ($variation->get_sku() === 'ltl') {
$instance['courseSchedule'] = '6:30 PM to 9:30 PM each day';
} else {
$instance['courseSchedule'] = '9:00 AM to 5:00 PM each day';
}
// Add image
$image_id = $variation->get_image_id();
$image_array = wp_get_attachment_image_src($image_id, array('500','500'), true);
if ($image_array) {
$instance['image'] = $image_array[0];
}
// Add location
$location_name = $variation->get_meta('location_name', true);
if ($location_name) {
$instance['location'] = array(
'@type' => 'Place',
'name' => $location_name
);
}
$instances[] = $instance;
}
return $instances;
}
private function format_datetime($datetime) {
// Convert to ISO 8601 format
return date('c', strtotime($datetime));
}
}
new WC_Schema_Course_Data();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment