Home > Back-end >  MySQL LIKE %filter% equivalent when query with wc_get_orders WooCommerce
MySQL LIKE %filter% equivalent when query with wc_get_orders WooCommerce

Time:06-22

First, sorry for my ignorance in this field and I will try to express myself clear.

I am trying to use wc_get_orders to get orders from Woocommerce with the following args

$args = array(
  'limit' => $rows_per_page,
  'paged' => $page,
  'paginate' => true,
  'customer' => $filter
);
$results = wc_get_orders($args);
$orders = $results->orders;

code taken from https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query#customer and modified by me.

$filter is a partial string of customer name or email. With this setup, I can only filter and receive result when $filter is full: [email protected]. I am trying to partially match %@woo% just like in MySQL with no luck.

Currently, I solve this with a raw query as follow (I understand that it is not a best practice and can break when woo commerce update their database):

global $wpdb;
$rows_per_page = intval((isset($_POST['rowsPerPage'])) ? sanitize_text_field($_POST['rowsPerPage']) : '5');
$page = intval((isset($_POST['page'])) ? sanitize_text_field($_POST['page']) : '1');
$filter = ((isset($_POST['filter'])) ? sanitize_text_field($_POST['filter']) : '') === 'undefined' ? '' : $_POST["filter"];

$skip = ($page - 1) * $rows_per_page;

$sqlCount = "
      SELECT 
      SQL_CALC_FOUND_ROWS {$wpdb->prefix}posts.ID 
   FROM 
      {$wpdb->prefix}posts 
   WHERE 
      1 = 1 
      AND wp_posts.post_type = 'shop_order' 
      AND (
      (
         {$wpdb->prefix}posts.post_status = 'wc-pending'
         OR {$wpdb->prefix}posts.post_status = 'wc-processing'
         OR {$wpdb->prefix}posts.post_status = 'wc-on-hold'
         OR {$wpdb->prefix}posts.post_status = 'wc-completed'
         OR {$wpdb->prefix}posts.post_status = 'wc-cancelled' 
         OR {$wpdb->prefix}posts.post_status = 'wc-refunded'
         OR {$wpdb->prefix}posts.post_status = 'wc-failed'
      )
      ) 
      AND {$wpdb->prefix}posts.ID IN (
         SELECT post_id AS order_id
         FROM {$wpdb->prefix}postmeta 
         WHERE meta_key 
         IN ('_billing_first_name', '_billing_email', '_billing_phone' )
         AND meta_value LIKE '%" . $filter . "%'
      )
   ORDER BY
      {$wpdb->prefix}posts.post_date DESC
";

$sql = "
      SELECT 
      SQL_CALC_FOUND_ROWS {$wpdb->prefix}posts.ID 
   FROM 
      {$wpdb->prefix}posts 
   WHERE 
      1 = 1 
      AND wp_posts.post_type = 'shop_order' 
      AND (
        (
          {$wpdb->prefix}posts.post_status = 'wc-pending' 
          OR {$wpdb->prefix}posts.post_status = 'wc-processing' 
          OR {$wpdb->prefix}posts.post_status = 'wc-on-hold' 
          OR {$wpdb->prefix}posts.post_status = 'wc-completed' 
          OR {$wpdb->prefix}posts.post_status = 'wc-cancelled' 
          OR {$wpdb->prefix}posts.post_status = 'wc-refunded' 
          OR {$wpdb->prefix}posts.post_status = 'wc-failed'
        )
      )
      AND {$wpdb->prefix}posts.ID IN (
         SELECT post_id AS order_id
         FROM {$wpdb->prefix}postmeta 
         WHERE meta_key 
         IN ('_billing_first_name', '_billing_email', '_billing_phone' )
         AND meta_value LIKE '%" . $filter . "%'
      )
   ORDER BY 
      {$wpdb->prefix}posts.post_date DESC 
   LIMIT 
      " . $skip . ", " . $rows_per_page . "       
";
$orders = $wpdb->get_results($sql);
$rowcount = count($wpdb->get_results($sqlCount));

I am not entirely happy with my raw sql setup because it may not be the best way to do such thing and I need to run 2 sql queries, one to get the paginated result, one to count the number of results returned. Is there a better way to do this?

Thank you very much.

CodePudding user response:

/**
 * Handle a custom 'customvar' query var to get orders with the 'customvar' meta.
 * @param array $query - Args for WP_Query.
 * @param array $query_vars - Query vars from WC_Order_Query.
 * @return array modified $query
 */
function handle_custom_query_var( $query, $query_vars ) {
    if ( ! empty( $query_vars['customvar'] ) ) {
        $query['meta_query'][] = array(
            'key' => 'customvar',
            'value' => esc_attr( $query_vars['customvar'] ),
            'compare' => 'LIKE'
        );
    }

    return $query;
}
add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', 'handle_custom_query_var', 10, 2 );

Using LIKE inside wc_get_orders() with custom values can be done like the above code snippet. You can find more details here

CodePudding user response:

I found out how to do this. First, this is my $args:

$args = array(
  'limit' => $rows_per_page,
  'paged' => $page,
  'status' => $status,
  'paginate' => true,
  'meta_query' => array(
    'relation' => 'OR',
    array(
      'key'     => '_billing_first_name',
      'value'   => esc_attr($filter),
      'compare' => 'LIKE'
    ),
     array(
       'key'     => '_billing_email',
       'value'   => esc_attr($filter),
       'compare' => 'LIKE'
     ),
     array(
       'key'     => '_billing_phone',
       'value'   => esc_attr($filter),
       'compare' => 'LIKE'
     )
   )
);

To this point wc_get_orders won't work.

$results = wc_get_orders($args);

We need to tell Wordpress/WooCommerce to take the meta_query into consideration:

add_filter('woocommerce_get_wp_query_args', function ($wp_query_args, $query_vars) {
   if (isset($query_vars['meta_query'])) {
      $meta_query = isset($wp_query_args['meta_query']) ? $wp_query_args['meta_query'] : [];
      $wp_query_args['meta_query'] = array_merge($meta_query, $query_vars['meta_query']);
   }
   return $wp_query_args;
}, 10, 2);

This wonderful code is from this answer: https://wordpress.stackexchange.com/a/337865

  • Related