如何使用含table、dropdown字段的matrix搭建CraftCMS前端表单
问题排查与实现方案
报错原因
你遇到的Impossible to access an attribute ("settings") on a null variable.错误来自三个核心问题:
courseTitle是属于Matrix Block Type(名称为course)下的子字段,不是站点全局的顶级字段,直接用craft.fields.getFieldbyHandle('courseTitle')获取不到对应字段对象,返回为空,调用settings就会触发报错- 你在for循环内部重定义了
entry变量,会覆盖外层的entry对象,导致后续调用异常 - Matrix子字段的提交参数格式错误,你把courseTitle写成了顶级字段,实际上它属于Matrix block的内部字段,参数层级不对
修正后的实现代码
以下代码同时兼容已有Matrix块的编辑、新Matrix块的提交,以及table字段内dropdown列的渲染:
{# 预先定义目标entry,避免循环内变量覆盖 #} {% set targetEntry = entry ?? create('craft\\elements\\Entry') %} {# 遍历已有Matrix块 #} {% for block in targetEntry.course.all() %} {% if block.type.handle == 'course' %} <form method="post" accept-charset="UTF-8"> {{ csrfInput() }} <input type="hidden" name="action" value="entries/save-entry"> <input type="hidden" name="sectionId" value="1"> <input type="hidden" name="enabled" value="1"> <input type="hidden" name="entryId" value="{{ targetEntry.id ?? '' }}"> {# Matrix字段排序、类型参数定义 #} <input type="hidden" name="fields[course][sort][]" value="{{ block.id ?? 'new1' }}"> <input type="hidden" name="fields[course][{{ block.id ?? 'new1' }}][type]" value="course"> <input type="hidden" name="fields[course][{{ block.id ?? 'new1' }}][enabled]" value="1"> {# 正确获取Matrix Block下的courseTitle字段 #} {% set matrixBlockType = craft.app.matrix.getBlockTypeByHandle('course') %} {% set dropdownField = matrixBlockType.getFieldByHandle('courseTitle') %} <select class="form-control" id="courseTitle" name="fields[course][{{ block.id ?? 'new1' }}][fields][courseTitle]"> {% for option in dropdownField.settings.options %} <option {{ block.courseTitle.value == option.value ? 'selected' : '' }} value="{{ option.value }}">{{ option.label }}</option> {% endfor %} </select> {# 渲染table类型字段stage1CourseBookingForm #} {% set tableField = matrixBlockType.getFieldByHandle('stage1CourseBookingForm') %} {% set tableData = block.stage1CourseBookingForm ?? [] %} <table class="form-table"> <thead> <tr> {% for col in tableField.settings.columns %} <th>{{ col.heading }}</th> {% endfor %} </tr> </thead> <tbody> {% for row in tableData %} <tr> {% for col in tableField.settings.columns %} <td> {% if col.handle == 'courseType' %} {# 渲染table内的dropdown列 #} <select name="fields[course][{{ block.id ?? 'new1' }}][fields][stage1CourseBookingForm][{{ loop.parent.loop.index0 }}][{{ col.handle }}]"> {% for option in col.options %} <option {{ row[col.handle] == option.value ? 'selected' : '' }} value="{{ option.value }}">{{ option.label }}</option> {% endfor %} </select> {% else %} <input type="text" name="fields[course][{{ block.id ?? 'new1' }}][fields][stage1CourseBookingForm][{{ loop.parent.loop.index0 }}][{{ col.handle }}]" value="{{ row[col.handle] ?? '' }}"> {% endif %} </td> {% endfor %} </tr> {% endfor %} </tbody> </table> <button type="submit">Publish</button> </form> {% endif %} {% endfor %} {# 新增entry无Matrix块的场景,渲染空表单 #} {% if targetEntry.course.count() == 0 %} <form method="post" accept-charset="UTF-8"> {{ csrfInput() }} <input type="hidden" name="action" value="entries/save-entry"> <input type="hidden" name="sectionId" value="1"> <input type="hidden" name="enabled" value="1"> <input type="hidden" name="entryId" value="{{ targetEntry.id ?? '' }}"> <input type="hidden" name="fields[course][sort][]" value="new1"> <input type="hidden" name="fields[course][new1][type]" value="course"> <input type="hidden" name="fields[course][new1][enabled]" value="1"> {% set matrixBlockType = craft.app.matrix.getBlockTypeByHandle('course') %} {% set dropdownField = matrixBlockType.getFieldByHandle('courseTitle') %} <select class="form-control" id="courseTitle" name="fields[course][new1][fields][courseTitle]"> {% for option in dropdownField.settings.options %} <option value="{{ option.value }}">{{ option.label }}</option> {% endfor %} </select> {% set tableField = matrixBlockType.getFieldByHandle('stage1CourseBookingForm') %} <table class="form-table"> <thead> <tr> {% for col in tableField.settings.columns %} <th>{{ col.heading }}</th> {% endfor %} </tr> </thead> <tbody> <tr> {% for col in tableField.settings.columns %} <td> {% if col.handle == 'courseType' %} <select name="fields[course][new1][fields][stage1CourseBookingForm][0][{{ col.handle }}]"> {% for option in col.options %} <option value="{{ option.value }}">{{ option.label }}</option> {% endfor %} </select> {% else %} <input type="text" name="fields[course][new1][fields][stage1CourseBookingForm][0][{{ col.handle }}]" value=""> {% endif %} </td> {% endfor %} </tr> </tbody> </table> <button type="submit">Publish</button> </form> {% endif %}
注意事项
- 遍历Matrix字段时必须调用
.all()方法,否则返回的是查询对象不是block列表 - 所有Matrix内的子字段提交参数必须嵌套在
fields[course][blockID][fields][字段名]层级下,不能直接写为顶级fields参数 - 若允许访客提交表单,需要配置对应section的访客提交权限,否则会触发权限错误
- Table字段的提交参数是二维数组格式,第一层是行索引,第二层是列handle
内容的提问来源于stack exchange,提问作者SynecdocheNORTH
相关产品推荐
相关产品推荐

