在 Yii2 中使用 Laravel 驗證類

minororange發表於2019-11-19

下載 Laravel 驗證類

composer require illuminate/validation:~5.6

建立 Validate 類

use Illuminate\Translation\FileLoader;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Translation\Translator;
use Illuminate\Validation\Factory;
use yii\web\Controller;
use yii\base\Action;

class Validate {
   protected $validator = null;

   //網上抄的程式碼,獲取 Laravel  Validate 例項
   private function getValidator(){
        if(is_null($this->validator)){
            $test_translation_path = __DIR__ . '/lang';
            $test_translation_locale = 'en';
            $translation_file_loader = new FileLoader(new Filesystem, $test_translation_path);
            $translator = new Translator($translation_file_loader, $test_translation_locale);
            $this->validator = new Factory($translator);
        }

        return $this->validator;
   }

   // 使用反射獲取控制器的驗證規則
   private function getRuleClass(Controller $controller,Action $action){
        $reflection = new ReflectionClass($controller);
        $comment = $reflection->getMethod($action->actionMethod)->getDocComment();

        return Tools::getDocComment($comment, 'validator');
   }

   // 驗證控制器規則
   public function check(Controller $controller, Action $action){
       $ruleClass = $this->getRuleClass($controller,$action);

       // 如果控制器沒有驗證規則,直接返回 true
       if(empty($rules) || !class_exists($validator)){
          return true;
       }
       $ruleClass = new $ruleClass;
       // 獲取表單資料,GET + POST
       $request = \Yii::$app->request;
       $input = array_merge($request->get(), $request->post());

       $rules = $ruleClass->rules();
       $messages = $ruleClass->messages();
       $attributes = $ruleClass->attributes();

       $result = $this->getValidator()->make($input, $rules, $messages, $attributes);

       // 驗證失敗丟擲 ValidateException
       if ($result->fails()) {
          throw new ValidateException($result->messages()->first());
       }

       return ture;
   }
}

Tools::getDocComment :

public static function getDocComment($comment, $tag)
{
    if (empty($tag)) {
        return $comment;
    }

    $matches = [];
    preg_match("/" . $tag . "(.*)(\\r\\n|\\r|\\n)/U", $comment, $matches);

    if (isset($matches[1])) {
        return trim($matches[1]);
    }

    return '';
}

建立抽象 RuleClass 類

abstract class ValidateRules{
    /**
     * 驗證規則
      *
     * @return array
     * @see https://laravel-china.org/docs/laravel/5.6/validation/1372
     */
     abstract public function rules(): array;

     abstract public function messages(): array;

     abstract public function attributes(): array;
}

建立具體的驗證規則

class UserAddRules extends ValidateRules{
    public function rules(){
        return [
             'username' => 'required|email',
             'password' => 'required'
        ];
    }

    public function messages(){
        return [
             'required' => ':attribute 不能為空',
             'email' => ':attribute 必須為郵箱格式'
        ];
    }

    public function attributes(){
        return [
            'username' => '使用者名稱',
            'password' => '密碼'
        ];
    }
}

在控制器中加入驗證

class UserController extends BaseController {
    /**
     * @validator app\Validate\UserAddRules
     */
     public function actionAdd(){
     }
}

使用註解將驗證規則加入到控制器上,@validator 填寫包含名稱空間的完整類名

在 BaseController 中全域性驗證

public function beforeAction($action)
{
    try {
      $validate = new Validate();
      $validate->check($this, $action);
    } catch (\ReflectionException $e) {
      $this->responseJson(Status::SERVER_ERROR, $e->getMessage());
      return false;
    } catch (ValidateException $e) {
      $this->responseJson(Status::BAD_REQUEST, $e->getMessage());
      return false;
    }

    return parent::beforeAction($action); 
}

結束語

之前在開發 Yii 應用時,發現框架自帶驗證是在資料入庫時進行的驗證,不太符合專案需求,然後透過搜尋發現 Laravel 驗證包可以單獨使用,嘗試一番,還是挺不錯的,在 RuleClass 中的編寫方式與 Laravel 中的 Request 類一致,只有個別需要資料庫驗證的( unique 等)不能使用,可以實現 Illuminate\Contracts\Validation\Rule 進行自定義規則。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章