在 WooCommerce 自定义循环中按产品分类 ID 筛选产品

心靈之曲
发布: 2025-09-28 14:52:01
原创
720人浏览过

在 WooCommerce 自定义循环中按产品分类 ID 筛选产品

本文将指导您如何在 WooCommerce 自定义模板中,使用 wc_get_products 函数而非传统的 WP_Query,高效且未来兼容地按指定产品分类 ID 筛选并显示产品。文章将提供详细的步骤和代码示例,帮助开发者构建一个灵活的产品展示页面,确保您的自定义产品列表功能强大且易于维护。

理解需求:自定义产品分类显示

在构建复杂的 woocommerce 商店时,开发者经常需要创建自定义页面来展示特定分类下的产品。例如,您可能希望在一个特殊的“特价商品”页面中,只显示特定几个产品分类(如“电子产品”和“服装”)中的商品。传统上,许多开发者会倾向于使用 wp_query 来实现这一目标。然而,对于 woocommerce 产品,官方推荐使用 wc_get_products 函数,因为它提供了更好的兼容性和性能优化。

为何选择 wc_get_products?

wc_get_products 和 WC_Product_Query 提供了一种标准化的方式来检索产品,这种方式安全且不会因未来 WooCommerce 数据库结构的变化而中断。随着 WooCommerce 数据逐渐向自定义表迁移以提升性能,直接构建 WP_Query 或数据库查询很可能会在未来的 WooCommerce 版本中导致代码失效。因此,采用 wc_get_products 是一个更具前瞻性和稳健性的选择。

设置自定义产品存档模板

首先,您需要在子主题中创建一个自定义的 WooCommerce 模板文件。

  1. 将 woocommerce/archive-product.php 文件复制到您的子主题的 woocommerce 文件夹中。

  2. 将其重命名为 custom-category-archive.php(或其他您喜欢的名称)。

  3. 在该文件的顶部添加以下模板头,以便在 WordPress 页面编辑器中选择它作为页面模板:

    <?php
    /*
    Template Name: Custom Category Archive Template
    */
    ?>
    登录后复制
  4. 在 WordPress 后台创建一个新页面,并在页面属性中选择“Custom Category Archive Template”作为模板。

构建 wc_get_products 查询

现在,我们将在 custom-category-archive.php 文件中替换默认的 WooCommerce 产品循环,使用 wc_get_products 来筛选产品。

wc_get_products 函数接受一个包含查询参数的数组,类似于 WP_Query 的 $args。要按产品分类 ID 筛选,我们需要使用 tax_query 参数。

百度文心百中
百度文心百中

百度大模型语义搜索体验中心

百度文心百中 22
查看详情 百度文心百中

以下是一个基本的 wc_get_products 查询示例,用于获取指定分类 ID(例如 123, 45, 6789)下的所有产品:

<?php
defined( 'ABSPath' ) || exit;

// 确保 wc_get_products 函数存在
if ( ! function_exists( 'wc_get_products' ) ) {
    return;
}

// 获取当前的目录排序参数
$ordering            = WC()->query->get_catalog_ordering_args();
$ordering['orderby'] = array_shift( explode( ' ', $ordering['orderby'] ) );
$ordering['orderby'] = stristr( $ordering['orderby'], 'price' ) ? 'meta_value_num' : $ordering['orderby'];

// 构建 wc_get_products 查询参数
$args = array(
    'stock_status' => 'instock', // 只显示有库存的产品
    'visibility'   => 'visible', // 只显示可见产品
    'status'       => 'publish', // 只显示已发布产品
    'limit'        => -1,        // 显示所有产品,不分页
    'paginate'     => true,      // 启用分页(即使 limit 为 -1,也建议设置为 true,以便获取总数)
    'return'       => 'ids',     // 只返回产品 ID
    'orderby'      => $ordering['orderby'], // 使用商店默认排序方式
    'order'        => $ordering['order'],   // 使用商店默认排序顺序
    'tax_query'    => array(
        array(
            'taxonomy' => 'product_cat', // 分类法为产品分类
            'field'    => 'term_id',     // 根据分类 ID 筛选
            'terms'    => array( 123, 45, 6789 ), // 指定要包含的产品分类 ID 数组
            'operator' => 'IN',          // 包含在指定 ID 列表中的分类
        )
    )
);

// 执行查询
$cat_products = wc_get_products( $args );

// 设置 WooCommerce 循环属性,以便其他函数(如分页)可以访问总数
wc_set_loop_prop( 'total', $cat_products->total );
?>
登录后复制

参数解释:

  • stock_status: instock 表示只查询有库存的产品。
  • visibility: visible 表示只查询在商店和搜索结果中可见的产品。
  • status: publish 表示只查询已发布的产品。
  • limit: -1 表示不限制产品数量。如果需要分页,可以设置为每页显示的数量。
  • paginate: true 表示启用分页。当 return 为 ids 或 objects 时,这将返回一个 WC_Product_Query 对象,其中包含 total 属性。
  • return: ids 表示只返回匹配产品的 ID 数组。这有助于减少内存消耗。
  • orderby 和 order: 用于设置产品的排序方式,这里使用了 WooCommerce 商店的默认排序设置。
  • tax_query: 这是关键参数,用于根据分类法进行筛选。
    • taxonomy: product_cat 指定要查询的产品分类法。
    • field: term_id 指定根据分类的 ID 进行匹配。
    • terms: array( 123, 45, 6789 ) 是一个包含您希望显示的产品分类 ID 的数组。请替换为您的实际分类 ID。
    • operator: IN 表示产品必须属于 terms 数组中的任何一个分类。

