本文的关键是想解释关于验证规则的问题,因为涉及到上传文件的时候,初学者常会在这里陷入错误,导致上传失败。
假设现有需求如下:创建一个表单,表单项一是普通的输入框,用来输入文件名称;表单项二是文件选择框,用来选择一个文件;点击上传按钮完成上传,并录入数据库。
在官方指导的基础上,我添加了一点点东西,以适应以上需求(官方文档:https://www.yiiframework.com/doc/guide/2.0/zh-cn/input-file-upload)。
创建模型
数据库中用来记录该项数据的表名为 document,因此首先要有对应的模型 Document.php
<?php
namespace app\models;
use yii\db\ActiveRecord;
// 注意是继承自ActiveRecord
class Document extends ActiveRecord
{
}
我们使用 ActiveForm 来上传表单,因此再新建一个模型类 DocumentAddForm.php
<?php
namespace app\models;
use yii\base\Model;
use yii\web\UploadedFile;
// 注意是继承自 Model,与上面的不同
class DocumentAddForm extends Model
{
public $file;
public $name;
private $filePath;
public function rules()
{
return [
['name', 'required', 'message' => '不能为空'],
['file', 'file', 'skipOnEmpty' => false, 'uploadRequired' => '请选择文件']
];
}
public function upload()
{
// $this->file早已在控制器中完成了赋值,传入了一个 UploadedFile 对象
// $this->validate()是验证上面rules定义的规则,原因见下文
if ($this->validate()) {
// 加入时间戳time()防止文件重名,这里的文件路径是相对于web/目录的,也就是你的可访问目录
$this->filePath = 'uploads/' . $this->file->baseName . '_' . time() . '.' . $this->file->extension;
$this->file->saveAs($this->filePath);
return true;
} else {
return false;
}
}
public function add()
{
// 如果上传成功,就把文件名和文件路径存入数据库
if ($this->upload()) {
$model = new Document();
$model->name = $this->name;
$model->path = $this->filePath;
return $model->save();
}
return false;
}
}
控制器
namespace app\controllers;
use Yii;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionDocument()
{
$modelAdd = new DocumentAddForm();
// 载入提交的数据,一般我们会在load()之后紧接着就进行validate(),验证是不是符合DocumentAddForm类的规则,但是这里不能这样,原因见下文
if ($modelAdd->load(Yii::$app->request->post())) {
// 上传文件通常借助yii\web\UploadedFile类,封装成UploadedFile对象后传递给$modelAdd->file
$modelAdd->file = UploadedFile::getInstance($modelAdd, 'file');
$this->checkAddResult($modelAdd->add());
}
return $this->render('document', [
'modelAdd' => $modelAdd
]);
}
}
视图
视图里通过 ActiveForm 把表单渲染输出,注意 file 使用 fileInput() 就可以了
<?php $form_formAdd = ActiveForm::begin([
'id' => 'formAdd-form',
'fieldConfig' => [
'template' => '{label}{input}<small class="text-danger">{error}</small>',
'labelOptions' => ['class' => '']
]
]);
?>
<?= $form_formAdd->field($modelAdd, 'name')->textInput()->label('文件名') ?>
<?= $form_formAdd->field($modelAdd, 'file')->fileInput(['class' => 'btn btn-light col-12'])->label('') ?>
<?= Html::submitButton('添加', ['class' => 'btn btn-primary']) ?>
<?php
ActiveForm::end();
?>
解释原因(何时验证规则)
我们在模型和控制器中留下了两个待解释的地方,就是 validate() 的使用。
按照往常的做法,我们会在控制器中先 load() 载入提交的数据,紧接着就进行 validate() 验证,但是请注意,此时进行验证的话,由于提交上来的数据没有 file 字段(上传文件传的是文件信息数组),因此就会报缺少文件的提示(因为在模型中定义了规则:'skipOnEmpty'=>false
)。
真正给 DocumentAddForm 模型的 $file 赋值是在控制器的这一行:
$modelAdd->file = UploadedFile::getInstance($modelAdd, 'file');
,应该在这行赋值完成后再进行验证,所以才把 validate() 放在 DocumentAddForm 模型中。
另外,DocumentAddForm 模型中还有一个变量是 $name,这个变量是要存到数据库的,所以要接收表单传上来的值,这个在 $modelAdd->load(Yii::$app->request->post())
就自动载入了。
如果还遇到了一些其他的问题,仍然可以尝试 var_dump($model->getErrors())
来看是什么错误。