假设您有一个带有 3 个名为 的文件输入的 HTML 表单certificate[]
,并且您想让它们都成为必需的。应该很容易吧?
一种合理的方法可能是在验证规则中使用数组点表示法:
<?php
$rules = [
'certificate.*' => [
'required',
],
];
然而,这并没有按预期工作。为什么?如果文件上传是空的,那么它甚至不是 Laravel 的一部分Request
,可以通过你的规则进行验证。该certificate.*
规则的意思是“对于证书数组中的每个元素,该元素都应该是必需的”,但如果certificate
您的 甚至不存在该元素Request
,则该规则将通过。
让我们弄清楚发生了什么。首先要检查:浏览器在做什么?如果文件上传为空,它甚至可能不会在其请求中发送表单字段。检查浏览器的网络选项卡,我发现表单字段确实存在。
接下来,PHP 呢?它是否包含$_FILES
超全局中的空文件?果然,$_FILES
包含一个包含certificate
3 个元素的数组,一个用于我的每个空文件输入:
array:1 [▼
"certificate" => array:5 [▼
"name" => array:3 [▶]
"type" => array:3 [▶]
"tmp_name" => array:3 [▶]
"error" => array:3 [▶]
"size" => array:3 [▶]
]
]
让我们添加一个包含 3 个空文本输入的数组,name[]
并查看 LaravelRequest
对象:
// output of $request->input()
array:2 [▼
"_token" => "eSGJ05lCLWLFZ1zCGxlP2rYxgy7FyZT6Yg8Fjawa"
"name" => array:3 [▼
0 => null
1 => null
2 => null
]
]
// output of $request->file()
[]
因此,请求中存在一个空文本字段数组,但我们的空文件输入数组却无处可寻。为什么会消失?
如果我们深入研究 aRequest
是如何创建的,我们就会找到答案。当 aRequest
被初始化时,它被传入所有 PHP 提供的超级全局变量,如$_GET
、$_POST
、$_FILES
等等。在内部,该$_FILES
参数被初始化为 SymfonyFileBag
对象。初始化时FileBag
,每个元素都通过convertFileInformation
函数传递。除其他外,它将这个数组重组为更一致的形状,但请注意这段相关的代码:
<?php
if (\UPLOAD_ERR_NO_FILE == $file['error']) {
$file = null;
} else {
$file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false);
}
尽管 PHP 在 中包含所有空文件对象$_FILES
,但它们中的每一个都有一个error
设置为 的值4
,它与UPLOAD_ERR_NO_FILE
常量匹配,因此初始化FileBag
为空。终于,我们找到了!这是那些文件消失的地方!
我相信你和我一样觉得这很有趣,但让我们回到最初的验证场景。我们有一个包含 3 个文件输入数组的表单,我们希望它们都是必需的。我们如何做到这一点?
<?php
$rules = [
'certificate' => [
'required',
'array',
'size:3',
],
'certificate.*' => [
// any other sort of file validation we want to do on each element
'file',
'mimes:pdf',
'size:4096',
]
]
通过在主数组上配对一组规则以及使用点符号为每个数组元素设置规则,我们得到了我们想要的验证行为