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 = ''; } }); });
