WooCommerce

WooCommerce后台通过自定义字段检索产品

WooCommerce后台的产品检索与前台搜索略有不同。前台是在WordPress默认搜索的基础上修改,后台直接用了一套自己的逻辑,从wc_product_meta_lookup这个表里检索产品,调用函数WC_Product_Data_Store_CPT->search_products(),这个函数只有一个filter——woocommerce_product_pre_search_products,允许返回自定义结果,代价是截断了WooCommerce所有的搜索逻辑,显然不适合小修改。后台可以搜sku,但其它自定义字段就不行了,且没有任何位置可以干预。幸运的是我们还能用WordPress自带的filter – request来完成自定义字段搜索功能。

WordPress Request Filter

它的用法如下:

  • 它可以修改WordPress所有主查询的参数,且是在sql被执行之前运行。也就是说,我们可以在WooCommerce搜索的查询参数确定后,修改查询参数,再执行查询,这是我们加入自定义字段查询的关键。
  • 它可以用在主题的functions.php里,但不能用在模板文件里,因为模板文件运行时,WordPress的主查询已经运行完了。
  • 它会在全站运行,无论前台还是后台,所以要仔细测试,不要弄坏网站其它功能。

简单用法如下,也是本文代码的和心思想。

add_filter( 'request', 'alter_the_query' ); 
function alter_the_query( $query_vars ) {
    
    $query_vars['post__in'] = array(1,2,3,4,5);
 
    return $request;
}

post___in参数负责告诉查询,把符合数组里指定的post_id的post全部返回给我们。只要我们用自定义查询找到想要的post_id,加到这个参数里,就会和WooCommerce的其它搜索结果一并返回。

增加自定义字段搜索功能的代码

这段代码来自Extending WooCommerce admin product search to use custom fields,放到主题的functions.php里即可。假定要搜索的自定义字段的meta_key为“ _your_meta_key ”。

/**
 * Alter the query vars to include products which have the meta we are searching for.
 * 
 * @param array $query_vars The current query vars.
 *
 * @return array
 */
function sola_request_query( $query_vars ) {

  global $typenow;
  global $wpdb;
  global $pagenow;

  // 确保仅在后台产品列表页面运行
  if ( 'product' === $typenow && isset( $_GET['s'] ) && 'edit.php' === $pagenow ) {

    $meta_key    = '_your_meta_key';  // 要搜索的meta_key
    $search_term = esc_sql( sanitize_text_field( $_GET['s'] ) );
    $post_types  = array( 'product', 'product_variation' );

    $sql = "SELECT DISTINCT posts.ID as product_id, posts.post_parent as parent_id 
         FROM {$wpdb->posts} posts 
         LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id 
         WHERE postmeta.meta_key = '{$meta_key}' 
          AND postmeta.meta_value LIKE %s 
          AND posts.post_type IN ('" . implode( "','", $post_types ) . "') 
         ORDER BY posts.post_parent ASC, posts.post_title ASC";

    $search_results = $wpdb->get_results(
      $wpdb->prepare( $sql,'%' . $wpdb->esc_like( $search_term ) . '%' )
    );

    // 将查询到的parent_id和product_id合并,去除重复数据
    $product_ids            = wp_parse_id_list( array_merge( wp_list_pluck( $search_results, 'product_id' ), wp_list_pluck( $search_results, 'parent_id' ) ) );

    // 修改query_vars,将查询到的产品id放到post_in参数里,该参数的作用是直接指定要获取的post
    $query_vars['post__in'] = array_merge( $product_ids, $query_vars['post__in'] );

  }

  return $query_vars;
}

add_filter( 'request', 'sola_request_query', 20 );

给产品列表增加自定义字段

我们来实际测试一下,先在后台的产品列表增加一个自定义字段,meta_key是“ _your_meta_key ”。

add_filter( 'manage_product_posts_columns', 'sola_new_custom_product_columns' );
function sola_new_custom_product_columns( $columns ){
  $newColumns = [
    'my_custom_column' => '自定义字段',
  ];
  return array_slice( $columns, 0, 2 ) + $newColumns + $columns;
}

add_action( 'manage_product_posts_custom_column', 'sola_show_custom_product_columns', 10, 2 );
function sola_show_custom_product_columns( $column, $post_id ){
  switch( $column ){
    case 'my_custom_column' :
      echo esc_html( get_post_meta( $post_id, '_your_meta_key', true) );
      break;
  }
}
添加自定义字段

再增加了自定义字段搜索的代码后,我们就能搜到这个自定义字段了,如下所示:

WooCommerce后台产品-搜索自定义字段

如何给产品增加自定义字段

你可以用Advanced Custom Fields插件创建字段,也可以用代码,例如在之前文章WooCommerce 后台自定义产品选项中提到的方法。

WooCommerce 后台自定义产品选项

函数解释

在代码中出现了一些WordPress函数,简单解释一下用法。

wp_list_pluck()

从数组或对象里提取一个字段,类似PHP的array_column()函数。

$foods = array(
    array(
        'name'  => 'Banana',
        'color' => 'Yellow',
    ),
    array(
        'name'  => 'Apple',
        'color' => 'Red',
    ),
    array(
        'name'  => 'Lettuce',
        'color' => 'Green',
    ),
    array(
        'name'  => 'Apple',
        'color' => 'Red',
    ),
);
$food_names = wp_list_pluck( $foods, 'name' );

/*
 * 返回值
 * array ( 0 => 'Banana', 1 => 'Apple', 2 => 'Lettuce', 3 => 'Apple' )
*/

wp_parse_id_list()

清理一组ID,如果输入是空格或逗号分隔的数组,先转成数组,再去除重复。如果是一个数组,去除重复。

$ids = array(1,1,2,3,4);
$cleaned_ids = wp_parse_id_list( $ids );

/*
 * 返回值
 * array ( 0 => 1, 2 => 2, 3 => 3, 4 => 4 )
*/

$ids = '1 2 45 67 9 9';
$cleaned_ids = wp_parse_id_list( $ids );
/*
 * 返回值
 * array ( 0 => 1, 1 => 2, 2 => 45, 3 => 67, 4 => 9 )
*/