dive-be/laravel-dry-requests

此包已被弃用,不再维护。作者建议使用 laravel/framework 包。

运行 Laravel 请求的干运行

2.3.0 2023-05-03 11:38 UTC

This package is auto-updated.

Last update: 2023-05-03 13:05:25 UTC


README

警告 在我们包发布两个月后,Taylor Otwell 宣布了一个几乎相同功能的内核包。由于这个包几乎已经被废弃,我们决定停止维护它。

因此,请考虑迁移到 Laravel Precognition

Social Card of Laravel Dry Requests

X-干运行您的请求

Latest Version on Packagist Total Downloads

此包允许您检查在正常执行时,您的请求是否可以通过验证。Laravel 各类 CLI 工具中 --dry-run 的 Laravel 相当物,或某些开发者称之为“预飞请求”。

🚀 当用户在表单中输入信息时,点击端点以提供100%准确性的实时反馈。

🚀 仅验证多步表单的数据子集,以确保表单最终提交时成功。

展示

LDR Demo

此包解决了什么问题?

在 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 /usersController

选项 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

  • 干请求的默认验证行为是发现错误时立即停止验证。这对于处理单个字段的异步验证特别有用。
  • 另一种选择是即使遇到错误也继续验证。这对于多步骤表单特别有用。

您可以在三个不同的级别上更改此行为。

  1. dry-request 配置文件中将 first 改为 all(或反之),这将适用于您所有的请求。
  2. 仅 FormRequest - 在 rules 方法中使用 Dive\DryRequests\Dry 属性和 Dive\DryRequests\Validation,以强制为特定的 FormRequest 设置特定的 Validation 行为。
#[Dry(Validation::AllFailures)]
public function rules(): array
{
    return [...];
}
  1. 使用 X-Dry-Run 标头从前端动态指定行为。有效值:allfirst
axios.post('/users', { email: '...', username: '...' }, { headers: { 'X-Dry-Run': 'all' } })
     .then(response => response.status); // 200 OK

注意:如果您使用 Dry 属性在 FormRequest 上显式设置了验证行为,则将忽略标头值。

冲突的 FormRequest 方法

该包使用 FormRequest 类上可用的 passedValidationwithValidator 方法来实现其行为。

如果您在自己的请求中定义了这些方法,则必须手动调用 "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)。有关更多信息,请参阅 许可证文件