dive-be / laravel-dry-requests
Requires
- php: ^8.1
- dive-be/php-enum-utils: ^1.1
- dive-be/php-utils: ^0.1.0
- laravel/framework: ^10.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.8
- nunomaduro/larastan: ^2.6
- orchestra/testbench: ^8.5
- pestphp/pest: ^2.6
- pestphp/pest-plugin-laravel: ^2.0
README
警告 在我们包发布两个月后,Taylor Otwell 宣布了一个几乎相同功能的内核包。由于这个包几乎已经被废弃,我们决定停止维护它。
因此,请考虑迁移到 Laravel Precognition。
X-干运行您的请求
此包允许您检查在正常执行时,您的请求是否可以通过验证。Laravel 各类 CLI 工具中 --dry-run
的 Laravel 相当物,或某些开发者称之为“预飞请求”。
🚀 当用户在表单中输入信息时,点击端点以提供100%准确性的实时反馈。
🚀 仅验证多步表单的数据子集,以确保表单最终提交时成功。
展示
此包解决了什么问题?
在 JavaScript 应用程序(Inertia / SPA / 移动)中,验证用户输入的传统方法是使用像 yup 这样的库来做重负载,并将复杂的业务验证委托给服务器。
然而,客户端永远不能被信任,所以你不能简单地省略在客户端运行的验证规则。这意味着验证必须存在于两个不同的地方,并且你必须保持它们同步。这非常繁琐且浪费,这就是这个包发挥作用的地方。
安装
您可以通过 composer 安装此包。
composer require dive-be/laravel-dry-requests
您可以使用以下命令发布配置文件:
php artisan vendor:publish --provider="Dive\DryRequests\ServiceProvider" --tag="config"
这是发布配置文件的内容
return [ /* |-------------------------------------------------------------------------- | Default Dry Validation Behavior |-------------------------------------------------------------------------- | | All dry requests are validated against a subset of the defined rules. | In other words only present fields are checked during the request. | You may choose to halt validation as soon as a failure occurs, | or continue validating all fields and return all failures. | | Supported: "all", "first" | */ 'validation' => 'first', ];
行为
💡 在成功验证尝试后,不会执行 Controller
逻辑。在成功干运行后返回 200 OK
。
💡 仅验证显示字段以确保良好的用户体验。其他字段使用 sometimes
规则跳过。这意味着您负责只发送相关字段进行验证,例如多步向导的某个步骤。
用法
假设以下端点: POST /users
和 Controller
。
选项 1 - 使用 FormRequest
Controller
注入一个 StoreUserRequest
class UserController { public function store(StoreUserRequest $request): UserResource { $user = User::create($request->validated()); return new UserResource($user); } }
将 DryRunnable
特性添加到您的 FormRequest
class StoreUserRequest extends FormRequest { use DryRunnable; public function rules(): array { return [ 'email' => ['required', 'email', 'max:255', 'unique:users'], 'username' => ['required', 'string', 'min:2', 'max:255', 'unique:users'], 'nickname' => ['nullable', 'string', 'min:2', 'max:255'], ]; } }
就是这样 😎。
选项 2 - 在 Request
对象上使用 validate
方法
class UserController { public function store(Request $request): UserResource { $validated = $request->validate([ 'email' => ['required', 'email', 'max:255', 'unique:users'], 'username' => ['required', 'string', 'min:2', 'max:255', 'unique:users'], 'nickname' => ['nullable', 'string', 'min:2', 'max:255'], ]); $user = User::create($request->validated()); return new UserResource($user); } }
你根本不需要做任何事情 🤙。
前端执行
现在,像平常一样从客户端调用端点。但是添加了 X-Dry-Run
标头。
// 1. "Username has already been taken" validation error axios.post('/users', { username: 'Agent007' }, { headers: { 'X-Dry-Run': true } }) .then(response => response.status); // 422 Unprocessable Entity // 2. Successful unique username check: Controller did not execute axios.post('/users', { username: 'Asil Kan' }, { headers: { 'X-Dry-Run': true } }) .then(response => response.status); // 200 OK // 3. Successful unique e-mail check: Controller did not execute axios.post('/users', { email: 'muhammed@dive.be' }, { headers: { 'X-Dry-Run': true } }) .then(response => response.status); // 200 OK // 4. Submit entire form: Controller executed axios.post('/users', { email: 'muhammed@dive.be', username: 'Asil Kan' }) .then(response => response.status); // 201 Created
Inertia.js 示例
const { clearErrors, data, errors, setData } = useForm({ email: '', password: '', password_confirmation: '', }); const pick = (obj, fields) => fields.reduce((acc, cur) => (acc[cur] = obj[cur], acc), {}); const validateAsync = (...fields) => () => { Inertia.post(route('register'), pick(data, fields) , { headers: { 'X-Dry-Run': 'all' }, onError: setError, onSuccess() { clearErrors(...fields); }, }); } // Somewhere in your template <input onBlur={validateAsync('email')} type="email" name="email" value={data.email} onChange={setData} /> <input type="password" name="password" value={data.password} onChange={setData} /> <input onBlur={validateAsync('password', 'password_confirmation')} type="password" name="password_confirmation" value={data.password_confirmation} onChange={setData} />
微调Dry验证:AllFailures
/ FirstFailure
- 干请求的默认验证行为是发现错误时立即停止验证。这对于处理单个字段的异步验证特别有用。
- 另一种选择是即使遇到错误也继续验证。这对于多步骤表单特别有用。
您可以在三个不同的级别上更改此行为。
- 在
dry-request
配置文件中将first
改为all
(或反之),这将适用于您所有的请求。 - 仅 FormRequest - 在
rules
方法中使用Dive\DryRequests\Dry
属性和Dive\DryRequests\Validation
,以强制为特定的FormRequest
设置特定的Validation
行为。
#[Dry(Validation::AllFailures)] public function rules(): array { return [...]; }
- 使用
X-Dry-Run
标头从前端动态指定行为。有效值:all
、first
。
axios.post('/users', { email: '...', username: '...' }, { headers: { 'X-Dry-Run': 'all' } }) .then(response => response.status); // 200 OK
注意:如果您使用 Dry
属性在 FormRequest
上显式设置了验证行为,则将忽略标头值。
冲突的 FormRequest
方法
该包使用 FormRequest
类上可用的 passedValidation
和 withValidator
方法来实现其行为。
如果您在自己的请求中定义了这些方法,则必须手动调用 "dry" 方法。
class CreateUserRequest extends FormRequest { protected function passedValidation() { $this->stopWhenDry(); // must be called first // your custom logic } protected function withValidator(Validator $instance) { $instance = /* your custom logic */; $this->withDryValidator($instance); // must be called last } }
测试
composer test
升级
有关详细信息,请参阅 UPGRADING。
变更日志
有关最近更改的更多信息,请参阅 CHANGELOG。
贡献
有关详细信息,请参阅 CONTRIBUTING。
安全
如果您发现任何安全相关的问题,请通过电子邮件 oss@dive.be 联系我们,而不是使用问题跟踪器。
致谢
许可证
MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件。