Laravel 模型自定義驗證,同於其它框架 (Yii、tp)

linziyi0509發表於2020-06-10

目的:
方便自定義驗證,需要從資料庫讀取的資料,或者一些邏輯性較強的驗證等

有道雲地址:
note.youdao.com/ynoteshare1/index....

laravel在官方文件中有幾種驗證方式 但是這些方式無法滿足,一些邏輯性判斷,比如一些特殊驗證,需要查詢表取資料做對比的

控制器驗證
$this->validate($request,[
    'name' => 'required|min:2|max:20',
    'age' => 'required|integer',
    'sex' => 'required|integer',
],[
    'required'=>':attribute 為必填項',
    'min' => ':attribute 長度不符合要求',
    'integer' => ':attribute 必須是一個整形',
],[  'name' => '姓名',
    'age' => '年齡',
    'sex' => '性別',
]);
Validator類驗證
$validator = \Validator::make($request->input(),[
    'name' => 'required|min:2|max:20',
    'age' => 'required|integer',
    'sex' => 'required|integer',
],[
    'required'=>':attribute 為必填項',
    'min' => ':attribute 長度不符合要求',
    'integer' => ':attribute 必須是一個整形',
],[  
    'name' => '姓名',
    'age' => '年齡',
    'sex' => '性別',
]);
if($validator->fails()){
    foreach ($validator->errors()->all() as $message) {
        return ['code'=>-1,'msg'=>$message];
    }
}

我追蹤原始碼發現 加上自定義方法後,直接提示找不到這個方法,或者就是直接返回了自定義的錯誤

錯誤資訊:

(1/1) BadMethodCallException Method [validateCheckShopPrice] does not exist.

因為我使用的是Validator::make進行驗證的我找到了這個類中重要的方法

位置 vendor\laravel\framework\src\Illuminate\Validation

protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
{
    if (is_null($this->resolver)) {
        return new Validator($this->translator, $data, $rules, $messages, $customAttributes);
    }
    return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
}
跟蹤程式碼會發現走的是new Validator
在相同目錄下的檔案中 預設會發現他直接初始化了,那麼怎麼辦呢?
public function __construct(Translator $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
{
    $this->initialRules = $rules;
    $this->translator = $translator;
    $this->customMessages = $messages;
    $this->data = $this->parseData($data);
    $this->customAttributes = $customAttributes;
    $this->setRules($rules);
}

接著往回看,發現在驗證完成之後,記性了fails()方法的呼叫

public function fails()
{
    return ! $this->passes();
}
是不是找到根源了 需要資料是否透過規則
public function passes()
{
    $this->messages = new MessageBag;
    foreach ($this->rules as $attribute => $rules) {
        $attribute = str_replace('\.', '->', $attribute);
        foreach ($rules as $rule) {
            $this->validateAttribute($attribute, $rule);
            if ($this->shouldStopValidating($attribute)) {
                break;
            }
        }
    }
    foreach ($this->after as $after) {
        call_user_func($after);
    }
    return $this->messages->isEmpty();
}

這是真正驗證的地方
需要注意
$this->validateAttribute($attribute, $rule);
這是驗證規則的地方,我進行了改動
protected function validateAttribute($attribute, $rule)
{
    $this->currentRule = $rule;
    $initialRule = $rule;
    list($rule, $parameters) = ValidationRuleParser::parse($rule);
    if ($rule == '') {
        return;
    }
    if (($keys = $this->getExplicitKeys($attribute)) &&
        $this->dependsOnOtherFields($rule)) {
        $parameters = $this->replaceAsterisksInParameters($parameters, $keys);
    }
    $value = $this->getValue($attribute);
    if ($value instanceof UploadedFile && ! $value->isValid() &&
        $this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
    ) {
        return $this->addFailure($attribute, 'uploaded', []);
    }
    $validatable = $this->isValidatable($rule, $attribute, $value);
    /**
     * 1.自定義方法的字首都是check
     * 2.寫法 check名字:類所在位置 checkShopPrice:\App\Models\Admin\Goods
     * 3.驗證如果是字串就提示錯誤 我給出的錯誤提示語均為字串
     */
    if(substr($initialRule,0,5) == 'check'){
        $initial = array_reverse(explode(':', $initialRule));
        $model = new $initial[0];
        $tipRes = $model->$initial[1]($value, $parameters, $this->data);
        if ($validatable && is_string($tipRes)) {
            $this->addCustomFailure($attribute, $rule, $tipRes);
        }
    }else{
        $method = "validate{$rule}";
        if ($validatable && ! $this->$method($attribute, $value, $parameters, $this)) {
            $this->addFailure($attribute, $rule, $parameters);
        }
    }
}

註釋的地方是我加入自定義的程式碼

報錯錯誤處理我也重新定義了方法,由於原有格式和目前自己寫的有出入
protected function addCustomFailure($attribute, $rule, $message)
{
    $this->messages->add($attribute, $message);
    $this->failedRules[$attribute][$rule] = $message;
}

以上程式碼已經封裝好了

使用方法 驗證規則

public $rule = [
    'shop_price' => ['required', 'checkShopPrice:\App\Models\Admin\Goods'],
];
//錯誤資訊
protected $message = [
    'shop_price.required' => '本店售價必填',
    'shop_price.regex' => '本店售價格式不對',
    'shop_price.checkShopPrice' => '本店售價格式不對',
];
public function checkForm($request){
    try{
        $model = new Goods();
        $validator = Validator::make($request->all(), $model->rule,$model->message);
        if($validator->fails()){
            foreach ($validator->errors()->all() as $message) {
                return ['code'=>-1,'msg'=>$message];
            }
        }
        return ['code'=>1,'msg'=>self::exec_success];
    }catch (ValidationException $e){
        return ['code'=>-1,'msg'=>$e->getMessage()];
    }
}
public static function checkShopPrice($value, $rule, $data){
    if ($value < 0.01) {
        return  '售價不能小於0.01元';
    } else {
        return true;
    }
}

以上是一種情況,就是每一個模型進行自己的驗證,還有一種情況就是使用公共函式去做,這樣的話 就需要把所有的驗證方法做到一個或者多個方法中,就會混亂 不建議這麼做

本作品採用《CC 協議》,轉載必須註明作者和本文連結
個人技術群,歡迎加入:511807472

相關文章