You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为WooCommerce Ajax搜索生成按文章类型过滤的URL?

Fixing WooCommerce Typeahead Search: Separating Results by Post Type

Hey there! Let's figure out how to get your WooCommerce typeahead search showing separated product and content results properly. The issue you're facing is that your existing AJAX endpoint isn't respecting the posttype parameter—so we'll need to adjust the backend logic first, then set up the frontend to either fetch separate results or group them in a single request.

Step 1: Update the AJAX Search Handler

First, let's modify the backend function that handles your ajax_search action. The goal is to make it recognize the posttype parameter and filter results accordingly, or return grouped results for all post types if the parameter isn't set.

Add this to your theme's functions.php file (or a custom plugin):

add_action('wp_ajax_ajax_search', 'custom_filtered_ajax_search');
add_action('wp_ajax_nopriv_ajax_search', 'custom_filtered_ajax_search');

function custom_filtered_ajax_search() {
    // Validate search term exists
    if (!isset($_GET['terms']) || empty($_GET['terms'])) {
        wp_send_json_error('No search term provided');
    }

    $search_term = sanitize_text_field($_GET['terms']);
    
    // Determine which post types to search
    $target_post_types = isset($_GET['posttype']) 
        ? (array) $_GET['posttype'] 
        : ['post', 'product', 'page']; // Default to all three types

    // Set up WP_Query arguments
    $query_args = [
        's' => $search_term,
        'post_type' => $target_post_types,
        'posts_per_page' => 8, // Adjust based on how many results you want
        'post_status' => 'publish',
        'no_found_rows' => true, // Optimize for faster queries
    ];

    $search_query = new WP_Query($query_args);
    $results = [];

    if ($search_query->have_posts()) {
        while ($search_query->have_posts()) {
            $search_query->the_post();
            $post_type = get_post_type();

            // Group results by post type
            if (!isset($results[$post_type])) {
                $results[$post_type] = [];
            }

            // Add relevant data (customize this based on your needs)
            $results[$post_type][] = [
                'id' => get_the_ID(),
                'title' => get_the_title(),
                'url' => get_permalink(),
                'price' => $post_type === 'product' ? wc_get_price_to_display(get_post()) : '',
                'excerpt' => wp_trim_words(get_the_excerpt(), 10)
            ];
        }
        wp_reset_postdata();
    }

    wp_send_json_success($results);
}

This function will now:

  • Respect the posttype parameter (you can pass a single type like product or multiple types like post,page)
  • Return results grouped by post type if no posttype is specified
  • Include WooCommerce-specific data like product prices for product results

Step 2: Frontend Options for Separated Results

You have two solid options here—choose whichever fits your workflow better:

Option 1: Separate AJAX Requests for Each Post Type

If you want to fetch results for each post type independently (e.g., one request for products, one for posts), use these URLs in your typeahead configuration:

  • Products only: wp-admin/admin-ajax.php?action=ajax_search&terms=[searchterm]&posttype=product
  • Posts only: wp-admin/admin-ajax.php?action=ajax_search&terms=[searchterm]&posttype=post
  • Pages only: wp-admin/admin-ajax.php?action=ajax_search&terms=[searchterm]&posttype=page

Here's how to set this up with Typeahead.js (using Bloodhound for remote data):

// Initialize Bloodhound for products
const productSuggestions = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: {
        url: 'wp-admin/admin-ajax.php?action=ajax_search&terms=%QUERY&posttype=product',
        wildcard: '%QUERY',
        transform: (response) => response.success ? response.data.product : []
    }
});

// Initialize Bloodhound for posts
const postSuggestions = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: {
        url: 'wp-admin/admin-ajax.php?action=ajax_search&terms=%QUERY&posttype=post',
        wildcard: '%QUERY',
        transform: (response) => response.success ? response.data.post : []
    }
});

// Initialize Typeahead with separate result groups
$('.search-input').typeahead({
    hint: true,
    highlight: true,
    minLength: 2
},
{
    name: 'products',
    display: 'title',
    source: productSuggestions,
    templates: {
        header: '<h4 class="search-group-header">Products</h4>',
        suggestion: (item) => `
            <div class="search-result product">
                <a href="${item.url}">${item.title}</a>
                <span class="product-price">${item.price}</span>
            </div>
        `
    }
},
{
    name: 'posts',
    display: 'title',
    source: postSuggestions,
    templates: {
        header: '<h4 class="search-group-header">Articles</h4>',
        suggestion: (item) => `
            <div class="search-result post">
                <a href="${item.url}">${item.title}</a>
                <p class="excerpt">${item.excerpt}</p>
            </div>
        `
    }
});

For better performance (fewer HTTP requests), fetch all results in one call and let the frontend render them in separate groups. Here's how:

const allSearchResults = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: {
        url: 'wp-admin/admin-ajax.php?action=ajax_search&terms=%QUERY',
        wildcard: '%QUERY',
        transform: (response) => {
            if (!response.success) return [];
            
            // Flatten grouped results into a single array with headers
            const flattenedResults = [];
            const results = response.data;

            // Add product group
            if (results.product) {
                flattenedResults.push({ type: 'header', label: 'Products' });
                flattenedResults.push(...results.product.map(item => ({ ...item, type: 'product' })));
            }

            // Add post group
            if (results.post) {
                flattenedResults.push({ type: 'header', label: 'Articles' });
                flattenedResults.push(...results.post.map(item => ({ ...item, type: 'post' })));
            }

            // Add page group
            if (results.page) {
                flattenedResults.push({ type: 'header', label: 'Pages' });
                flattenedResults.push(...results.page.map(item => ({ ...item, type: 'page' })));
            }

            return flattenedResults;
        }
    }
});

$('.search-input').typeahead({
    hint: true,
    highlight: true,
    minLength: 2
},
{
    name: 'all-results',
    display: (item) => item.type === 'header' ? item.label : item.title,
    source: allSearchResults,
    templates: {
        suggestion: (item) => {
            if (item.type === 'header') {
                return `<h4 class="search-group-header">${item.label}</h4>`;
            } else if (item.type === 'product') {
                return `
                    <div class="search-result product">
                        <a href="${item.url}">${item.title}</a>
                        <span class="product-price">${item.price}</span>
                    </div>
                `;
            } else {
                return `
                    <div class="search-result content">
                        <a href="${item.url}">${item.title}</a>
                        <p class="excerpt">${item.excerpt}</p>
                    </div>
                `;
            }
        }
    }
});

Why Your Original Parameter Didn't Work

The most likely reason the posttype parameter was ignored is that your original get_ajax_search function wasn't designed to read or act on that parameter. The updated function above explicitly checks for it and adjusts the WP_Query accordingly.

内容的提问来源于stack exchange,提问作者ThomasB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:56:10