You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于WooCommerce实现课程报名动态结账字段的需求与问题

解决方案

一、插件替代方案

直接选支持动态字段组+数量关联+订单/邮件同步的插件,推荐两款:

  • WooCommerce Multistep Checkout:用它的自定义字段模块设置「个人/企业」单选切换的条件逻辑,能根据购买数量自动生成对应数量的员工信息字段组,且默认支持把数据同步到订单后台和邮件。
  • Checkout Field Manager for WooCommerce(付费版):高级版本支持按购买数量克隆字段组,结合条件字段功能,满足批量员工信息收集需求,数据会自动同步到订单详情和邮件。

选插件时盯紧两个核心功能:1. 能根据商品数量动态生成重复字段组;2. 支持按「报名类型」切换显示对应字段。

二、混合插件+代码实现方案

如果不想换现有插件,用免费版字段编辑器配合自定义代码补全数据存储和展示逻辑,步骤如下:

1. 用插件搭基础条件切换

用免费版Checkout Field Editor添加「报名类型」单选字段(个人/企业),设置选「企业」时显示企业信息字段(企业名称、联系人、电话等)。

2. 自定义代码补全核心功能

替换你当前functions.php里的旧代码,用以下完整代码实现:动态生成员工字段、存储数据、在订单页和邮件展示数据。

// 1. 添加报名类型切换字段
add_action('woocommerce_before_checkout_billing_form', 'add_user_type_checkout_field');
function add_user_type_checkout_field() {
    woocommerce_form_field('user_type', array(
        'type' => 'radio',
        'class' => array('form-row-wide'),
        'label' => '报名类型',
        'options' => array(
            'personal' => '个人报名',
            'corporate' => '企业批量报名'
        ),
        'default' => 'personal'
    ), WC()->checkout->get_value('user_type'));
}

// 2. 根据购买数量动态生成员工信息字段
add_action('woocommerce_after_checkout_billing_form', 'dynamic_employee_fields');
function dynamic_employee_fields() {
    $cart_items = WC()->cart->get_cart();
    foreach ($cart_items as $item) {
        $quantity = $item['quantity'];
        // 仅企业报名且数量大于1时显示
        if (WC()->checkout->get_value('user_type') == 'corporate' && $quantity > 1) {
            echo '<div id="employee_fields" class="form-row">
                    <h3>员工报名信息</h3>';
            for ($i = 1; $i <= $quantity; $i++) {
                echo '<div class="employee-group form-row-wide" style="margin: 15px 0; padding: 10px; border: 1px solid #eee;">
                        <h4>员工 '.$i.'</h4>';
                woocommerce_form_field('employee_name_'.$i, array(
                    'type' => 'text',
                    'class' => array('form-row-wide'),
                    'label' => '姓名'
                ), WC()->checkout->get_value('employee_name_'.$i));
                woocommerce_form_field('employee_email_'.$i, array(
                    'type' => 'email',
                    'class' => array('form-row-wide'),
                    'label' => '邮箱'
                ), WC()->checkout->get_value('employee_email_'.$i));
                woocommerce_form_field('employee_phone_'.$i, array(
                    'type' => 'tel',
                    'class' => array('form-row-wide'),
                    'label' => '电话'
                ), WC()->checkout->get_value('employee_phone_'.$i));
                echo '</div>';
            }
            echo '</div>';
        }
    }
}

// 3. 验证企业报名时的必填字段
add_action('woocommerce_checkout_process', 'validate_employee_fields');
function validate_employee_fields() {
    if ($_POST['user_type'] == 'corporate') {
        $cart_items = WC()->cart->get_cart();
        foreach ($cart_items as $item) {
            $quantity = $item['quantity'];
            for ($i = 1; $i <= $quantity; $i++) {
                if (!isset($_POST['employee_name_'.$i]) || empty($_POST['employee_name_'.$i])) {
                    wc_add_notice('员工'.$i.'的姓名不能为空', 'error');
                }
                if (!isset($_POST['employee_email_'.$i]) || empty($_POST['employee_email_'.$i])) {
                    wc_add_notice('员工'.$i.'的邮箱不能为空', 'error');
                }
            }
        }
    }
}

// 4. 把字段数据保存到订单元数据
add_action('woocommerce_checkout_update_order_meta', 'save_employee_fields_to_order');
function save_employee_fields_to_order($order_id) {
    // 保存报名类型
    if (!empty($_POST['user_type'])) {
        update_post_meta($order_id, '_user_type', sanitize_text_field($_POST['user_type']));
    }
    // 保存企业信息(字段名要和你用插件加的一致,比如_company_name)
    if ($_POST['user_type'] == 'corporate') {
        if (!empty($_POST['company_name'])) {
            update_post_meta($order_id, '_company_name', sanitize_text_field($_POST['company_name']));
        }
        // 保存员工信息
        $cart_items = WC()->cart->get_cart();
        foreach ($cart_items as $item) {
            $quantity = $item['quantity'];
            for ($i = 1; $i <= $quantity; $i++) {
                if (!empty($_POST['employee_name_'.$i])) {
                    update_post_meta($order_id, '_employee_name_'.$i, sanitize_text_field($_POST['employee_name_'.$i]));
                }
                if (!empty($_POST['employee_email_'.$i])) {
                    update_post_meta($order_id, '_employee_email_'.$i, sanitize_email($_POST['employee_email_'.$i]));
                }
                if (!empty($_POST['employee_phone_'.$i])) {
                    update_post_meta($order_id, '_employee_phone_'.$i, sanitize_text_field($_POST['employee_phone_'.$i]));
                }
            }
        }
    }
}

