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

基于产品及购票数量的WooCommerce自定义结账字段开发需求

嘿,针对你这个WooCommerce可变产品门票的参会人字段需求,我给你整理了一套完整的实现方案,直接用代码就能落地,还预留了扩展空间,一起来看看:

实现WooCommerce结账页动态生成参会人字段组

核心思路

根据购物车中门票的总数量,在结账页自动渲染对应数量的参会人信息字段组,完成表单验证后把数据绑定到订单中,同时支持后台查看这些信息。


完整代码实现

你可以把这些代码放到主题的functions.php文件里,或者做成独立的自定义插件(更推荐,避免主题更新丢失代码):

1. 计算购物车中门票总数量

function get_total_ticket_quantity() {
    $total = 0;
    foreach (WC()->cart->get_cart() as $cart_item) {
        // 这里可以自定义判断逻辑:比如检查产品是否属于"门票"分类/标签
        if ($cart_item['data']->is_type('variable')) {
            $total += $cart_item['quantity'];
        }
    }
    return $total;
}

2. 在结账页渲染参会人字段组

function render_attendee_fields() {
    $total_tickets = get_total_ticket_quantity();
    if ($total_tickets <= 0) return;

    echo '<div id="attendee-fields" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">';
    echo '<h3>' . __('参会人信息', 'woocommerce') . '</h3>';

    for ($i = 0; $i < $total_tickets; $i++) {
        echo '<div class="attendee-group" style="margin: 20px 0; padding: 15px; border: 1px solid #eee;">';
        echo '<h4>' . __('参会人 #' . ($i+1), 'woocommerce') . '</h4>';

        // 称谓下拉框
        woocommerce_form_field(
            "attendee[$i][title]",
            array(
                'type' => 'select',
                'label' => __('称谓', 'woocommerce'),
                'required' => true,
                'options' => array(
                    '' => __('请选择', 'woocommerce'),
                    'Mr' => __('先生', 'woocommerce'),
                    'Ms' => __('女士', 'woocommerce'),
                    'Dr' => __('博士', 'woocommerce'),
                    'Prof' => __('教授', 'woocommerce')
                )
            ),
            WC()->checkout->get_value("attendee[$i][title]")
        );

        // 姓名输入框
        woocommerce_form_field(
            "attendee[$i][name]",
            array(
                'type' => 'text',
                'label' => __('姓名', 'woocommerce'),
                'required' => true,
                'placeholder' => __('请输入姓名', 'woocommerce')
            ),
            WC()->checkout->get_value("attendee[$i][name]")
        );

        // 机构输入框
        woocommerce_form_field(
            "attendee[$i][organisation]",
            array(
                'type' => 'text',
                'label' => __('机构', 'woocommerce'),
                'required' => true,
                'placeholder' => __('请输入机构名称', 'woocommerce')
            ),
            WC()->checkout->get_value("attendee[$i][organisation]")
        );

        // 职位输入框
        woocommerce_form_field(
            "attendee[$i][position]",
            array(
                'type' => 'text',
                'label' => __('职位', 'woocommerce'),
                'required' => true,
                'placeholder' => __('请输入职位', 'woocommerce')
            ),
            WC()->checkout->get_value("attendee[$i][position]")
        );

        // 邮箱输入框
        woocommerce_form_field(
            "attendee[$i][email]",
            array(
                'type' => 'email',
                'label' => __('邮箱', 'woocommerce'),
                'required' => true,
                'placeholder' => __('请输入邮箱', 'woocommerce')
            ),
            WC()->checkout->get_value("attendee[$i][email]")
        );

        // 电话输入框
        woocommerce_form_field(
            "attendee[$i][tel]",
            array(
                'type' => 'tel',
                'label' => __('电话', 'woocommerce'),
                'required' => true,
                'placeholder' => __('请输入电话', 'woocommerce')
            ),
            WC()->checkout->get_value("attendee[$i][tel]")
        );

        echo '</div>';
    }

    echo '</div>';
}
add_action('woocommerce_checkout_after_customer_details', 'render_attendee_fields');

