Last active
November 17, 2023 11:43
-
-
Save createit-dev/57eea735022f0809551d6a14db873f23 to your computer and use it in GitHub Desktop.
"Live Visitor Count" feature using Server-Sent Events (SSE) in a WooCommerce
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* "Live Visitor Count" feature using Server-Sent Events (SSE) in a WooCommerce | |
* Display a live count of how many users are currently viewing a particular product. | |
*/ | |
add_action('rest_api_init', 'register_api_routes'); | |
function register_api_routes() | |
{ | |
register_rest_route('eshop/v1', '/ct_visit_counter', array( | |
'methods' => 'GET', | |
'callback' => 'send_visitor_count', | |
'args' => array( | |
'product_id' => array( | |
'required' => true | |
) | |
), | |
'permission_callback' => '__return_true', | |
)); | |
} | |
function send_visitor_count(WP_REST_Request $request) | |
{ | |
header('Content-Type: text/event-stream'); | |
header('Cache-Control: no-cache'); | |
header('Connection: keep-alive'); | |
header('X-Accel-Buffering: no'); | |
$lastEventId = 0; | |
$product_id = $request->get_param('product_id'); | |
if (!$product_id) { | |
// Handle the case where product_id is not provided | |
echo "data: No product ID provided.\n\n"; | |
flush(); | |
return; | |
} | |
while (true) { | |
$visitor_count_key = 'visitor_count_product_' . $product_id; | |
wp_suspend_cache_addition(true); // Suspend caching additions | |
$visitor_count = get_transient($visitor_count_key) ?: 0; | |
wp_suspend_cache_addition(false); // Resume caching additions | |
echo "id: " . $lastEventId . "\n"; | |
echo "event: visitCount\n"; | |
echo 'data: ' . json_encode(['total' => $visitor_count]) . "\n\n"; | |
flush(); | |
sleep(5); | |
} | |
} | |
add_action('template_redirect', 'update_product_visitor_count'); | |
function update_product_visitor_count() | |
{ | |
if (is_product()) { | |
global $post; | |
// Ensure $post is not null and is a WP_Post object | |
if ($post && $post instanceof WP_Post) { | |
$product_id = $post->ID; // Directly get the ID from the global $post object | |
$visitor_count_key = 'visitor_count_product_' . $product_id; | |
$current_count = get_transient($visitor_count_key) ?: "0"; | |
set_transient($visitor_count_key, $current_count + 1, 10 * MINUTE_IN_SECONDS); // 10 minutes active threshold | |
} | |
} | |
} | |
function add_product_id_to_body_class($classes) | |
{ | |
if (is_product()) { | |
global $product; | |
if ($product instanceof WC_Product) { | |
$classes[] = 'product-id-' . $product->get_id(); | |
} | |
} | |
return $classes; | |
} | |
add_filter('body_class', 'add_product_id_to_body_class'); | |
function add_visitor_count_to_product_page() | |
{ ?> | |
<script type="text/javascript"> | |
var sseConnectionActive = false; | |
function setupLiveVisitorCount() { | |
if (sseConnectionActive) { | |
console.log("SSE already running."); | |
return; | |
} | |
if (typeof (EventSource) == "undefined") { | |
console.log("Error: Server-Sent Events are not supported in your browser"); | |
return; | |
} | |
if (!!window.EventSource && jQuery('.product').length) { | |
var productId = null; | |
var bodyClass = jQuery('body').attr('class').split(/\s+/); | |
jQuery.each(bodyClass, function (index, item) { | |
if (item.indexOf('product-id-') !== -1) { | |
productId = item.split('-')[2]; | |
} | |
}); | |
if (productId) { | |
var source = new EventSource('/wp-json/eshop/v1/ct_visit_counter?product_id=' + productId); | |
source.addEventListener('visitCount', function (event) { | |
var data = JSON.parse(event.data); | |
document.getElementById('visitorCount').innerText = data.total; | |
}, false); | |
source.addEventListener('error', function (event) { | |
console.log('SSE error:', event); | |
if (event.target.readyState === EventSource.CLOSED) { | |
console.log('SSE closed (' + event.target.readyState + ')'); | |
sseConnectionActive = false; | |
} else if (event.target.readyState === EventSource.CONNECTING) { | |
console.log('SSE reconnecting (' + event.target.readyState + ')'); | |
} | |
}, false); | |
// Ensure that if the browser tab is closed, the SSE connection is also closed. | |
window.onbeforeunload = function () { | |
source.close(); | |
sseConnectionActive = false; | |
}; | |
} | |
} else { | |
console.log("Your browser does not support Server-Sent Events"); | |
} | |
} | |
// Call the function when the DOM is ready | |
jQuery(document).ready(function () { | |
setupLiveVisitorCount(); | |
}); | |
</script> | |
<style> | |
#visitorCountWrapper { | |
margin: 10px 0; | |
padding: 10px; | |
border: 5px solid orange; | |
max-width: 300px; | |
} | |
</style> | |
<?php | |
echo '<div id="visitorCountWrapper">Live Visitor Count: <span id="visitorCount">Loading...</span></div>'; | |
} | |
add_action('woocommerce_before_add_to_cart_form', 'add_visitor_count_to_product_page', 15); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment