This PHP code adds a custom REST API endpoint (custom/v1/products) to a WooCommerce site, allowing for real-time product searches via AJAX or external requests. When a user sends a GET request with a search query (?search=product-name), the function custom_products_search retrieves up to 10 published WooCommerce products that match the search term. The response includes each product’s ID, title, permalink, price, and featured image (or a default image if none exists). The endpoint is public (permission_callback allows unrestricted access), making it useful for integrating real-time search into a WooCommerce store without requiring user authentication
php:
/* === SEACH PAGE REALTIME SEARCH custom endpoint for woo === */
add_action('rest_api_init', function () {
register_rest_route('custom/v1', '/products', array(
'methods' => 'GET',
'callback' => 'custom_products_search',
'permission_callback' => '__return_true', // Allow public access; adjust if needed.
));
});
/**
* Callback for the custom product search endpoint.
*
* Searches for published products matching the "search" query parameter.
*
* @param WP_REST_Request $request The current request.
* @return WP_REST_Response|array The search results.
*/
function custom_products_search(WP_REST_Request $request) {
// Retrieve and sanitize the search query
$search_query = sanitize_text_field($request->get_param('search'));
// Define query arguments for WP_Query
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
's' => $search_query,
'posts_per_page' => 10,
);
$query = new WP_Query($args);
$results = [];
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$post_id = get_the_ID();
// Get the product price from WooCommerce meta field
$price = get_post_meta($post_id, '_price', true);
// Get the featured image URL (thumbnail size)
$thumbnail = get_the_post_thumbnail_url($post_id, 'thumbnail');
if (!$thumbnail) {
$thumbnail = 'https://yourwebsiteurl.localhost/default-image-if-empty.jpg';
}
// Build each product’s result
$results[] = array(
'id' => $post_id,
'title' => get_the_title(),
'link' => get_permalink(),
'thumbnail' => $thumbnail,
'price' => $price,
);
}
wp_reset_postdata();
}
return rest_ensure_response($results);
}
js:
/* === SEACH PAGE REALTIME SEARCH === */
const MIN_SEARCH_CHARACTERS = 2; // Minimum characters required to trigger a search
const MAX_SEARCH_RESULTS = 6; // Maximum number of search results to display
document.addEventListener('DOMContentLoaded', function () {
// Get the search input element and search form element
const searchInput = document.querySelector('.search-page-input .bricks-search-form input');
const searchForm = document.querySelector('.search-page-input .bricks-search-form');
// Check if the necessary DOM elements exist; if not, do not run the rest of the code
if (!searchInput || !searchForm) {
return;
}
// Create a new ul element to display the product search results and add a custom class for styling
const resultList = document.createElement('ul');
resultList.classList.add('custom-search-results');
resultList.style.marginTop = '10px';
resultList.style.listStyle = 'none';
resultList.style.padding = '0';
searchForm.insertAdjacentElement('afterend', resultList);
// Function to fetch product search results from the custom WooCommerce REST API endpoint
async function fetchSearchResults(query) {
const response = await fetch(`/wp-json/custom/v1/products?search=${query}`);
if (response.ok) {
const data = await response.json();
return data;
}
return [];
}
// Function to display product search results
function displayResults(results) {
// Clear the current result list
resultList.innerHTML = '';
// Limit results to the maximum number specified in settings
results.slice(0, MAX_SEARCH_RESULTS).forEach(product => {
// Create a list item for each product
const li = document.createElement('li');
li.style.display = 'flex';
li.style.alignItems = 'center';
li.style.justifyContent = 'space-between';
li.style.marginBottom = '10px';
li.style.border = '1px solid #ddd';
li.style.padding = '5px';
li.style.borderRadius = '4px';
// Create a container for the thumbnail and title (left side)
const leftDiv = document.createElement('div');
leftDiv.style.display = 'flex';
leftDiv.style.alignItems = 'center';
// Create and set up the thumbnail image
const img = document.createElement('img');
img.src = product.thumbnail ? product.thumbnail : 'https://dev.baggy.geopard-digital.com/wp-content/uploads/2025/02/image-30124.webp';
img.alt = product.title;
img.style.width = '50px';
img.style.height = '50px';
img.style.objectFit = 'cover';
leftDiv.appendChild(img);
// Create the title span and add a little margin to separate it from the image
const titleSpan = document.createElement('span');
titleSpan.textContent = product.title;
titleSpan.style.marginLeft = '10px';
leftDiv.appendChild(titleSpan);
li.appendChild(leftDiv);
// Create the price element (right side)
const priceSpan = document.createElement('span');
priceSpan.textContent = product.price ? product.price + ' €' : '';
li.appendChild(priceSpan);
// Wrap the entire list item in an anchor tag for linking to the product page
const link = document.createElement('a');
link.href = product.link;
link.style.textDecoration = 'none';
link.style.color = 'inherit';
link.appendChild(li);
resultList.appendChild(link);
});
}
// Add an event listener to the search input for real-time product search
searchInput.addEventListener('input', async function () {
const query = searchInput.value.trim();
// Perform the product search only if the query has at least the minimum number of characters specified in settings
if (query.length >= MIN_SEARCH_CHARACTERS) {
const results = await fetchSearchResults(query);
displayResults(results);
} else {
// Clear the result list if the query is less than the minimum number of characters required
resultList.innerHTML = '';
}
});
});
