phpyiiyii2yii2-basic-appyii-components

Yii 2 How add a custom validation check on file upload


I am building a CSV uploader and I want to add a custom validation function that will check the header row of the CSV file to ensure the correct columns are in place.

I am trying to put a custom validation rule in the model to do this but failing at the first hurdle.

I am getting

Setting unknown property: yii\validators\FileValidator::0

exception but as far as I can tell from the documentation this should work.

Model

/**
* UploadForm is the model behind the upload form.
*/
class UploadForm extends Model
{
/**
 * @var UploadedFile file attribute
 */
public $file;

/**
 * @return array the validation rules.
 */
public function rules()
{
    return [
        [['file'], 'file', 'extensions' => 'csv', 'checkExtensionByMimeType'=>false, 'headerCheck', 'skipOnEmpty' => false]
    ];
}

public function attributeLabels(){
    return [
        'file'=>'Select csv'
    ];
}

 function headerCheck($attribute, $params, $validato){

    $this->addError($attribute, "error");
}
}

Controller function:

     public function actionUpload()
{
    $model = new UploadForm();

    if (Yii::$app->request->isPost) {
        $model->file = UploadedFile::getInstance($model, 'file');
        $filename = $model->file->baseName . '.' . $model->file->extension;

        if ($model->file && $model->validate()) {
            $upload = $model->file->saveAs('uploads/'.$filename );

            if($upload){
                define('CSV_PATH','uploads/');
                $csv_file = CSV_PATH . $filename;
                $filecsv = file($csv_file);

                foreach($filecsv as $data){
                    $lines = explode(',',$data);
                    $t=1;
                }
            }
        }
    }

    return $this->render('csvUpload', ['model' => $model]);
}

View

<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>

<?= $form->field($model, 'file')->fileInput() ?>

<button>Submit</button>

<?php ActiveForm::end() ?>

Why is headerCheck() not getting picked up as a custom validation function?


Solution

  • Short Answer

    Your rules should be written like so:

    return [
        [['file'], 'file', 'extensions' => 'csv', 'checkExtensionByMimeType'=>false, 'skipOnEmpty' => false],
        [["file"], "headerCheck"],
    ];
    

    Note that your validation rule, "headerCheck", is a separate item in the array.

    Long Answer

    A rule's structure is like so:

    [["attributes_to_validate"],"vaildatorCallback","param1"=>"value","param2"=>"value]
    

    Note the first two items are the attributes and the callback respectively, and then after that you can specify params that should be assigned to the validator, or passed to your validator callback. These params are expected in a form where the key is the name of the property, and the value is the value to assign to the property.

    In the example you provided, Yii sees that you want to utilize the "file" validator, so it creates an instance of yii\validators\FileValidator. It then sees that you want the parameter "extensions" set to "csv", so it does:yii\validators\FileValidator::$extensions = "csv"; But then, because you have included your custom validator in this part of the array, it thinks that "headerCheck" is actually a value of a property you want to assign to the validator. Because you have entered this "param" without a key, the key defaults to 0 and so Yii thinks the property you want to assign is called '0'. Thus, Yii attempts this: yii\validators\FileValidator::0 = "headerCheck";

    Of course, there is no property '0' on FileValidator, and so that's where the error you're getting is coming from.