Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dive-be/laravel-dry-requests
Check if your requests would pass validation if you executed them normally
https://github.com/dive-be/laravel-dry-requests
Last synced: 2 months ago
JSON representation
Check if your requests would pass validation if you executed them normally
- Host: GitHub
- URL: https://github.com/dive-be/laravel-dry-requests
- Owner: dive-be
- License: mit
- Archived: true
- Created: 2022-04-02T16:17:10.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2023-05-03T13:05:20.000Z (over 1 year ago)
- Last Synced: 2024-10-14T00:51:06.284Z (3 months ago)
- Language: PHP
- Size: 201 KB
- Stars: 205
- Watchers: 2
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-inertiajs - Dry requests - Generic Laravel package for real-time form validation. (Resources / Packages)
README
> **Warning** two months after the release of our package,
> [Taylor Otwell announced an almost identical functionality](https://youtu.be/f4QShF42c6E?t=20679) as a core package.
> Since this package has pretty much been made obsolete, we have decided to stop maintaining it.
>
> So, please consider migrating to [Laravel Precognition](https://github.com/laravel/framework/pull/44339).# X-Dry-Run your requests
[![Latest Version on Packagist](https://img.shields.io/packagist/v/dive-be/laravel-dry-requests.svg?style=flat-square)](https://packagist.org/packages/dive-be/laravel-dry-requests)
[![Total Downloads](https://img.shields.io/packagist/dt/dive-be/laravel-dry-requests.svg?style=flat-square)](https://packagist.org/packages/dive-be/laravel-dry-requests)This package 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.
## Showcase
![LDR Demo](./art/demo.gif)
## What problem does this package solve?
A traditional approach to validating user input in JavaScript applications (Inertia / SPA / Mobile) is using a library such as **yup**
to do the heavy lifting and delegating complex business validations to the server.However, the client-side can never be trusted, so you can't simply omit the validation rules that ran on the front-end.
This means that validation has to live in 2 distinct places and you will have to keep them in sync.
This is very tedious and wasteful, so this is where this package comes into play.## Installation
You can install the package via composer:
```bash
composer require dive-be/laravel-dry-requests
```You can publish the config file with:
```bash
php artisan vendor:publish --provider="Dive\DryRequests\ServiceProvider" --tag="config"
```This is the contents of the published config file:
```php
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',
];
```## 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 `FormRequest`s
`Controller` injecting a `StoreUserRequest`:
```php
class UserController
{
public function store(StoreUserRequest $request): UserResource
{
$user = User::create($request->validated());return new UserResource($user);
}
}
```Add the `DryRunnable` trait to your `FormRequest`:
```php
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'],
];
}
}
```That's it 😎.
### Option 2 - using `validate` method on the `Request` object
```php
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);
}
}
```You don't have to do anything at all 🤙.
### Front-end execution
Now, hit the endpoint from the client-side like you normally would.
But with the added `X-Dry-Run` header.```js
// 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: '[email protected]' }, { headers: { 'X-Dry-Run': true } })
.then(response => response.status); // 200 OK// 4. Submit entire form: Controller executed
axios.post('/users', { email: '[email protected]', username: 'Asil Kan' })
.then(response => response.status); // 201 Created
```### Inertia.js example
```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
```
### Fine-tuning Dry Validations: `AllFailures` / `FirstFailure`
- The default validation behavior for dry requests is halting validation as soon as an error is found.
This is especially useful when handling async validation for a **single field**.
- The other option is to keep validating even if an error is encountered.
This is especially useful for multi-step forms.You can alter this behavior on 3 distinct levels.
1. Change `first` to `all` (or vice versa) in the `dry-request` config file. This will apply to all of your requests.
2. **FormRequest only** - Use the `Dive\DryRequests\Dry` attribute along with `Dive\DryRequests\Validation` on the `rules` method
to force a specific `Validation` behavior for a particular `FormRequest`.
```php
#[Dry(Validation::AllFailures)]
public function rules(): array
{
return [...];
}
```
3. Dictate the behavior on the fly from the front-end using the `X-Dry-Run` header. Valid values: `all`, `first`.
```php
axios.post('/users', { email: '...', username: '...' }, { headers: { 'X-Dry-Run': 'all' } })
.then(response => response.status); // 200 OK
```
*Note: the header value will be ignored if you have explicitly set a validation behavior on the `FormRequest` using the `Dry` attribute.*### Conflicting `FormRequest` methods
The package makes use of the available methods `passedValidation` and `withValidator` available on `FormRequest` classes to enable its behavior.
If you define these in your own requests, you must call the "dry" methods manually:
```php
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
}
}
```## Testing
```bash
composer test
```## Upgrading
Please see [UPGRADING](UPGRADING.md) for details.
## Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
## Security
If you discover any security related issues, please email [email protected] instead of using the issue tracker.
## Credits
- [Muhammed Sari](https://github.com/mabdullahsari)
- [All Contributors](../../contributors)## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.