gordensong/laravel-table-validator 規則收集器

gordensong發表於2021-09-02

gordensong / laravel-table-validator

簡介

這是一個基於 laravel 的規則收集器。將規則可以像積木一樣任意搭建。可以自定義呼叫方式。

  1. 複用規則
  2. 自定義規則
  3. 自動載入表規則
  4. 排除規則
  5. 場景規則
  6. 快取規則

概念

  1. Validator:規則收集器。
  2. TableValidator:表規則收集器(擴充套件表欄位規則)。
  3. Collection:(表)規則收集器集合。
  4. scene :規則場景。表規則收集器可以合併獲取指定場景規則。規則收集器集合可以分別獲取每個規則收集器的場景規則。

機制

  1. 規則合併優先順序: table rules < $rules|customizeRules() < excludeRules() < scene rules,優先順序高的規則覆蓋優先順序低的規則。
  2. 同一表欄位規則: required 優先順序最高,排最前面,

2. 基礎篇

2.1 migrations

user

Schema::create('user', function (Blueprint $table) {
    $table->id();
    $table->string('username', 50)->nullable(false);
    $table->string('password', 50)->nullable(false);
    $table->timestamps();
    $table->softDeletes();
});

user_info

Schema::create('user_info', function (Blueprint $table) {
    $table->id();
    $table->integer('user_id')->nullable(false)->default(0);
    $table->string('xing')->nullable(true)->comment('姓');
    $table->string('ming')->nullable(true)->comment('名');
    $table->integer('age')->default(0);
    $table->json('config1');
    $table->text('config2')->comment('序列化欄位');
});

user_address

Schema::create('user_address', function (Blueprint $table) {
    $table->id();
    $table->integer('user_id')->nullable(false)->default(0);
    $table->string('province')->default('')->comment('省');
    $table->string('city')->default('')->comment('市');
    $table->string('district')->default('')->comment('區');
    $table->string('address', 255)->default('')->comment('詳細地址');
    $table->timestamps();
    $table->softDeletes();
});

2.2 命令建立 Table Validator

2.2.1 建立指定表的 Validator

php artisan validator:make user user_info --connection=mysql --force

輸出

Table validator created: /home/gorden/Workspace/laravel-model-validator-app/app/Validators/Mysql/UserValidator.php

2.2.2 建立所有表的 Validator

php artisan validator:make . --connection=mysql --force

輸出

Table validator created: /home/gorden/Workspace/laravel-model-validator-app/app/Validators/Mysql/UserValidator.php
Table validator created: /home/gorden/Workspace/laravel-model-validator-app/app/Validators/Mysql/UserAddressValidator.php
Table validator created: /home/gorden/Workspace/laravel-model-validator-app/app/Validators/Mysql/UserInfoValidator.php

以下是自動生成的 Validator

UserValidator
<?php

namespace App\Validators\Mysql;

class UserValidator extends \GordenSong\Support\TableValidator
{
    protected $connection = 'mysql'; // 作者注: 預設 mysql 可以不設定
    protected $table = 'user';

    public function customizeRules(): array
    {
        return [
            'id' => ['required'],       // 作者注: 資料庫非空所以自動追加 'required' 規則 
            'username' => ['required'],
            'password' => ['required'],
            'created_at' => [],
            'updated_at' => [],
            'deleted_at' => [],
        ];
    }

    /**
    * 作者注:驗證要排除的欄位
    */
    public function excludeRules(): array
    {
        return [
            'created_at',
            'updated_at',
            'deleted_at',
        ];
    }

    protected $messages = [
    ];

    protected $attributes = [
    ];

    protected $scenes = [

    ];
}
UserInfoValidator
<?php

namespace App\Validators\Mysql;

class UserInfoValidator extends \GordenSong\Support\TableValidator
{
    protected $connection = 'mysql';
    protected $table = 'user_info';

    public function customizeRules(): array
    {
        return [
            'id' => ['required'],
            'user_id' => ['required'],
            'xing' => [],
            'ming' => [],
            'age' => ['required'],
            'config1' => ['required'],
            'config2' => ['required'],
        ];
    }

    public function excludeRules(): array
    {
        return [
        ];
    }

    protected $messages = [
    ];

    protected $attributes = [
    ];

    protected $scenes = [
    ];
}
UserAddressValidator
<?php

namespace App\Validators\Mysql;

class UserAddressValidator extends \GordenSong\Support\TableValidator
{
    protected $connection = 'mysql';
    protected $table = 'user_address';