实现自定义产品循环

获取到产品 ID 列表后,我们需要遍历这些 ID,并使用 WooCommerce 的标准钩子来显示每个产品。

将以下代码整合到您的 custom-category-archive.php 文件中,替换掉原有的 while ( have_posts() ) 循环部分:

<?php
// ... (之前的 wc_get_products 查询代码) ...

get_header( 'shop' ); // 获取商店头部
do_action( 'woocommerce_before_main_content' ); // WooCommerce 主内容前钩子
?>

<?php if ( $cat_products && $cat_products->products ) : ?>

    <?php do_action( 'woocommerce_before_shop_loop' ); // 商店循环前钩子 ?>

    <div id="container" class="products columns-<?php echo esc_attr( wc_get_loop_prop( 'columns' ) ); ?>">

        <?php foreach ( $cat_products->products as $product_id ) : ?>

            <?php
            // 获取产品对象并设置全局 $post 变量
            $post_object = get_post( $product_id );
            setup_postdata( $GLOBALS['post'] =& $post_object );
            $product = wc_get_product( $product_id ); // 获取 WC_Product 对象
            ?>

            <div <?php wc_product_class( '', $product ); ?>>
                <?php
                /**
                 * 钩子: woocommerce_before_shop_loop_item.
                 *
                 * @hooked woocommerce_template_loop_product_link_open - 10
                 */
                do_action( 'woocommerce_before_shop_loop_item' );

                /**
                 * 钩子: woocommerce_before_shop_loop_item_title.
                 *
                 * @hooked woocommerce_show_product_loop_sale_flash - 10
                 * @hooked woocommerce_template_loop_product_thumbnail - 10
                 */
                do_action( 'woocommerce_before_shop_loop_item_title' );

                /**
                 * 钩子: woocommerce_shop_loop_item_title.
                 *
                 * @hooked woocommerce_template_loop_product_title - 10
                 */
                do_action( 'woocommerce_shop_loop_item_title' );

                /**
                 * 钩子: woocommerce_after_shop_loop_item_title.
                 *
                 * @hooked woocommerce_template_loop_rating - 5
                 * @hooked woocommerce_template_loop_price - 10
                 */
                do_action( 'woocommerce_after_shop_loop_item_title' );

                /**
                 * 钩子: woocommerce_after_shop_loop_item.
                 *
                 * @hooked woocommerce_template_loop_add_to_cart - 10
                 * @hooked woocommerce_template_loop_product_link_close - 10
                 */
                do_action( 'woocommerce_after_shop_loop_item' );
                ?>
            </div>

        <?php endforeach; ?>

        <?php wp_reset_postdata(); // 重置全局 $post 变量 ?>

    </div><!-- #container -->

    <?php do_action( 'woocommerce_after_shop_loop' ); // 商店循环后钩子 ?>

<?php else : ?>

    <?php do_action( 'woocommerce_no_products_found' ); // 未找到产品时钩子 ?>

<?php endif; ?>

<?php
do_action( 'woocommerce_after_main_content' ); // WooCommerce 主内容后钩子
get_footer( 'shop' ); // 获取商店底部
?>
登录后复制

代码解释:

  1. get_header('shop') 和 get_footer('shop'): 引入 WooCommerce 商店的头部和底部。
  2. do_action('woocommerce_before_main_content') / woocommerce_after_main_content: 这是 WooCommerce 的主要内容区域钩子,确保您的内容被正确包裹。
  3. if ( $cat_products && $cat_products->products ): 检查查询结果是否有效且包含产品。
  4. do_action('woocommerce_before_shop_loop') / woocommerce_after_shop_loop: 这些钩子用于在产品列表前后输出内容,例如排序选项、结果计数或分页。
  5. foreach ( $cat_products->products as $product_id ): 遍历 wc_get_products 返回的产品 ID 数组。
  6. get_post( $product_id ) 和 setup_postdata( $GLOBALS['post'] =& $post_object ): 这是非常关键的步骤。由于我们是手动循环产品 ID,WordPress 的全局 $post 对象不会自动更新。setup_postdata() 会为当前迭代的产品设置全局 $post 变量,使得像 the_title()、the_permalink() 等标准的 WordPress 模板函数能够正常工作。
  7. wc_get_product( $product_id ): 获取当前产品的 WC_Product 对象,这对于使用 wc_product_class() 等 WooCommerce 特定函数是必需的。
  8. wc_product_class('', $product): 输出产品项的 CSS 类,确保样式正确应用。
  9. do_action(...): 这一系列 do_action 钩子是 WooCommerce 产品循环的核心。它们允许主题和插件在产品列表项的不同位置(如产品图片、标题、价格、添加到购物车按钮等)插入自定义内容。通过保留这些钩子,您的自定义循环将与大多数 WooCommerce 插件和主题保持良好的兼容性。
  10. wp_reset_postdata(): 在 foreach 循环结束后,务必调用 wp_reset_postdata() 来恢复全局 $post 变量到主查询的状态,以避免对页面其他部分的潜在影响。
  11. do_action('woocommerce_no_products_found'): 如果没有找到产品,这个钩子可以显示一个“未找到产品”的消息。

