3

I'm currently trying to extend the WooCommerce product search so that the search uses my custom field I've created within the general product data section:

add_action( 'woocommerce_product_options_general_product_data', 'woocommerce_product_options_general_product_data_action', 9999, 0 );
function woocommerce_product_options_general_product_data_action(): void {
    global $post;

    echo '<div class="options_group">';

    woocommerce_wp_text_input( [
        'id'            => '_pdc_notch',
        'wrapper_class' => '',
        'label'         => 'Notch',
        'desc_tip'      => true,
        'type'          => 'text',
        'description'   => 'Enter notch PDC',
        'value'         => get_post_meta( $post->ID, '_pdc_notch', true )
    ] );

    echo '</div>';
}

add_action( 'woocommerce_process_product_meta', 'woocommerce_process_product_meta_action', 10, 1 );
function woocommerce_process_product_meta_action( int $product_id ): void {
    $pdc_notch = sanitize_text_field( wp_unslash( $_POST['_pdc_notch'] ?? null ) );

    update_post_meta( $product_id, '_pdc_notch', $pdc_notch );
}

With the above code, I can add and save my custom field. Now I've extended the search with the below hook:

add_action( 'woocommerce_product_query', 'woocommerce_product_query_action', 10, 2 );
function woocommerce_product_query_action( WP_Query $q, object $instance ) {
    if ( ! is_admin() && $q->is_main_query() && $q->is_search() ) {
        $meta_query = $q->get( 'meta_query' );

        $meta_query[] = [
            'key'     => '_pdc_notch',
            'value'   => $q->query['s'],
            'compare' => 'LIKE'
        ];

        $q->set( 'meta_query', $meta_query );
    }
}

When I set a text like "Iamabigtestword" to my field and put it in the search, I'm still getting nothing. What am I missing? I really can't find the issue here. Normally, the hook should work (regarding an answer from StackOverflow: Include custom fields value in woocommerce search)

Note

You can copy/paste the code directly in the functions.php file of your child theme to test it, since it has no dependencies and should add the field to the Products > Product > General tab at the end.

Mr. Jo
  • 4,946
  • 6
  • 41
  • 100
  • Have you already tested (by debugging) whether the `woocommerce_product_query` hook is effectively fired on a/after a search? – 7uc1f3r Dec 16 '21 at 20:27
  • 1
    Yes, I'm always doing this by default. The if condition gets used also and the query gets returned. I think I will debug it line by line in the core function. I was just hoping that somebody knows a solution. – Mr. Jo Dec 16 '21 at 20:29

1 Answers1

1

I think I have found a way after some testing. First, I've debugged the WC function where the action gets applied, but changed my approach since I was making no progress. I've now extended the post search via the given WordPress filter:

add_filter( 'posts_search', 'filter_posts_search', 10, 2 );
/**
 * Extend product search to use the new custom field within search
 *
 * @param string $search
 * @param WP_Query $query
 *
 * @return string
 */
function filter_posts_search( string $search, WP_Query $query ): string {
    global $wpdb;

    if ( empty( $search ) || ! ( isset( $query->query_vars['s'], $query->query_vars['post_type'] ) && ! empty( $query->query_vars['s'] ) && $query->query_vars['post_type'] === 'product' ) || is_admin() || ! is_search() || ! is_main_query() ) {
        return $search;
    }

    $product_ids = [];

    $products = wc_get_products( [
        'post_type'    => 'product',
        'limit'        => - 1,
        'meta_key'     => '_pdc_notch',
        'meta_value'   => esc_attr( $query->query_vars['s'] ),
        'meta_compare' => 'LIKE' // or '='
    ] );

    /**
     * @var WC_Product $product
     */
    foreach ( $products as $product ) {
        $product_ids[] = $product->get_id();
    }

    $product_ids = array_unique( $product_ids );

    if ( count( $product_ids ) > 0 ) {
        $search = str_replace( 'AND (((', "AND ((({$wpdb->posts}.ID IN (" . implode( ',', $product_ids ) . ")) OR (", $search );
    }

    return $search;
}

I hope it helps someone! If someone finds a better approach, I can test it for you.

Mr. Jo
  • 4,946
  • 6
  • 41
  • 100