Skip to content

Instantly share code, notes, and snippets.

@createit-dev
Last active November 17, 2023 11:43
Show Gist options
  • Save createit-dev/57eea735022f0809551d6a14db873f23 to your computer and use it in GitHub Desktop.
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
<?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