Simple WP-REST Powered SNN Like Button Shortcode for WordPress

Registers a Custom Post Type A hidden post type (snn_like) is created to store like counts.

Retrieves or Creates a Like Record When a like button is clicked, the system checks if a record for the given identifier exists.

If not, it creates one and initializes the like count to zero.

Handles Like Requests via REST API A custom REST API endpoint (snn/v1/like) is registered to process like

The SNN Like Button Shortcode generates a simple thumbs-up button that allows users to like a specific item (e.g., a post, product, or any unique entity).

By the way this will work fine for any wordpress theme or builder ..etc doesnt matter the system.


/**
 * SNN Like Button Shortcode
 *
 * This code registers a hidden custom post type for storing like counts,
 * sets up a REST API endpoint to handle AJAX like requests,
 * and defines a shortcode 
 * that outputs a thumbs‑up button.
 *
 * Place this code in your theme’s functions.php file.
 */

/* ============================================================================
   1. Register Custom Post Type for Storing Likes
============================================================================ */
add_action( 'init', 'snn_register_like_post_type' );
function snn_register_like_post_type() {
	$args = [
		'public'             => false,
		'show_ui'            => false,
		'capability_type'    => 'post',
		'supports'           => [ 'title' ],
	];
	register_post_type( 'snn_like', $args );
}

/* ============================================================================
   2. Helper Functions to Retrieve/Create a Like Record and Get the Count
============================================================================ */
/**
 * Retrieve (or create) the like record (post) for a given identifier.
 *
 * @param string $identifier Sanitized identifier.
 * @return WP_Post|false
 */
function snn_get_like_post( $identifier ) {
	// Try to find the post by its slug.
	$post = get_page_by_path( $identifier, OBJECT, 'snn_like' );
	if ( ! $post ) {
		// Create a new post if not found.
		$post_id = wp_insert_post( [
			'post_title'  => $identifier,
			'post_name'   => $identifier,
			'post_type'   => 'snn_like',
			'post_status' => 'private',
		] );
		if ( ! is_wp_error( $post_id ) ) {
			$post = get_post( $post_id );
			update_post_meta( $post->ID, '_snn_like_count', 0 );
			update_post_meta( $post->ID, '_snn_like_ips', [] );
		}
	}
	return $post;
}

/**
 * Get the current like count for a given identifier.
 *
 * @param string $identifier
 * @return int
 */
function snn_get_like_count( $identifier ) {
	$post  = snn_get_like_post( $identifier );
	$count = get_post_meta( $post->ID, '_snn_like_count', true );
	return $count ? intval( $count ) : 0;
}

/* ============================================================================
   3. REST API Endpoint to Handle Like Requests
============================================================================ */
add_action( 'rest_api_init', function() {
	register_rest_route( 'snn/v1', '/like', [
		'methods'             => 'POST',
		'callback'            => 'snn_handle_like',
		'permission_callback' => '__return_true',
	] );
} );

function snn_handle_like( WP_REST_Request $request ) {
	// Sanitize and validate the identifier.
	$identifier = $request->get_param( 'identifier' );
	$identifier = sanitize_key( $identifier );
	if ( empty( $identifier ) ) {
		return new WP_Error( 'no_identifier', 'No identifier provided', [ 'status' => 400 ] );
	}

	// Get the visitor’s IP address.
	$user_ip = $_SERVER['REMOTE_ADDR'];

	// Retrieve (or create) the like record.
	$post = snn_get_like_post( $identifier );
	if ( ! $post ) {
		return new WP_Error( 'post_error', 'Could not create like record', [ 'status' => 500 ] );
	}

	// Retrieve saved IPs for this like.
	$ips = get_post_meta( $post->ID, '_snn_like_ips', true );
	if ( ! is_array( $ips ) ) {
		$ips = [];
	}

	// If this IP already liked this identifier, do nothing.
	if ( in_array( $user_ip, $ips, true ) ) {
		return rest_ensure_response( [
			'count' => intval( get_post_meta( $post->ID, '_snn_like_count', true ) )
		] );
	}

	// Increment the like count.
	$count = intval( get_post_meta( $post->ID, '_snn_like_count', true ) );
	$count++;
	update_post_meta( $post->ID, '_snn_like_count', $count );

	// Save the IP.
	$ips[] = $user_ip;
	update_post_meta( $post->ID, '_snn_like_ips', $ips );

	return rest_ensure_response( [ 'count' => $count ] );
}

/* ============================================================================
   4. Shortcode to Output the Like Button
============================================================================ */
/**
 * Shortcode: 
 *
 * @param array $atts Shortcode attributes.
 * @return string HTML for the like button.
 */
function snn_like_button_shortcode( $atts ) {
	$atts = shortcode_atts( [
		'identifier' => '',
	], $atts, 'snn_like_button' );

	$identifier = sanitize_key( $atts['identifier'] );
	if ( empty( $identifier ) ) {
		return 'Identifier missing';
	}

	$like_count = snn_get_like_count( $identifier );
	$button     = '<button class="snn-like-button" data-identifier="' . esc_attr( $identifier ) . '" onclick="snn_likeButton(this)">đź‘Ť' . esc_html( $like_count ) . '</button>';
	return $button;
}
add_shortcode( 'snn_like_button', 'snn_like_button_shortcode' );

/* ============================================================================
   5. Inline JavaScript for AJAX Handling (No External JS File)
============================================================================ */
add_action( 'wp_footer', 'snn_inline_like_script' );
function snn_inline_like_script() {
	?>
	<script type="text/javascript">
		// Define the global object with the REST API endpoint and nonce.
		var snnLike = {
			endpoint: "<?php echo esc_url( rest_url( 'snn/v1/like' ) ); ?>",
			nonce: "<?php echo wp_create_nonce( 'wp_rest' ); ?>"
		};

		function snn_likeButton(el) {
			var identifier = el.getAttribute('data-identifier');
			// Prevent multiple rapid clicks.
			if ( el.getAttribute('data-processing') === 'true' ) {
				return;
			}
			el.setAttribute('data-processing', 'true');

			var xhr = new XMLHttpRequest();
			xhr.open("POST", snnLike.endpoint, true);
			xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
			xhr.onload = function() {
				if ( xhr.status >= 200 && xhr.status < 300 ) {
					var response = JSON.parse(xhr.responseText);
					if ( response.count !== undefined ) {
						el.innerHTML = 'đź‘Ť' + response.count;
					}
				}
				el.removeAttribute('data-processing');
			};
			xhr.onerror = function() {
				el.removeAttribute('data-processing');
			};
			xhr.send(JSON.stringify({ identifier: identifier, nonce: snnLike.nonce }));
		}
	</script>
	<?php
}