完整代码示例

将上述所有代码片段组合起来,您的 custom-category-archive.php 文件将如下所示:

<?php
/*
Template Name: Custom Category Archive Template
*/
defined( 'ABSPath' ) || exit;

get_header( 'shop' );

do_action( 'woocommerce_before_main_content' );

// 确保 wc_get_products 函数存在
if ( ! function_exists( 'wc_get_products' ) ) {
    do_action( 'woocommerce_no_products_found' ); // 如果函数不存在,显示未找到产品
    do_action( 'woocommerce_after_main_content' );
    get_footer( 'shop' );
    return;
}

// 获取当前的目录排序参数
$ordering            = WC()->query->get_catalog_ordering_args();
$ordering['orderby'] = array_shift( explode( ' ', $ordering['orderby'] ) );
$ordering['orderby'] = stristr( $ordering['orderby'], 'price' ) ? 'meta_value_num' : $ordering['orderby'];

// 构建 wc_get_products 查询参数
$args = array(
    'stock_status' => 'instock',
    'visibility'   => 'visible',
    'status'       => 'publish',
    'limit'        => -1, // -1 表示不限制数量,显示所有匹配产品
    'paginate'     => true,
    'return'       => 'ids', // 只返回产品 ID
    'orderby'      => $ordering['orderby'],
    'order'        => $ordering['order'],
    'tax_query'    => array(
        array(
            'taxonomy' => 'product_cat',
            'field'    => 'term_id',
            'terms'    => array( 123, 45, 6789 ), // 替换为您的产品分类 ID
            'operator' => 'IN',
        )
    )
);

// 执行查询
$cat_products = wc_get_products( $args );

// 设置 WooCommerce 循环属性,以便其他函数(如分页)可以访问总数
wc_set_loop_prop( 'total', $cat_products->total );

if ( $cat_products && $cat_products->products ) : ?>

    <?php do_action( 'woocommerce_before_shop_loop' ); ?>

    <div id="container" class="products columns-<?php echo esc_attr( wc_get_loop_prop( 'columns' ) ); ?>">

        <?php foreach ( $cat_products->products as $product_id ) : ?>

            <?php
            $post_object = get_post( $product_id );
            setup_postdata( $GLOBALS['post'] =& $post_object );
            $product = wc_get_product( $product_id ); // 获取 WC_Product 对象
            ?>

            <div <?php wc_product_class( '', $product ); ?>>
                <?php
                do_action( 'woocommerce_before_shop_loop_item' );
                do_action( 'woocommerce_before_shop_loop_item_title' );
                do_action( 'woocommerce_shop_loop_item_title' );
                do_action( 'woocommerce_after_shop_loop_item_title' );
                do_action( 'woocommerce_after_shop_loop_item' );
                ?>
            </div>

        <?php endforeach; ?>

        <?php wp_reset_postdata(); ?>

    </div><!-- #container -->

    <?php do_action( 'woocommerce_after_shop_loop' ); ?>

<?php else : ?>

    <?php do_action( 'woocommerce_no_products_found' ); ?>

<?php endif;

do_action( 'woocommerce_after_main_content' );

get_footer( 'shop' );
登录后复制

注意事项

  • 分类 ID 的获取:您可以在 WordPress 后台进入“产品”->“分类”,编辑某个分类时,查看浏览器地址栏中的 tag_ID 参数来获取分类 ID。
  • 性能优化:如果产品数量非常大,limit => -1 可能会导致性能问题。在这种情况下,您应该考虑实现分页 (paginate => true 并结合 paged 参数) 或限制显示数量。
  • 兼容性:此方法利用了 WooCommerce 的核心功能和钩子,因此与大多数遵循 WooCommerce 标准的主题和插件兼容性良好。
  • 动态分类:如果需要根据用户交互或其他条件动态选择分类,您可以通过 $_GET 参数或自定义字段来获取分类 ID,并将其传递给 terms 数组。
  • 错误处理:在代码开头添加 if ( ! function_exists( 'wc_get_products' ) ) { ... } 可以防止 WooCommerce 未激活时出现致命错误。

总结

通过采用 wc_get_products 函数并结合 tax_query 参数,您可以安全、高效地在 WooCommerce 自定义模板中按产品分类 ID 筛选和显示产品。这种方法不仅保证了代码的未来兼容性,还通过集成 WooCommerce 的标准钩子,确保了与现有生态系统的良好协作。遵循本教程的步骤,您将能够构建出功能强大且维护友好的自定义产品列表页面。

以上就是在 WooCommerce 自定义循环中按产品分类 ID 筛选产品的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号