如何将Gravity Form动态创建字段保存至表单提交条目
- 基础场景:通过
gform_pre_render_5、gform_pre_validation_5钩子为ID为5的Gravity Form动态添加选择字段,提交后动态字段值可以正常出现在通知邮件中,但不会被保存到表单entries(条目)数据中,仅后台手动创建的字段会被存入条目,需要实现动态字段值同步写入条目。 - 扩展场景:单动态字段的存值方案可正常运行,但改为循环批量生成葡萄酒选择字段后方案失效,需要排查问题原因。
基础场景实现代码
add_filter('gform_pre_render_5', 'populate_wines'); add_filter('gform_pre_validation_5', 'populate_wines'); function populate_wines($form) { // 创建动态选择字段 $props = array( 'id' => 51, 'type' => 'select', 'label' => 'Dynamic field label', 'choices' => array( array( 'text' => '', 'value' => '', ), array( 'text' => '1', 'value' => '1', ), array( 'text' => '2', 'value' => '2', ), array( 'text' => '3', 'value' => '3', ), ) ); $new_field = GF_Fields::create( $props ); $form['fields'][] = $new_field; return $form; }
循环批量生成字段的失效代码
function populate_wines($form) { // 选择项配置 $select_choices = array( array( 'text' => '', 'value' => '', ), array( 'text' => '1', 'value' => '1', ), array( 'text' => '2', 'value' => '2', ), array( 'text' => '3', 'value' => '3', ), ); // 循环生成葡萄酒选择字段 if( have_rows('wine_options') ): $wine_count = 50; while( have_rows('wine_options') ) : the_row(); $wine_ID = get_sub_field('wine_option'); $wine_name = get_the_title($wine_ID); // 创建单个葡萄酒选择字段 $props = array( 'id' => $wine_count, 'type' => 'select', 'label' => $wine_name, 'choices' => $select_choices ); $new_field = GF_Fields::create( $props ); $form['fields'][] = $new_field; $wine_count++; endwhile; endif; if ( GFForms::get_page() !== 'form_editor' ) { return $form; } }
动态字段不存入条目的核心原因
仅挂载gform_pre_render、gform_pre_validation两个钩子只能覆盖前端渲染、提交校验两个流程,Gravity Forms在提交入库、后台加载条目结构时,无法识别到动态添加的字段,自然不会把对应值写入条目表。
单字段场景修复方式
需要把动态注入字段的逻辑同时挂载到以下4个钩子,覆盖表单全生命周期的结构加载节点:
gform_pre_render_{$form_id}:前端表单渲染gform_pre_validation_{$form_id}:提交前校验gform_pre_submission_{$form_id}:提交入库前字段识别gform_admin_pre_render_{$form_id}:后台条目列表、详情页加载字段结构
GF识别到字段存在后,会自动把提交值写入条目表,不需要额外编写入库逻辑。
循环生成字段失效的排查与修复
循环版本失效是3个代码逻辑错误导致的,按优先级修复即可:
Return逻辑完全写反
原代码在所有字段生成完成后,仅当上下文不是表单编辑器时才返回$form,如果是表单编辑器页面、或者提交流程的其他上下文,函数没有返回值,会直接导致表单结构丢失。
正确写法是把动态生成字段的逻辑包裹在「非表单编辑器」的判断中,函数末尾无条件返回$form。ACF字段取值上下文缺失
前端渲染页面时存在全局$post对象,have_rows('wine_options')可以正常取到值,但表单提交走的是admin-post.php/admin-ajax.php接口,没有全局文章上下文,have_rows会直接返回false,根本不会生成任何动态字段。
修复方式是给have_rows传入第二个参数,明确指定wine_options字段所属的对象ID,如果是选项页存的配置就传'option',如果是固定页面存的配置就传对应页面ID。字段ID冲突风险
循环起始ID设为50,需要先确认表单内手动创建的字段ID没有占用50及之后的号段,建议把起始ID设为比现有字段最大ID大10以上的数值,避免同ID字段被覆盖导致值无法匹配。
修复后可正常运行的循环代码示例
// 挂载全流程钩子 add_filter('gform_pre_render_5', 'populate_wines'); add_filter('gform_pre_validation_5', 'populate_wines'); add_filter('gform_pre_submission_5', 'populate_wines'); add_filter('gform_admin_pre_render_5', 'populate_wines'); function populate_wines($form) { // 表单编辑器页面不注入动态字段,避免破坏原有表单结构 if ( GFForms::get_page() === 'form_editor' ) { return $form; } // 选择项配置 $select_choices = array( array( 'text' => '', 'value' => '', ), array( 'text' => '1', 'value' => '1', ), array( 'text' => '2', 'value' => '2', ), array( 'text' => '3', 'value' => '3', ), ); // 替换为你的wine_options所属对象ID,选项页填'option',固定页面填对应页面ID $acf_source_id = 'option'; $wine_count = 100; // 起始ID设为100,避开原有字段ID段 // 循环生成字段,明确传入ACF数据源ID if( have_rows('wine_options', $acf_source_id) ): while( have_rows('wine_options', $acf_source_id) ) : the_row(); $wine_ID = get_sub_field('wine_option'); $wine_name = get_the_title($wine_ID); $props = array( 'id' => $wine_count, 'type' => 'select', 'label' => $wine_name, 'choices' => $select_choices ); $new_field = GF_Fields::create( $props ); $form['fields'][] = $new_field; $wine_count++; endwhile; endif; // 始终返回表单结构 return $form; }
内容的提问来源于stack exchange,提问作者deeve007

