使用 Yii 2 构建表单:模型、校验与 ActiveForm 完整实战指南
【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2
本篇文章基于 Yii 2 官方指南中的“表单处理”章节(对应仓库 docs/guide-pl/start-forms.md 及其英文原版 docs/guide/start-forms.md),完整演示如何从零创建一个用于收集用户数据的表单页面:页面包含name与email两个输入字段,提交后把用户输入的值回显确认。你将掌握三件事:创建承载表单数据的 Model 类并声明校验规则、在控制器中编写处理表单提交的 Action、以及用ActiveForm组件在视图中渲染带标签与错误提示的 HTML 表单。文章还深入框架源码(framework/base/Model.php、framework/widgets/ActiveForm.php)解释验证的底层执行流程与客户端/服务端双重校验机制。
前置说明:本文以 Yii 2 基础应用模板为背景(即通过 start-installation.md 安装好的项目结构),示例代码中出现的
app\models、app\controllers命名空间与views/site/目录均来自该模板约定。若尚未完成入门流程,建议先阅读 start-hello.md 了解 Action 与视图的创建方式,以及 concept-autoloading.md 了解类文件命名与自动加载约定。
整体思路:MVC 三要素协同
要构建一个可用的表单页面,除了创建 action(动作)和两个 views(视图)之外,还需要一个 model(模型)。三者各司其职:
- Model:用类来表示用户通过表单提交的数据,并声明对这些数据的校验规则;
- Action:接收 HTTP 请求,把请求数据填充进模型、执行校验,然后根据校验结果决定渲染哪个视图;
- View:负责输出 HTML 表单(数据采集页)以及提交成功后的确认回显页。
本教程将带你逐步完成以下三件事:
- 创建一个 Model 类,代表用户通过表单输入的数据;
- 在 Model 中声明用于检查输入数据的校验规则(rules);
- 在 View 中构建 HTML 表单。
第一步:创建模型(Model)
需要向用户采集的数据由EntryForm模型类表示,保存在models/EntryForm.php文件中。类文件与类名的命名约定遵循 Yii 的自动加载机制,详见 概念:类自动加载。
<?php namespace app\models; use Yii; use yii\base\Model; class EntryForm extends Model { public $name; public $email; public function rules() { return [ [['name', 'email'], 'required'], ['email', 'email'], ]; } }为什么继承yii\base\Model
EntryForm继承自 [[yii\base\Model]],这是 Yii 提供的基础类,通常用于表示表单数据。在 framework/base/Model.php 的类注释中,官方明确了它的定位与内置能力:
- 属性声明(attribute declaration):默认情况下,每个 public 成员都会被当作模型属性,
attributes()通过反射(ReflectionClass)收集类中所有非静态的 public 属性; - 属性标签(attribute labels):每个属性可关联一个用于展示的标签;
- 批量赋值(massive attribute assignment):支持一次性把数组数据批量填充到模型属性;
- 基于场景的校验(scenario-based validation):按场景执行校验规则。
Info:
Model是不关联数据库表的模型类的父类;而yii\db\ActiveRecord才是关联数据库表的模型类的父类。本文示例属于前者。
两个 public 属性
name与email两个 public 属性用于存放用户输入的数据。正因为它们是 public,Model才能通过反射机制把它们识别为可批量赋值的属性——这是后续load()能自动填充数据的前提。
声明校验规则rules()
模型中的rules()方法返回一组校验规则,上面的规则表达了两个约束:
name和email两个字段的值都是必填的(required校验器);email字段的值必须是语法上合法的 email 地址(email校验器)。
关于规则的写法,framework/base/Model.php 的文档注释给出了完整约定:每条规则是一个数组,第一个元素是属性列表(单个属性可直接传字符串),第二个元素是校验器类型(可以是内置校验器别名、模型类方法名、匿名函数或校验器类名),还可以通过on指定适用场景、通过额外的键值对初始化校验器属性(例如['username', 'string', 'min' => 3, 'max' => 12])。Yii 内置了丰富的校验器集合,完整清单可参考 tutorial-core-validators.md。
触发校验:validate()与错误读取
当EntryForm对象被填充了用户数据后,可以调用它的 [[yii\base\Model::validate()|validate()]] 方法触发数据校验例程。校验失败时,[[yii\base\Model::hasErrors|hasErrors]] 属性会变为true,并可通过 [[yii\base\Model::getErrors()|getErrors()]] 查看具体发生了哪些错误。
<?php $model = new EntryForm(); $model->name = 'Qiang'; $model->email = 'bad'; if ($model->validate()) { // 校验通过! } else { // 校验失败! // 使用 $model->getErrors() 查看错误详情 }从源码看,validate()的实际执行流程(framework/base/Model.php)为:先调用clearErrors()清空旧错误 → 触发beforeValidate事件(可通过 EVENT_BEFORE_VALIDATE 拦截并终止校验)→ 依据当前场景获取活跃属性与活跃校验器 → 逐个执行Validator::validateAttributes()→ 触发afterValidate事件 → 最终返回!$this->hasErrors()。本例中email = 'bad'明显不是合法邮箱,因此validate()会返回false,且getErrors()中会包含 email 属性的错误信息。
第二步:创建动作(Action)
接下来需要在site控制器中创建一个名为entry的动作来使用新模型。动作的创建与使用流程已在 你好,世界 一节中介绍过。
<?php namespace app\controllers; use Yii; use yii\web\Controller; use app\models\EntryForm; class SiteController extends Controller { // ...现有代码... public function actionEntry() { $model = new EntryForm(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { // 已接收到合法数据并填充进 $model // 在此对 $model 做有意义的事情…… return $this->render('entry-confirm', ['model' => $model]); } else { // 要么是首次打开页面,要么是校验出现错误 return $this->render('entry', ['model' => $model]); } } }动作的完整工作流
该动作的工作流程可以拆解为四步:
- 实例化模型:
new EntryForm()创建空模型对象; - 填充数据:
$model->load(Yii::$app->request->post())尝试把$_POST数据填充进模型。load()的实现位于 framework/base/Model.php,它按照模型的formName()(默认取类名)从提交数据中取出对应分组并批量赋值。Yii 通过 [[yii\web\Request::post()|post()]] 安全地访问$_POST数据; - 执行校验:若
load()成功(说明用户确实提交了 HTML 表单),则调用validate()确保输入值合法; - 分流渲染:一切正常时渲染
entry-confirm视图向用户确认数据提交成功;若没有任何提交或数据包含错误,则渲染entry视图,在该视图中展示 HTML 表单并附带校验错误消息。
Info:表达式
Yii::$app表示 应用(application) 实例,它是全局可访问的单例。同时它也是一个 服务定位器(service locator),提供request、response、db等组件以支撑特定功能。上面的代码正是通过应用实例的request组件来访问$_POST数据。
Note:在这个简单示例中,数据校验通过后我们只是直接渲染确认页。实际项目中,建议使用 [[yii\web\Controller::refresh()|refresh()]] 或 [[yii\web\Controller::redirect()|redirect()]] 进行页面跳转,以避免表单重复提交(Post/Redirect/Get 模式)带来的问题。
第三步:创建视图(View)
最后创建两个视图文件entry-confirm和entry,它们由entry动作按上述逻辑分别渲染。
确认视图entry-confirm
entry-confirm视图只是简单地展示name与email数据,保存在views/site/entry-confirm.php:
<?php use yii\helpers\Html; ?> <p>您输入了以下信息:</p> <ul> <li><label>名称</label>: <?= Html::encode($model->name) ?></li> <li><label>邮箱</label>: <?= Html::encode($model->email) ?></li> </ul>注意这里使用Html::encode()对输出进行 HTML 转义,这是 Yii 安全最佳实践的核心要求——防止用户输入被当作 HTML 执行(XSS 攻击)。关于更多安全实践可参考 security-best-practices.md。
表单视图entry
entry视图负责输出 HTML 表单,保存在views/site/entry.php:
<?php use yii\helpers\Html; use yii\widgets\ActiveForm; ?> <?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'name') ?> <?= $form->field($model, 'email') ?> <div class="form-group"> <?= Html::submitButton('提交', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?>ActiveForm 是如何工作的
该视图使用了功能强大的 widget(小组件) [[yii\widgets\ActiveForm|ActiveForm]] 来构建 HTML 表单(源码位于 framework/widgets/ActiveForm.php):
begin()与end()方法分别渲染表单的开标签与闭标签;- 两个方法调用之间,通过
field()方法创建输入字段(ActiveForm::field() 内部会基于fieldConfig与传入的模型、属性创建一个 ActiveField 对象),第一个字段用于name数据,第二个用于email数据。每个字段自动包含三部分:label 标签、输入控件、错误提示区; - 字段之后,调用 [[yii\helpers\Html::submitButton()|submitButton()]] 生成提交按钮。
动手验证:运行表单页
在浏览器中访问以下地址来查看效果:
https://hostname/index.php?r=site%2Fentry你会看到一个包含两个输入字段的页面,每个字段前都有描述其含义的标签。如果未输入任何内容就点击“提交”按钮,或输入的邮箱地址不合法,每个出问题的输入框旁都会显示对应的错误消息(见文首第一张截图)。
在输入合法的名称与邮箱地址并点击提交后,会跳转到新页面显示你刚刚输入的数据(见文首第二张截图)。
揭秘“魔法”:客户端与服务端双重校验
你可能会好奇这个 HTML 表单为什么“看起来像魔法一样”——不用刷新页面,就能为每个字段显示标签、在输入错误时立刻提示错误消息。
答案在于双层校验机制:
- 客户端校验:数据校验首先在浏览器端通过 JavaScript 完成。
ActiveForm足够智能,能把你声明在EntryForm中的校验规则提取出来,转换成可执行的 JavaScript 代码并注册到页面(见ActiveForm::registerClientScript(),位于 framework/widgets/ActiveForm.php),然后用这些 JS 代码在提交前后进行即时校验; - 服务端校验:即使你在浏览器中禁用了 JavaScript,校验仍会在服务端照常执行,正如
actionEntry()中演示的那样。这保证了任何情况下数据都是有效的。
Warning:客户端校验只是提升用户体验的便捷手段,服务端校验始终是必须的——无论客户端校验是否开启,服务端都必须校验数据。因为客户端校验可以被绕过,服务端才是数据安全的最后防线。
ActiveForm 的客户端校验配置项
从源码看,客户端校验行为由 ActiveForm 的多个 public 属性 控制,默认值如下:
| 配置项 | 默认值 | 作用 |
|---|---|---|
enableClientValidation | true | 是否启用客户端数据校验;ActiveField级设置可覆盖 |
enableAjaxValidation | false | 是否启用基于 AJAX 的数据校验 |
enableClientScript | true | 是否挂载yii.activeFormJavaScript 插件;为false时表单不生成任何 JS |
validateOnSubmit | true | 是否在表单提交时执行校验 |
validateOnChange | true | 是否在输入框值改变时执行校验 |
validateOnBlur | true | 是否在输入框失去焦点时执行校验 |
validateOnType | false | 是否在用户输入过程中实时校验 |
validationDelay | 500 | validateOnType为true时,输入校验的延迟毫秒数 |
scrollToError | true | 校验失败后是否滚动到第一个错误处 |
这些开关可以在视图中通过配置ActiveForm::begin()的参数统一控制,也可以针对单个字段通过field()返回的ActiveField对象单独控制,非常适合按需优化表单交互体验。
标签从哪来
表单中每个字段的标签由field()方法根据模型的属性名自动生成。例如,name属性会生成Name标签,email属性会生成Email标签。其底层逻辑可参考 Model::generateAttributeLabel()(把camelCase属性名转换为适合展示的单词形式),你也可以重写 Model::attributeLabels() 显式指定更友好的标签。
你可以在视图中使用如下代码自定义标签:
<?= $form->field($model, 'name')->label('您的姓名') ?> <?= $form->field($model, 'email')->label('您的邮箱') ?>Info:Yii 提供了大量类似的 widget 帮助开发者快速构建复杂、动态的视图。后续你还会学到,编写一个新 widget 也非常容易——你可以把大量视图代码提炼成可复用的 widget,简化未来视图的开发。
扩展:批量校验与 AJAX 校验
除了常规流程,ActiveForm还提供了两个静态校验辅助方法(framework/widgets/ActiveForm.php),专门用于简化 AJAX 校验场景:
ActiveForm::validate($model1, $model2, ...):校验一个或多个模型,返回以“属性 HTML id”为索引的错误消息数组;ActiveForm::validateMultiple($models):校验模型数组,适用于表格输入(tabular input)场景,错误消息索引形如[0]name、[1]email。
典型的用法是在控制器动作里配合 AJAX 请求返回 JSON 错误:
$model = new EntryForm(); $model->load(Yii::$app->request->post()); if (Yii::$app->request->isAjax) { Yii::$app->response->format = Response::FORMAT_JSON; return ActiveForm::validate($model); } // ……处理非 AJAX 请求……小结
在这一节指南中,你已经接触到了 MVC 架构模式的每一个部分:学会了创建模型类来表示用户数据并校验这些数据,学会了如何从用户处获取数据、以及如何在浏览器中把数据展示回来。
这类任务在开发应用时往往非常耗时,但 Yii 提供了强大的 widget 让这一切变得异常简单。下一节将学习几乎每个应用都需要的数据库操作,对应指南文档为 start-databases.md。
【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考