3. 表单验证(确保必填字段都填写)

function validate_attendee_fields() {
    $total_tickets = get_total_ticket_quantity();
    if ($total_tickets <= 0) return;

    if (!isset($_POST['attendee']) || !is_array($_POST['attendee'])) {
        wc_add_notice(__('请填写所有参会人信息', 'woocommerce'), 'error');
        return;
    }

    foreach ($_POST['attendee'] as $index => $attendee) {
        if (empty($attendee['title'])) {
            wc_add_notice(__('参会人 #' . ($index+1) . ' 的称谓不能为空', 'woocommerce'), 'error');
        }
        if (empty($attendee['name'])) {
            wc_add_notice(__('参会人 #' . ($index+1) . ' 的姓名不能为空', 'woocommerce'), 'error');
        }
        if (empty($attendee['organisation'])) {
            wc_add_notice(__('参会人 #' . ($index+1) . ' 的机构不能为空', 'woocommerce'), 'error');
        }
        if (empty($attendee['position'])) {
            wc_add_notice(__('参会人 #' . ($index+1) . ' 的职位不能为空', 'woocommerce'), 'error');
        }
        if (empty($attendee['email']) || !is_email($attendee['email'])) {
            wc_add_notice(__('参会人 #' . ($index+1) . ' 的邮箱格式不正确', 'woocommerce'), 'error');
        }
        if (empty($attendee['tel'])) {
            wc_add_notice(__('参会人 #' . ($index+1) . ' 的电话不能为空', 'woocommerce'), 'error');
        }
    }
}
add_action('woocommerce_checkout_process', 'validate_attendee_fields');

4. 保存参会人数据到订单

function save_attendee_data($order_id) {
    if (isset($_POST['attendee']) && is_array($_POST['attendee'])) {
        $attendees = array_map(function($attendee) {
            return array_filter($attendee); // 过滤空值
        }, $_POST['attendee']);
        update_post_meta($order_id, '_attendee_details', $attendees);
    }
}
add_action('woocommerce_checkout_update_order_meta', 'save_attendee_data');

5. 在后台订单页展示参会人信息

function display_attendee_data_in_admin($order) {
    $attendees = get_post_meta($order->get_id(), '_attendee_details', true);
    if (!empty($attendees) && is_array($attendees)) {
        echo '<div class="attendee-details">';
        echo '<h3>' . __('参会人信息', 'woocommerce') . '</h3>';
        echo '<table class="widefat fixed">';
        echo '<thead><tr><th>序号</th><th>称谓</th><th>姓名</th><th>机构</th><th>职位</th><th>邮箱</th><th>电话</th></tr></thead>';
        echo '<tbody>';
        foreach ($attendees as $index => $attendee) {
            echo '<tr>';
            echo '<td>' . ($index+1) . '</td>';
            echo '<td>' . esc_html($attendee['title']) . '</td>';
            echo '<td>' . esc_html($attendee['name']) . '</td>';
            echo '<td>' . esc_html($attendee['organisation']) . '</td>';
            echo '<td>' . esc_html($attendee['position']) . '</td>';
            echo '<td>' . esc_html($attendee['email']) . '</td>';
            echo '<td>' . esc_html($attendee['tel']) . '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
        echo '</div>';
    }
}
add_action('woocommerce_admin_order_data_after_billing_address', 'display_attendee_data_in_admin', 10, 1);

扩展性建议

如果后续需要扩展功能,可以从这几个方向入手:

  • 可配置字段:添加WooCommerce后台设置页面,让商家自定义需要显示的字段(比如要不要"饮食偏好"字段),用get_option获取配置动态生成字段。
  • 门票类型区分:针对不同类型的门票(比如VIP/普通票),生成不同的字段组(比如VIP票额外添加"住宿需求"字段)。
  • AJAX动态更新:用户在结账页修改购物车数量时,实时更新参会人字段组,不用刷新页面。
  • 数据导出:添加功能把订单中的参会人信息导出为CSV,方便活动签到统计。
  • 邮件通知:在订单确认邮件中插入参会人信息,同步发送给用户和管理员。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:01:03