WooCommerce添加自定义缺货状态并识别为原生缺货的实现方法
我创建了2个新的自定义库存状态:
- Out of stock (Permanent)(永久缺货)
- Out of stock (Supplier)(供应商原因缺货)
核心需求:让这两个自定义状态被WooCommerce系统完全按照原生缺货状态处理。
目前已经通过代码实现了前端商品目录中隐藏这两个状态的商品,但仍存在两个未解决的问题:
- Flatsome主题自带的Ajax实时搜索结果中仍会展示这类商品
- 用户仍然可以将该类商品加入购物车
现有实现代码参考了WooCommerce自定义库存状态、隐藏指定库存状态商品的相关教程,当前代码如下:
// Add new stock status options function filter_woocommerce_product_stock_status_options( $status ) { // Add new statuses $status['permanent'] = __( 'Out of stock (Permanent)', 'woocommerce' ); $status['supplier'] = __( 'Out of stock (Supplier)', 'woocommerce' ); return $status; } add_filter( 'woocommerce_product_stock_status_options', 'filter_woocommerce_product_stock_status_options', 10, 1 ); // Availability text function filter_woocommerce_get_availability_text( $availability, $product ) { // Get stock status switch( $product->get_stock_status() ) { case 'permanent': $availability = __( 'Out of stock (Permanent)', 'woocommerce' ); break; case 'supplier': $availability = __( 'Out of stock (Supplier)', 'woocommerce' ); break; } return $availability; } add_filter( 'woocommerce_get_availability_text', 'filter_woocommerce_get_availability_text', 10, 2 ); // Availability CSS class function filter_woocommerce_get_availability_class( $class, $product ) { // Get stock status switch( $product->get_stock_status() ) { case 'permanent': $class = 'permanent'; break; case 'supplier': $class = 'supplier'; break; } return $class; } add_filter( 'woocommerce_get_availability_class', 'filter_woocommerce_get_availability_class', 10, 2 ); // Admin stock html function filter_woocommerce_admin_stock_html( $stock_html, $product ) { // Simple if ( $product->is_type( 'simple' ) ) { // Get stock status $product_stock_status = $product->get_stock_status(); // Variable } elseif ( $product->is_type( 'variable' ) ) { foreach( $product->get_visible_children() as $variation_id ) { // Get product $variation = wc_get_product( $variation_id ); // Get stock status $product_stock_status = $variation->get_stock_status(); } } // Stock status switch( $product_stock_status ) { case 'permanent': $stock_html = '<mark class="permanent" style="background:transparent none;color:#33ccff;font-weight:700;line-height:1;">' . __( 'Out of stock (Permanent)', 'woocommerce' ) . '</mark>'; break; case 'supplier': $stock_html = '<mark class="supplier" style="background:transparent none;color:#cc33ff;font-weight:700;line-height:1;">' . __( 'Out of stock (Supplier)', 'woocommerce' ) . '</mark>'; break; } return $stock_html; } add_filter( 'woocommerce_admin_stock_html', 'filter_woocommerce_admin_stock_html', 10, 2 ); //hide specific stock status add_action( 'woocommerce_product_query_meta_query', 'custom_product_query_meta_query', 1000 ); function custom_product_query_meta_query( $meta_query ) { if ( ! is_admin() ) { $meta_query[] = array( 'key' => '_stock_status', 'value' => 'permanent', 'compare' => '!=', ); } if ( ! is_admin() ) { $meta_query[] = array( 'key' => '_stock_status', 'value' => 'supplier', 'compare' => '!=', ); } return $meta_query; }
你现有代码仅覆盖了WooCommerce原生商品列表页的查询过滤,没有触及商品可购买判断的核心钩子,也没有覆盖Flatsome Ajax搜索使用的自定义查询逻辑,所以会出现搜索结果漏出、可加购的问题,补充以下代码即可实现和原生缺货一致的处理逻辑:
注意:原有代码中custom_product_query_meta_query对应的隐藏商品代码可以直接删除,新的全局过滤代码已经完全覆盖其功能,且适配场景更广。
1. 拦截自定义缺货状态商品的购买权限
WooCommerce判断商品是否可售核心走woocommerce_is_purchasable过滤器,加入以下代码后,不管是商品页加购、Ajax加购还是购物车手动添加,都会被拦截:
add_filter('woocommerce_is_purchasable', 'custom_stock_status_not_purchasable', 99, 2); function custom_stock_status_not_purchasable($purchasable, $product) { $custom_outofstock_status = ['permanent', 'supplier']; if (in_array($product->get_stock_status(), $custom_outofstock_status)) { $purchasable = false; } // 兼容可变商品:对应缺货状态的变体不可购买 if ($product->is_type('variable')) { add_filter('woocommerce_variation_is_purchasable', function($purchasable, $variation) use ($custom_outofstock_status) { return in_array($variation->get_stock_status(), $custom_outofstock_status) ? false : $purchasable; }, 99, 2); } return $purchasable; }
2. 全局过滤前端所有商品查询,覆盖Flatsome Ajax搜索
Flatsome的Ajax搜索走自定义WP_Query接口,原有的WooCommerce商品查询钩子不生效,需要使用更底层的pre_get_posts钩子做全局过滤,同时覆盖商品列表、搜索结果、相关商品、上下架商品推荐等所有前端商品展示场景:
add_action('pre_get_posts', 'hide_custom_outofstock_products_globally', 999); function hide_custom_outofstock_products_globally($query) { // 后台查询不拦截,保证管理员可正常管理商品 if (is_admin()) return; // 仅对商品、商品变体相关查询生效 $post_type = $query->get('post_type'); if ( (is_array($post_type) && array_intersect(['product', 'product_variation'], $post_type)) || $post_type === 'product' || $post_type === 'product_variation' || $query->is_search() || $query->is_shop() || $query->is_product_taxonomy() ) { $meta_query = $query->get('meta_query', []); $meta_query[] = [ 'key' => '_stock_status', 'value' => ['permanent', 'supplier'], 'compare' => 'NOT IN' ]; $query->set('meta_query', $meta_query); } }
3. 兼容原生缺货判断逻辑(可选,适配第三方插件/主题)
如果需要让系统所有涉及缺货判断的逻辑都把这两个自定义状态识别为缺货,可以加入以下代码,覆盖绝大多数第三方插件、主题的兼容判断:
add_filter('woocommerce_product_is_out_of_stock', 'mark_custom_status_as_outofstock', 99, 2); function mark_custom_status_as_outofstock($is_outofstock, $product) { if (in_array($product->get_stock_status(), ['permanent', 'supplier'])) { $is_outofstock = true; } return $is_outofstock; }
样式优化(可选)
如果需要让自定义缺货状态的前端提示样式和原生缺货完全一致,可以把原有filter_woocommerce_get_availability_class函数中返回的自定义class改为out-of-stock,即可自动沿用主题原生缺货的样式,不需要额外编写CSS。
内容的提问来源于stack exchange,提问作者Ares

