Blog Detail

10

Oct
Check if your Requests Would Pass Validation after Execution cover image

arrow_back Check if your Requests Would Pass Validation after Execution

Laravel Dry Requests is an amazing package that allows you to check if your requests would pass validation if you executed them normally. The Laravel equivalent of --dry-run in various CLI tools, or what some devs call “preflight requests”.

? Hit the endpoint as users are entering information on the form to provide real-time feedback with 100% accuracy.

? Validate only a subset of data of a multi-step form to guarantee success when the form is eventually submitted.

Installation

You can install the package via composer:

composer require dive-be/laravel-dry-requests

You can publish the config file with:

php artisan vendor:publish --provider="Dive\DryRequests\ServiceProvider" --tag="config"

Behavior

? Controller logic is not executed after a successful validation attempt. 200 OK is returned upon a successful dry run.

? Only present fields are validated to ensure good UX. Other fields are skipped using the sometimes rule. This means that you are responsible for only sending the relevant fields for validating e.g. a step of a multi-step wizard.

Usage

Assume the following endpoint: POST /users and Controller.

Option 1 - using FormRequests

Controller injecting a StoreUserRequest:

class UserController
{
    public function store(StoreUserRequest $request): UserResource
    {
        $user = User::create($request->validated());

        return new UserResource($user);
    }
}
Add the DryRunnable trait to your 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'],
        ];
    }
}

Option 2 - using validate method on the Request object

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);
    }
}

Front-end execution

Now, hit the endpoint from the client-side like you normally would. But with the added X-Dry-Run header.

// 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 example

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} />

For more details, Please visit Github

Published at : 10-10-2022

Author : Rizwan Aslam
AUTHOR
Rizwan Aslam

I am a highly results-driven professional with 12+ years of collective experience in the grounds of web application development especially in laravel, native android application development in java, and desktop application development in the dot net framework. Now managing a team of expert developers at Codebrisk.

Launch your project

Launch project