// 5. 在后台订单页显示数据
add_action('woocommerce_admin_order_data_after_billing_address', 'display_employee_fields_in_admin_order');
function display_employee_fields_in_admin_order($order) {
    $user_type = get_post_meta($order->get_id(), '_user_type', true);
    if ($user_type == 'corporate') {
        echo '<div class="address">
                <h3>企业报名信息</h3>
                <p><strong>企业名称:</strong>'.get_post_meta($order->get_id(), '_company_name', true).'</p>
                <h3>员工报名详情</h3>';
        $items = $order->get_items();
        foreach ($items as $item) {
            $quantity = $item->get_quantity();
            for ($i = 1; $i <= $quantity; $i++) {
                echo '<p><strong>员工'.$i.':</strong><br>
                        姓名:'.get_post_meta($order->get_id(), '_employee_name_'.$i, true).'<br>
                        邮箱:'.get_post_meta($order->get_id(), '_employee_email_'.$i, true).'<br>
                        电话:'.get_post_meta($order->get_id(), '_employee_phone_'.$i, true).'</p>';
            }
        }
        echo '</div>';
    }
}

// 6. 在前台订单详情页显示数据
add_action('woocommerce_order_details_after_order_table', 'display_employee_fields_in_frontend_order');
function display_employee_fields_in_frontend_order($order) {
    $user_type = get_post_meta($order->get_id(), '_user_type', true);
    if ($user_type == 'corporate') {
        echo '<section class="woocommerce-order-details">
                <h2>企业报名信息</h2>
                <table class="woocommerce-table woocommerce-table--order-details shop_table order_details">
                    <tr>
                        <th>企业名称</th>
                        <td>'.get_post_meta($order->get_id(), '_company_name', true).'</td>
                    </tr>
                </table>
                <h2>员工报名详情</h2>
                <table class="woocommerce-table woocommerce-table--order-details shop_table order_details">
                    <thead>
                        <tr>
                            <th>序号</th>
                            <th>姓名</th>
                            <th>邮箱</th>
                            <th>电话</th>
                        </tr>
                    </thead>
                    <tbody>';
        $items = $order->get_items();
        foreach ($items as $item) {
            $quantity = $item->get_quantity();
            for ($i = 1; $i <= $quantity; $i++) {
                echo '<tr>
                        <td>'.$i.'</td>
                        <td>'.get_post_meta($order->get_id(), '_employee_name_'.$i, true).'</td>
                        <td>'.get_post_meta($order->get_id(), '_employee_email_'.$i, true).'</td>
                        <td>'.get_post_meta($order->get_id(), '_employee_phone_'.$i, true).'</td>
                    </tr>';
            }
        }
        echo '</tbody></table></section>';
    }
}

// 7. 在订单邮件中显示数据
add_action('woocommerce_email_after_order_table', 'display_employee_fields_in_email');
function display_employee_fields_in_email($order) {
    $user_type = get_post_meta($order->get_id(), '_user_type', true);
    if ($user_type == 'corporate') {
        echo '<h2>企业报名信息</h2>
                <p><strong>企业名称:</strong>'.get_post_meta($order->get_id(), '_company_name', true).'</p>
                <h2>员工报名详情</h2>
                <table style="width: 100%; border-collapse: collapse;">
                    <thead>
                        <tr style="border-bottom: 1px solid #eee;">
                            <th style="text-align: left; padding: 8px;">序号</th>
                            <th style="text-align: left; padding: 8px;">姓名</th>
                            <th style="text-align: left; padding: 8px;">邮箱</th>
                            <th style="text-align: left; padding: 8px;">电话</th>
                        </tr>
                    </thead>
                    <tbody>';
        $items = $order->get_items();
        foreach ($items as $item) {
            $quantity = $item->get_quantity();
            for ($i = 1; $i <= $quantity; $i++) {
                echo '<tr style="border-bottom: 1px solid #eee;">
                        <td style="padding: 8px;">'.$i.'</td>
                        <td style="padding: 8px;">'.get_post_meta($order->get_id(), '_employee_name_'.$i, true).'</td>
                        <td style="padding: 8px;">'.get_post_meta($order->get_id(), '_employee_email_'.$i, true).'</td>
                        <td style="padding: 8px;">'.get_post_meta($order->get_id(), '_employee_phone_'.$i, true).'</td>
                    </tr>';
            }
        }
        echo '</tbody></table>';
    }
}

代码说明

  • 先加「报名类型」单选框,控制企业字段和员工字段的显示逻辑
  • 根据购物车商品数量动态生成对应数量的员工信息组
  • 强制验证企业报名时的必填字段,避免漏填
  • 把所有字段数据存到订单元数据里,确保不丢失
  • 分别在后台订单页、前台订单详情、邮件里读取并展示数据

三、注意事项

  • 代码里的企业字段名(比如company_name)要和你用插件添加的字段名完全一致,不然存不了数据
  • 如果只想针对特定课程商品生成员工字段,可在代码里加商品ID判断
  • 测试时走完整下单流程,检查订单后台、用户邮件、前台订单页的数据是否正常显示

内容的提问来源于stack exchange,提问作者LesCa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:05:29