在 cakephp 4 中没有上传文件时避免 mimeType 验证

Avoid mimeType validation when no file is uploaded in cakephp 4

这是对我的输入文件“图像”的验证:

public function validationDefault(Validator $validator): Validator
{
    $validator = parent::validationDefault($validator);

    $validator
        ->allowEmptyFile('image')
        ->add('image', 'uploadError', [
            'rule' => function ($value, $context) {
                foreach ($value as $v) {
                    return Validation::uploadError($v, true);
                }
            },
            'last' => true,
            'message' => 'Upload error'
        ])
        ->add('image', 'mimeType', [
            'rule' => function ($value, $context) {
                foreach ($value as $v) {
                    return Validation::mimeType($v, [
                        'image/png',
                        'image/gif',
                        'image/pjpeg',
                        'image/jpeg'
                    ]);
                }
            },
            'message' => 'Bad mime type.',
        ]);

}

提交文件时效果很好,但是当没有文件上传时会触发 mimeType 验证错误。

所以我修改了 mimeType 规则以在检查 mimeType 之前检查文件是否已上传:

public function validationDefault(Validator $validator): Validator
{
    $validator = parent::validationDefault($validator);

    $validator
        ->allowEmptyFile('image')
        ->add('image', 'uploadError', [
            'rule' => function ($value, $context) {
                foreach ($value as $v) {
                    return Validation::uploadError($v, true);
                }
            },
            'last' => true,
            'message' => 'Upload error'
        ])
        ->add('image', 'mimeType', [
            'rule' => function ($value, $context) {

                // Added to avoid mimeType validation when no file is uploaded
                if ($value[0]->getError() === UPLOAD_ERR_NO_FILE) {
                    return true;
                }

                foreach ($value as $v) {
                    return Validation::mimeType($v, [
                        'image/png',
                        'image/gif',
                        'image/pjpeg',
                        'image/jpeg'
                    ]);
                }
            },
            'message' => 'Bad mime type.',
        ]);

}

它可以工作,但对我来说似乎不太干净 if ($value[0]->getError() === UPLOAD_ERR_NO_FILE) {return true;} 在 mime 类型检查后可以添加的每条规则上(例如,我将添加文件大小检查、图像宽度检查等)

是否有更好的方法来仅在提交文件时才对文件添加验证规则?

查看您的上传功能当前的结构,即所有上传都存储在同一个 table 中,并且行为通过 hasMany 关联处理它们,包括仅接受单个文件,并且前端使用多文件输入,一种可能的解决方案是在上传空文件的情况下简单地删除该字段。

您可以通过 Model.beforeMarshal event/callback 更改数据,它将 运行 当 creating/patching 个实体应用验证之前,例如:

public function beforeMarshal(
    \Cake\Event\EventInterface $event,
    \ArrayAccess $data,
    \ArrayObject $options
): void {
    if (
        isset($data['image'][0]) &&
        $data['image'][0] instanceof \Psr\Http\Message\UploadedFileInterface &&
        $data['image'][0]->getError() === \UPLOAD_ERR_NO_FILE
    ) {
        unset($data['image']);
    }
}

这将删除 image 字段,以防它是一个数组,并且第一个元素是一个空的上传文件对象。这是未选择文件的多文件输入以及使用数组作为名称的单文件输入的结果。

当该字段不再存在时,您的验证规则将不会 运行,而当它们 执行 运行 时,它们将不会允许这种情况,而是可以严格要求有效的上传文件对象。

因此,例如对于只应接受单个上传的模型,您可以执行类似这样的操作以确保该值是一个数组,该数组仅包含一个包含上传文件对象的元素:

$validator
    ->add('image', 'exactlyOneUploadedFile', [
        'rule' => function ($value, $context) {
            if (
                is_array($value) &&
                count($value) === 1 &&
                $value[0] instanceof \Psr\Http\Message\UploadedFileInterface
            ) {
                return true;
            }

            return false;
        },
        // ...
    ])
    // ...

类似地,对于一个应该接受多个上传的模型,你可以做这样的事情来确保该值是一个数组,其中包含一个或多个仅上传文件对象的元素:

$validator
    ->add('image', 'onlyUploadedFiles', [
        'rule' => function ($value, $context) {
            if (
                !is_array($value) ||
                count($value) < 1
            ) {
                return false;
            }

            foreach ($value as $upload) {
                if (!($upload instanceof \Psr\Http\Message\UploadedFileInterface)) {
                    return false;
                }
            }

            return true;
        },
        // ...
    ])
    // ...

真正严格执行这些检查很重要!例如,您发布的验证规则只会检查数组中的第一个条目,但不会检查是否存在更多元素,这可能会导致发送多个上传的情况,并且额外的上传会丢失通过未经验证!

另见