如何让WordPress/WooCommerce搜索功能只搜索产品标签和产品?

问题描述 投票:0回答:1

我在进行 WordPress 搜索查询、搜索 WooCommerce 产品和标签/关键字时遇到一些问题。让它搜索类别也很棒。

目前我已经让它只搜索 WooCommerce 产品,但仍然需要标签/关键字,并且也能很好地获取产品类别搜索结果

而且标签/关键词可能很多,所以它必须是动态的,这样客户或我就不必偶尔更新它。

// Only search woocommerce products not posts and pages
add_action( 'pre_get_posts', 'search_only_products' );
function search_only_products( $query ) {
  if( ! is_admin() && is_search() && $query->is_main_query() ) {
        $args = array(
        
        );
        $query->set(    'post_type', 'product', 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => '_visibility',
                'value' => 'hidden',
                'compare' => 'NOT EXISTS',
            ),
            array(
                'key' => '_visibility',
                'value' => 'hidden',
                'compare' => '!=',
            ),
        ));
        // $query->set($args);
  }
}
php wordpress woocommerce hook-woocommerce
1个回答
0
投票
<?php
/**
 * Limits search results to only show products
 *
 * @param WP_Query $wp_query The main WP_Query object
 * @return void
 */
function limit_search_to_products( $wp_query ) {
    
    // Only limit frontend search queries for main query
    if ( ! is_admin() && is_search() && $wp_query->is_main_query() ) {

        // Get search term
        $search_term = $wp_query->get( 's' );

        // Split search term into an array of keywords
        $keywords_array = preg_split(
            "/[\s-]+/",
            $search_term,
            -1,
            PREG_SPLIT_NO_EMPTY
        );

        if ( ! empty( $keywords_array ) ) {

            global $wpdb;
            
            // Array to hold product IDs that match the search term keywords
            $tag_filtered_product_ids_unorganized = array();

            $wp_query_hash = md5( serialize( $wp_query->query_vars ) );

            // Check if the result of the tag query has already been cached
            $catch_of_tag_query_result = get_transient( $wp_query_hash . '_for_custom_tag_search' );

            // Check if the result of the tag query has already been cached
            if( false === $catch_of_tag_query_result ) {

                // Loop through each keyword to search for products with matching tags or categories
                foreach ( $keywords_array as $keyword ) {
    
                    // Query to find products for matching tags or categories
                    $tag_search_query = $wpdb->prepare( "
                        SELECT p.ID, p.post_type, p.post_status
                        FROM {$wpdb->prefix}posts AS p
                        JOIN {$wpdb->prefix}term_relationships AS tr ON p.id = tr.object_id
                        JOIN {$wpdb->prefix}term_taxonomy AS tt ON ( tr.term_taxonomy_id = tt.term_taxonomy_id AND ( tt.taxonomy = 'product_cat' OR tt.taxonomy = 'product_tag' ) )
                        JOIN {$wpdb->prefix}terms AS t ON tt.term_id = t.term_id
                        WHERE t.name LIKE %s AND p.post_type = 'product' AND p.post_status = 'publish'
                    ", '%' . $wpdb->esc_like( $keyword ) . '%' );
    
                    // Get search query results
                    $tag_search_query_results = $wpdb->get_results( $tag_search_query );
    
                    if ( ! empty( $tag_search_query_results ) ) {
                        // Loop through search query results to collect product IDs
                        foreach ( $tag_search_query_results as $tag_search_query_result ) {
                            array_push(
                                $tag_filtered_product_ids_unorganized,
                                $tag_search_query_result->ID
                            );
                        }
                    }
                }

                if ( ! empty( $tag_filtered_product_ids_unorganized ) ) {

                    // Count how many times each product ID appears in the array of search results
                    $count_product_ids_in_array = array_count_values( $tag_filtered_product_ids_unorganized );

                    // Total number of keywords in search term
                    $total_keywords = count( $keywords_array );

                    // Filter the array of product IDs to only include those that match all keywords
                    $tag_filtered_product_ids_unorganized = array_filter(
                        $count_product_ids_in_array,
                        function ( $number_of_occurrences_of_product_id_in_array )
                            use ( $total_keywords ) {
                                return $number_of_occurrences_of_product_id_in_array == $total_keywords;
                        }
                    );                    
                }
                // Final array of product IDs that match the search term keywords for tags or categories
                $tag_filtered_product_ids = array_keys( $tag_filtered_product_ids_unorganized );

                // Cache the result
                set_transient( $wp_query_hash . '_for_custom_tag_search', serialize( $tag_filtered_product_ids ), 3600 );

            }
            else {
                // If the tag query result is already cached, retrieve it
                $tag_filtered_product_ids = unserialize( $catch_of_tag_query_result );
            }
        }

        // If products found that matches tags or categories
        if ( isset( $tag_filtered_product_ids ) && ! empty( $tag_filtered_product_ids ) ) {

            // Get product IDs from default search query
            $query_vars = $wp_query->query_vars;
            $query_vars['post_type'] = 'product';
            $query_vars['fields'] = 'ids';
            $query_vars['posts_per_page'] = -1;
            $query = new WP_Query( $query_vars );
            $search_result_products_id = $query->posts;
            wp_reset_postdata();wp_reset_query();

            // Get the product IDs that appear in both the default search query and the custom tag search query
            $merged_search_results = array_unique(
                array_merge(
                    $search_result_products_id,
                    $tag_filtered_product_ids
                )
            );

            // Set the search parameter to false to prevent it from interfering with the product list
            // By setting it to false, this change the default search page title tag from being displayed correctly
            // But the second function (which I add bellow in this answer) to control the search page title tag
            $wp_query->set( 's', false );

            // Set the product IDs that we want to display in the search results
            $wp_query->set( 'post__in', $merged_search_results );

            // Order the search results by the merged product ID array to maintain the default search order and add the custom tag search results to the end
            $wp_query->set( 'orderby', 'post__in' );

        }
        
        // Set post type to the product only
        $wp_query->set( 'post_type', 'product' );

        // Get only visible products
        // Now, Woocommerce use tax_query instead of meta_query
        # https://woocommerce.com/posts/woocommerce-3-0-release/
        # https://stackoverflow.com/a/48487242/20896084
        $wp_query->set( 'tax_query', array(
            'taxonomy'  => 'product_visibility',
            'terms'     => array('exclude-from-catalog'),
            'field'     => 'name',
            'operator'  => 'NOT IN',
        ));
    }
}
add_action( 'pre_get_posts', 'limit_search_to_products' );

我添加了多条注释来解释代码中发生的情况,我认为这是理解这段长代码更简单的方法,而不是在一个段落中进行所有解释。

它 100% 有效,但我建议从头开始构建自定义搜索功能,这样比 WP 搜索功能中的这种更改更灵活、更容易构建和可定制。


如果您阅读了上面的所有代码,那么您会听到第二个函数,该函数解决由我的答案创建的搜索页面标题标签问题以及有关它的更多解释。

在我的回答中,我这样做:

$wp_query->set( 's', false );

原因:我使用

post__in
来显示所有帖子,但如果查询中存在
s
,则
post__in
无法按照我想要的方式工作。

解释:如果

s
给我们产品
array(1,2,3,4,5)
并且
post__in
array(3,5,7,9)
,结果是
array(3,5)
,但这不是我们想要的,我们希望所有帖子都在
post__in
,所以我设置
 s
是假的。

这就是为什么我们从 get_search_query() 没有得到任何东西,它用于设置搜索页面标题标签(我用于修复的钩子:document_title_parts

<?php
/**
 * Modify the part of the document title for the search page
 *
 * @param array $title Default document title, page number, site name & description
 * @return array $title
 */
function set_title_tag_for_search_page( $title ) {
    if ( ! is_admin() && is_search() && isset( $_GET['s'] ) && ! is_404() ) {
        $search_query_parameter = sanitize_text_field( wp_unslash( esc_attr( $_GET['s'] ) ) );
        $blog_name    = get_bloginfo( 'name' );
        $search_title = sprintf( __( 'Search Results for &#8220;%s&#8221;', 'text-domain' ), $search_query_parameter );
        $title['title'] = html_entity_decode( $search_title, ENT_QUOTES, get_bloginfo( 'charset' ) );
    }
    return $title;
}
add_filter( 'document_title_parts', 'set_title_tag_for_search_page' );

我添加了图片以便更好地理解

如果你使用不要第二个功能 如果您使用第二个功能
Before fix After fix
Before fix After fix
© www.soinside.com 2019 - 2024. All rights reserved.