公司动态
深入 Rectify 嵌套表单:一个 valid? 方法搞定多级表单验证难题
深入 Rectify 嵌套表单一个 valid? 方法搞定多级表单验证难题【免费下载链接】rectifyBuild maintainable Rails apps项目地址: https://gitcode.com/gh_mirrors/re/rectify在 Rails 应用开发中Rectify 嵌套表单验证是一个让人又爱又恨的话题。Rectify 是一个轻量级 Ruby gem主打构建可维护的 Rails 应用它的四大组件Form Object、Command、Presenter、Query Object中**Form Object表单对象**最能体现其价值把验证逻辑从模型和控制器中剥离出来。这篇文章带你深入 Rectify 的嵌套表单机制——只需一个valid?方法就能递归验证多级表单彻底告别手动逐层校验的繁琐代码。为什么 Rails 新手总被嵌套表单验证难住想象一个注册页面用户信息 地址 多个联系人。传统做法通常是在模型里堆条件验证不同场景不同规则在控制器里逐个检查user.address.valid?、user.contacts.each……代码散落在各处测试也痛苦不堪。Rectify 的思路是每个上下文用独立的 Form Object 承载输入数据与验证模型只管数据关系控制器只管 HTTP。 架构分工一览数据输入 → Form Object业务逻辑 → Command视图逻辑 → Presenter数据库查询 → Query Object。3 步定义一个嵌套表单父表单 子表单 表单数组Rectify 的表单对象基于 Virtus 构建属性声明非常直观。以下示例来自项目的测试夹具class UserForm Rectify::Form attribute :first_name, String attribute :address, AddressForm # 嵌套表单对象 attribute :contacts, Array[ContactForm] # 表单对象数组 validates :first_name, presence: true end对应的子表单定义在 spec/fixtures/forms/address_form.rb 和 spec/fixtures/forms/contact_form.rbclass AddressForm Rectify::Form attribute :street, String attribute :post_code, String validates :street, :post_code, presence: true end关键点嵌套表单就是一个普通属性类型声明为另一个Rectify::Form子类或Array[Form]。不需要任何关联配置声明即完成。核心揭秘valid? 是如何递归验证多级表单的打开源码 lib/rectify/form.rbvalid?的实现只有几行却暗藏玄机def valid?(options {}) before_validation options {} if options.blank? context options[:context] validations [super(context)] # 1. 验证自身属性 validations form_attributes_valid? unless options[:exclude_nested] # 2. 验证嵌套表单 validations array_attributes_valid? unless options[:exclude_arrays] # 3. 验证表单数组 validations.all? end它做了三件事步骤动作说明①super(context)走 ActiveModel 标准流程验证当前表单的validates规则②form_attributes_valid?遍历所有响应valid?的属性即子表单逐个调用其valid?③array_attributes_valid?展平数组属性对其中每个表单对象调用valid?三者全部通过整个表单才算有效。更妙的是——递归是天然支持的ContactForm里还可以声明Array[PhoneForm]见 spec/fixtures/forms/contact_form.rbvalid?会一层层向下穿透多级嵌套同样一网打尽。相关的自动化测试用例集中在 spec/lib/rectify/form_spec.rb覆盖了嵌套表单有效、无效、以及忽略嵌套的完整场景。快速跳过exclude_nested 与 exclude_arrays 两个选项并非每次都要全量验证。valid?接受选项参数让你精确控制验证范围form.valid? # 完整验证自身 嵌套表单 表单数组 form.valid?(exclude_nested: true) # 跳过嵌套表单只验证自身属性 form.valid?(exclude_arrays: true) # 跳过表单数组验证自身 嵌套表单 form.invalid? # 返回 valid? 的相反结果什么场景会用到分步表单Wizard第一步只校验用户信息地址还没填先exclude_nested: true草稿保存临时保存时宽松验证提交时再完整验证性能优化子表单数量大且已单独验证过避免重复校验进阶技巧用 with_context 给所有子表单注入上下文子表单经常需要当前用户这类全局信息来做跨层验证比如检查资源归属。Rectify 没有父链指针所以用with_context把上下文一次性下传整棵表单树form UserForm.from_params(params).with_context(user: current_user)之后任何层级的表单都能通过context.user访问当前用户在自己的validate里做归属检查而无需经过 Command。这是 Rectify 相比普通 Virtus 模型的一大亮点。实战组合Command 里的一行 invalid? 判断Rectify 推荐的完整链路是Form Object 验证 Command 执行业务。在 Command 的call方法里参考 readme.md 的示例def call return broadcast(:invalid) if form.invalid? # 多级验证一行搞定 # ...事务内执行业务然后 broadcast(:ok) end控制器则完全清爽def create form RegistrationForm.from_params(params) RegisterAccount.call(form) do on(:ok) { redirect_to dashboard_path } on(:invalid) { render :new } end end配合before_validation钩子可在验证前给字段填默认值和 I18n你的表单层就具备了生产级能力。新手避坑清单5 个常见误区❌把验证写回模型→ ✅ 每个上下文建独立 Form Object这正是 Rectify 的存在意义❌手动form.address.valid? form.contacts.all?(:valid?)→ ✅ 直接调form.valid?❌以为嵌套需要额外关联配置→ ✅ 声明属性类型即可❌忘记强参数→ ✅ Form Object 白名单机制天然替代 Strong Parameters未声明的属性一律忽略❌上下文只在根表单传入→ ✅ 用with_context让所有子表单共享总结一个 valid? 换回多少简洁能力传统 Rails 写法Rectify 嵌套表单多级表单验证逐层手写检查form.valid?一行递归部分验证复制多套检查逻辑exclude_nested/exclude_arrays选项跨层上下文层层透传参数with_context一次下传控制器职责混入验证与业务只剩 HTTP 转发如果你想动手试试把gem rectify加入 Gemfile 后执行bundle install即可开始。Rectify 的设计哲学是轻量 按需取用——你可以只借用 Form Object 的嵌套验证能力而无需整套架构迁移。对于被多级表单验证折磨过的 Rails 开发者来说这个valid?方法绝对值得收藏。【免费下载链接】rectifyBuild maintainable Rails apps项目地址: https://gitcode.com/gh_mirrors/re/rectify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考