skip to Main Content

I’m trying to set up a new custom REST API endpoint for our wordpress site. I believe I have that part working correctly. The issue I cannot seem to figure out (even after extensive googling and asking Microsoft Copilot and Gitlab Duo Chat) is how to use WP_Query to successfully search for post types by passing a list of url parameters throught the REST API url.

This is what I have as of right now (all code in functions.php)

// Register new REST API route
add_action('rest_api_init', function () {
    register_rest_route('fx-product-search', '/products/search', array(
        'methods' => 'GET',
        'callback' => 'search_products',
        'permission_callback' => '__return_true',
    ));
});

function search_products($request) {
    
    $terms = $request->get_param( 'term' );
    $terms_array = explode(',', $terms);

    $args = array(
        'post_type' => 'fx-product', 
        'posts_per_page' => -1,
        's' => implode( ',', $terms_array),
        'compare' => 'LIKE',
        'sentence' => true,
        'suppress_filters' => true,
     );

    $query = new WP_Query($args);
    //return rest_ensure_response("WP_Query $args are: " . json_encode($args));
    
    $results = [];
    
    if ($query->have_posts()) {   
        //return rest_ensure_response(array('message' => 'have_posts is true.'));
        
        while ($query->have_posts()) {
            //return rest_ensure_response(array('message' => 'In while loop'));
            $query->the_post();
            $content = get_field('content', get_the_ID());
            $thumbnail_id = get_post_meta(get_the_ID(), 'product_image', true);
            $thumbnail_url = wp_get_attachment_url($thumbnail_id);
            $results[] = array(
                'id' => get_the_ID(),
                'title' => get_the_title(),
                'link' => get_permalink(),
                'content' => $content,
                'thumbnail' => $thumbnail_url,
            );
            
        }
                
    }
    
    // Reset post data
    wp_reset_postdata();
    // Return the response  

    if (empty($results)) {
        return rest_ensure_response(array('message' => 'No results found.'));
    } else {
        return rest_ensure_response($results);
    }

}

Note that I’m using the return rest_ensure_response lines as "quick and dirty" sanity checks. (I’m having trouble finding out where the debug and error logs are, or only my server guy can get them and I don’t want to bug him every few mins.)

I’ve confirmed there are matching results on the site as well.

At the moment, it always says "No results found" when I hit a test url.

The goal is to have the REST API call work for URLs like this:
(one term)
…/products/search?term=SecretProduct

and this (multiple terms)
…/products/search?term=Public Product 500,Copier 5000,Quick Print 400i

In the case of multiple terms, the terms would be comma separated but themselves may have spaces in them as shown above. Also, with mutliple terms, it should try to match with logical "OR".

Is this possible? I can get it work with one single word term.

Please advise. Thank you.

3

Answers


  1. Chosen as BEST ANSWER

    Since I couldn't figure out the WP_Query approach, I instead found that querying the database directly seemed to produce the desired results.

    function search_products($request) {
        // Use for confirming request went through correctly
        // Retrieve the request data (if needed)
        $terms = $request->get_param( 'term' ); // Get the array of terms
        $terms_array = explode(',', $terms);
    
        global $wpdb;
    
        // Construct the WHERE clause for the keywords
        $keyword_conditions = array();
        foreach ($terms_array as $term) {
            $escaped_keyword = $wpdb->esc_like($term);
            $keyword_conditions[] = "post_title LIKE '%{$escaped_keyword}%'";
        }
    
        // Combine the conditions with OR
        $where_clause = implode(' OR ', $keyword_conditions);
    
        // Build the final query
        $query = "
            SELECT ID, post_title
            FROM {$wpdb->posts}
            WHERE post_type = 'fx-product'
            AND post_status = 'publish'
            AND ({$where_clause})
        ";
    
        $results = $wpdb->get_results($query);
    
        if ($results) {
            $output = array();
            foreach ($results as $result) {
                $content = get_field('content', $result->ID);
                $thumbnail_id = get_post_meta($result->ID, 'product_image', true);
                $thumbnail_url = wp_get_attachment_url($thumbnail_id);
                $permalink = get_permalink($result->ID);
    
                $post_data = array(
                    'post_id' => $result->ID,
                    'post_title' => $result->post_title,
                    'content' => $content,
                    'thumbnail_url' => $thumbnail_url,
                    'permalink' => $permalink
                );
    
                $output[] = $post_data;
            }
    
            echo json_encode($output);
        } else {
            echo json_encode(array('message' => 'No matching posts found.'));
        }
    
    }
    

  2. I have checked the code and found that the issue is appearing because in that code we are using s parameter for searching multiple terms while that parameter is taking those terms as string which causes this issue so need to create meta query by looping over the exploded term array so desired results can be achieved, so you need to replace the search_products function in your code.

    <?php
    function search_products( $request ) {
        
        $terms       = $request->get_param( 'term' );
        $terms_array = explode( ',', $terms );
        
        // We are using this to trim whitespace from each term we have exploded.
        $terms_array = array_map('trim', $terms_array);
    
        $args = array(
            'post_type'        => 'fx-product', 
            'posts_per_page'   => -1,
            'meta_query'       => array(
                'relation' => 'OR',
            ),
            'suppress_filters' => true,
        );
        // Here we are looping over each term to build meta query.
        foreach ( $terms_array as $term ) {
            $args['meta_query'][] = array(
                'key'     => 'content',  // Here we need to replace the ACF field key as per the need.
                'value'   => $term,
                'compare' => 'LIKE',
            );
        }
    
        $query   = new WP_Query($args);
        
        $results = [];
        
        if ( $query->have_posts() ) {   
            while ( $query->have_posts() ) {
                $query->the_post();
                $content       = get_field('content', get_the_ID());
                $thumbnail_id  = get_post_meta(get_the_ID(), 'product_image', true);
                $thumbnail_url = wp_get_attachment_url($thumbnail_id);
                $results[]     = array(
                    'id'        => get_the_ID(),
                    'title'     => get_the_title(),
                    'link'      => get_permalink(),
                    'content'   => $content,
                    'thumbnail' => $thumbnail_url,
                );
            }
        }
        
        wp_reset_postdata();
        
        if ( empty( $results ) ) {
            return rest_ensure_response( array( 'message' => 'No results found.') );
        } else {
            return rest_ensure_response( $results );
        }
    }
    
    Login or Signup to reply.
  3. s is the search option for posts, which by default searches post_title, post_excerpt, post_content. As far as I understand, you want to get posts from specific categories. So use tax_query:

    $args = array(
     'post_type' => 'fx-product',
     'posts_per_page' => -1,
     'tax_query' => array(
        array(
          'taxonomy' => 'your_taxonomy_name',
          'field' => 'slug',
          'terms' => array( 'term_slug_1', 'term_slug_1' ),
        )
      )
    );
    
    $query = new WP_Query($args);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search