    public function customizeRules(): array
    {
        return [
            'id' => ['required'],
            'user_id' => ['required'],
            'province' => ['required'],
            'city' => ['required'],
            'district' => ['required'],
            'address' => ['required'],
            'created_at' => [],
            'updated_at' => [],
            'deleted_at' => [],
        ];
    }

    public function excludeRules(): array
    {
        return [
            'created_at',
            'updated_at',
            'deleted_at',
        ];
    }

    protected $messages = [
    ];

    protected $attributes = [
    ];

    protected $scenes = [
    ];
}

2.2.3 方法介紹

2.2.3.1 rules() 獲取規則

右邊的規則覆蓋左邊的規則: tableRules, customizeRules, excludeRules, scene rules.

$rules = UserValidator::instance()->rules();
dump($rules);

array:3 [
  "id" => array:3 [
    0 => "required"
    1 => "integer"
    2 => "min:0"
  ]
  "username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:50"
  ]
  "password" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:50"
  ]
]

2.2.3.2 customizeRules() 定製規則

如對 UserValidator 的規則不滿意,你可以

  1. 追加
  2. 覆蓋
public function customizeRules(): array
{
    return [
        // ...
        'username' => ['required', 'max:30', 'min:3'],
        //...
    ];
}

// 結果
array:3 [
  "id" => ...
  "username" => array:4 [
    0 => "required"
    1 => "string"
    2 => "max:30"
    3 => "min:3"
  ]
  "password" => ...
]

2.2.3.3 excludeRules() 排除規則

你對結果還是不滿意,想去掉 username 的規則 min:3

public function excludeRules(): array
{
    return [
        // 排除整個驗證欄位
        'created_at',
        'updated_at',
        'deleted_at', 

        // 排除指定欄位的部分規則
        // 你可以寫多個規則, 你可以簡寫規則,只寫“:”前面的內容。
        'username' => ['min'],  
    ];
}

array:3 [
  ...,
  "username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:30"
  ],
  ...
]

2.2.3.4 prefix($prefix, bool $required = false)

示例 1
$validator = UserValidator::instance()->prefix('user');

dump($validator->rules());

array:4 [
  "user" => array:1 [
    0 => "array"
  ]
  "user.id" => array:3 [
    0 => "required"
    1 => "integer"
    2 => "min:0"
  ]
  "user.username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:30"
  ]
  "user.password" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:50"
  ]
]
示例2: 表單陣列
$validator = UserValidator::instance()->prefix('users.*', true);

dump($validator->rules());

array:5 [
  "users" => array:2 [
    0 => "required"
    1 => "array"
  ]
  "users.*" => array:2 [
    0 => "required"
    1 => "array"
  ]
  "users.*.id" => array:3 [
    0 => "required"
    1 => "integer"
    2 => "min:0"
  ]
  "users.*.username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:30"
  ]
  "users.*.password" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:50"
  ]
]

2.2.3.5 addRule(string $key, array $rules) 追加規則

$validator = UserValidator::instance()->addRule('password_confirm', ['required']);

dump($validator->rules());

array:4 [
  "id" => array:3 [
    0 => "required"
    1 => "integer"
    2 => "min:0"
  ]
  "username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:30"
  ]
  "password" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:50"
  ]
  "password_confirm" => array:1 [
    0 => "required"
  ]
]

2.2.3.6 exclude(array $rules) 排除規則

$validator = UserValidator::instance()->exclude(['id', 'password' => ['required']]);

dump($validator->rules());

array:2 [
  "username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:30"
  ]
  "password" => array:2 [
    0 => "string"
    1 => "max:50"
  ]
]

3 中級用法

3.1 scene(string ...$scene)

scene() 規則覆蓋 $rules 規則。

<?php

namespace Tests\Validators\Mysql;

class UserInfoValidator extends \GordenSong\Support\TableValidator
{
    protected $connection = 'mysql';
    protected $table = 'user_info';

    public function customizeRules(): array
    {
        return [
            'id' => ['required'],
            'user_id' => ['required'],
            'xing' => [],
            'ming' => [],
            'age' => ['required'],
            'config1' => ['required'],
            'config2' => ['required'],
        ];
    }

    protected $scenes = [
        'edit-name' => ['user_id' => 'required', 'xing' => 'required', 'ming' => 'required'],
        'edit-age' => ['user_id' => 'required', 'age'],
    ];
}

示例 1

$validator = UserInfoValidator::instance()->scene('edit-name');
dump($validator->rules());

array:3 [
  "user_id" => array:4 [
    0 => "required"
    1 => "integer"
    2 => "min:-2147483648"
    3 => "max:2147483647"
  ]
  "xing" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
  "ming" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
]

示例 2

$validator = UserInfoValidator::instance()->scene('edit-name', 'edit-age');
dump($validator->rules());

array:4 [
  "user_id" => array:4 [
    0 => "required"
    1 => "integer"
    2 => "min:-2147483648"
    3 => "max:2147483647"
  ]
  "xing" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
  "ming" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
  "age" => array:4 [
    0 => "required"
    1 => "integer"
    2 => "min:-2147483648"
    3 => "max:2147483647"
  ]
]

3.2 macro

Validator::macro('validate', function (array $data) {
    /** @var Factory $factory */
    $factory = app(Factory::class);

    /** @var Validator $this */
    return $factory->validate($data, $this->rules(), $this->messages(), $this->attributes());
});

$data = [
    'name' => 'a',
    'age' => 10,
];
$validated = (new AuthorValidator())->validate($data);

self::assertSame($data, $validated);
Collection::macro('validate', function (array $data) {
    /** @var Factory $factory */
    $factory = app(Factory::class);

    /** @var Validator $this */
    return $factory->validate($data, $this->rules(), $this->messages(), $this->attributes());
});

$data = [
    'name' => 'a',
    'age' => 10,
];
$validated = Collection::make(new AuthorValidator())->validate($data);

self::assertSame($data, $validated);

你也可以 $validator->validateRequest(),你想怎麼用怎麼用!

4 高階用法

Validator, Collection 就像一個個積木,發揮你的想象力,使用它吧。

$collection = Collection::make([
    Collection::make([
        UserValidator::instance(),
        EmailValidator::instance(),
        QqValidator::instance()
    ])->prefix('user'),
    UserInfoValidator::instance()->exclude(['id', 'user_id'])->prefix('user_info'),
    UserAddressValidator::instance()->exclude(['id', 'user_id'])->prefix('user_address.*'),
]);
dump($collection->rules());

array:22 [
  "user" => array:1 [
    0 => "array"
  ]
  "user.id" => array:3 [
    0 => "required"
    1 => "integer"
    2 => "min:0"
  ]
  "user.username" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:30"
  ]
  "user.password" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:50"
  ]
  "user.email" => array:2 [
    0 => "string"
    1 => "email"
  ]
  "user.qq" => array:1 [
    0 => "numeric"
  ]
  "user_info" => array:1 [
    0 => "array"
  ]
  "user_info.xing" => array:2 [
    0 => "string"
    1 => "max:255"
  ]
  "user_info.ming" => array:2 [
    0 => "string"
    1 => "max:255"
  ]
  "user_info.age" => array:4 [
    0 => "required"
    1 => "integer"
    2 => "min:-2147483648"
    3 => "max:2147483647"
  ]
  "user_info.config1" => array:2 [
    0 => "required"
    1 => "json"
  ]
  "user_info.config2" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:65535"
  ]
  "user_address" => array:1 [
    0 => "array"
  ]
  "user_address.*" => array:1 [
    0 => "array"
  ]
  "user_address.*.province" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
  "user_address.*.city" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
  "user_address.*.district" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
  "user_address.*.address" => array:3 [
    0 => "required"
    1 => "string"
    2 => "max:255"
  ]
]

Command

vendor:publish

php artisan vendor:publish
# select Provider: GordenSong\Providers\LaravelTableValidatorServiceProvider
# publish table-validator.php to /config/

validator

validator
  validator:cache {connection}                  Generate one or more table validator cache
  validator:make {table*} {--connection=mysql} [options] <table>...  Generate one or more table validators
生成 Validator

預設檔案位置: app/Validators/BooksValidator.php

php artisan validator:make .         # 為預設資料庫所有的表自動生成 Validator 
php artisan validator:make tablename # 為指定表生成 Validator
php artisan validator:make tablename --force # overwrite the file
php artisan validator:make tablename --force --connection=mysql 
快取表規則

根據 config/table-validator.php 設定規則快取生成目錄。

php artisan validator:cache {connection} {-d}

-d 刪除快取
# bootstrap/cache/table-validator/{$connection}/{$table}.php
php artisan validator:cache mysql
本作品採用《CC 協議》,轉載必須註明作者和本文連結