Skip to content

Commit

Permalink
Merge pull request #4 from IndexZer0/feature/map
Browse files Browse the repository at this point in the history
Feature/map
  • Loading branch information
IndexZer0 authored Mar 8, 2024
2 parents 341f503 + 4f53042 commit 2f60fa0
Show file tree
Hide file tree
Showing 21 changed files with 492 additions and 204 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

All notable changes to `laravel-validation-provider` will be documented in this file.

## v2.1.0 - 2024-03-08
### Added
- Ability to define validation `rules`, `messages` and `attributes` by class properties.
- New core validation provider.
- `MapAttributesValidationProvider`.
- Fluent API.
- `map()`
- Documentation updates.

## v2.0.0 - 2024-03-05
### Added
- Fluent API.
Expand Down
178 changes: 107 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,12 @@ use IndexZer0\LaravelValidationProvider\Facades\ValidationProvider;

class AuthorValidationProvider extends AbstractValidationProvider
{
public function rules(): array
{
return ['name' => ['required'],];
}
protected array $rules = ['name' => ['required']];
}

class BookValidationProvider extends AbstractValidationProvider
{
public function rules(): array
{
return ['title' => ['required',],];
}
protected array $rules = ['title' => ['required']];
}

$validationProvider = ValidationProvider::make([
Expand All @@ -56,9 +50,11 @@ $validationProvider->rules();
- [Installation](#installation)
- [Usage](#usage)
- [Defining Validation Providers](#defining-validation-providers)
- [Via Properties](#via-properties)
- [Via Methods](#via-methods)
- [Creating Validation Providers](#creating-validation-providers)
- [Manual Creation](#manual-creation)
- [Facade](#facade)
- [Manual Creation](#manual-creation)
- [Fluent API](#fluent-api)
- [Service/Action Class Usage](#serviceaction-class-usage)
- [Form Requests Usage](#form-requests-usage)
Expand All @@ -70,6 +66,7 @@ $validationProvider->rules();
- [Array Validation Provider](#array-validation-provider)
- [Custom Validation Provider](#custom-validation-provider)
- [Exclude Attributes Validation Provider](#exclude-attributes-validation-provider)
- [Map Attributes Validation Provider](#map-attributes-validation-provider)
- [Digging Deeper](#digging-deeper)
- [Using Fluent API](#using-fluent-api)
- [Using Facade](#using-facade)
Expand Down Expand Up @@ -106,18 +103,43 @@ composer require indexzer0/laravel-validation-provider
- Create granular representations of domain concepts in validation provider classes.
- Should extend `AbstractValidationProvider`.

#### Via Properties

`$rules`, `$messages`, `$attributes`

```php
class AddressValidationProvider extends AbstractValidationProvider
{
protected array $rules = [
'post_code' => ['required', 'string', 'between:1,20'],
];

protected array $messages = [];

protected array $attributes = [];
}
```

#### Via Methods

- You can also define methods `rules()`, `messages()`, `attributes()`.
- Sometimes you need to dynamically define rules, messages and attributes.
- You are using a [dependent](https://github.com/laravel/framework/blob/5e95946a8283a8d5c015035793f9c61c297e937f/src/Illuminate/Validation/Validator.php#L236) rule.
- See [Dependent Rules](#dependent-rules) for more info.

```php
class AddressValidationProvider extends AbstractValidationProvider
{
public function rules(): array
{
return [
'post_code' => ['required', 'string', 'between:1,20'],
'post_code' => ['required', 'string', "regex:" . RegexHelper::getPostCodeRegex()],
'zip_code' => ["same:{$this->dependentField('post_code')}"]
];
}

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

public function attributes(): array { return []; }
}
```
Expand All @@ -126,53 +148,33 @@ class AddressValidationProvider extends AbstractValidationProvider

### Creating Validation Providers

There are 3 ways to create validation providers.
There are 3 ways to create validation providers. `Facade`, `Manual Creation`, and `Fluent API`.

In all 3 examples, were going to use the following two defined validation providers along-side this packages core validation providers to achieve validation rules of:
In all 3 examples, were going to use the following two defined validation providers along-side this packages [core validation providers](#available-validation-providers) to achieve validation rules of:

```php
class AuthorValidationProvider extends AbstractValidationProvider
{
public function rules(): array
{
return ['name' => ['required'],];
}
protected array $rules = ['name' => ['required'],];
}

class BookValidationProvider extends AbstractValidationProvider
{
public function rules(): array
{
return ['title' => ['required',],];
}
protected array $rules = ['title' => ['required',],];
}

// Desired validation rules:
// [
// 'author.name' => ['required'],
// 'author.books' => ['required', 'array', 'min:1', 'max:2',],
// 'author.books' => ['required', 'array', 'min:1', 'max:2'],
// 'author.books.*.title' => ['required'],
// ]
```

- [Manual Creation](#manual-creation)
- [Facade](#facade)
- [Manual Creation](#manual-creation)
- [Fluent API](#fluent-api)

#### Manual Creation

```php
$validationProvider = new NestedValidationProvider(
'author',
new AggregateValidationProvider(
new AuthorValidationProvider(),
new CustomValidationProvider(['books' => ['required', 'array', 'min:1', 'max:2',],]),
new ArrayValidationProvider('books', new BookValidationProvider())
)
);
$validationProvider->rules();
```

#### Facade

```php
Expand All @@ -181,11 +183,35 @@ use IndexZer0\LaravelValidationProvider\Facades\ValidationProvider;
$validationProvider = ValidationProvider::make([
'author' => [
AuthorValidationProvider::class,
new CustomValidationProvider(['books' => ['required', 'array', 'min:1', 'max:2',],]),
new CustomValidationProvider(['books' => ['required', 'array', 'min:1', 'max:2']]),
new ArrayValidationProvider('books', new BookValidationProvider()),
],
]);
$validationProvider->rules();
// [
// 'author.name' => ['required'],
// 'author.books' => ['required', 'array', 'min:1', 'max:2'],
// 'author.books.*.title' => ['required'],
// ]
```

#### Manual Creation

```php
$validationProvider = new NestedValidationProvider(
'author',
new AggregateValidationProvider(
new AuthorValidationProvider(),
new CustomValidationProvider(['books' => ['required', 'array', 'min:1', 'max:2']]),
new ArrayValidationProvider('books', new BookValidationProvider())
)
);
$validationProvider->rules();
// [
// 'author.name' => ['required'],
// 'author.books' => ['required', 'array', 'min:1', 'max:2'],
// 'author.books.*.title' => ['required'],
// ]
```

#### Fluent API
Expand All @@ -195,10 +221,15 @@ $validationProvider->rules();
```php
$validationProvider = (new BookValidationProvider())
->nestedArray('books')
->with(new CustomValidationProvider(['books' => ['required', 'array', 'min:1', 'max:2',],]))
->with(new CustomValidationProvider(['books' => ['required', 'array', 'min:1', 'max:2']]))
->with(AuthorValidationProvider::class)
->nested('author');
$validationProvider->rules();
// [
// 'author.name' => ['required'],
// 'author.books' => ['required', 'array', 'min:1', 'max:2'],
// 'author.books.*.title' => ['required'],
// ]
```

---
Expand Down Expand Up @@ -286,6 +317,7 @@ This package provides core classes that give you the ability to compose your val
- [Array Validation Provider](#array-validation-provider)
- [Custom Validation Provider](#custom-validation-provider)
- [Exclude Attributes Validation Provider](#exclude-attributes-validation-provider)
- [Map Attributes Validation Provider](#map-attributes-validation-provider)

#### Aggregate Validation Provider

Expand Down Expand Up @@ -341,7 +373,7 @@ $validationProvider->rules();
```php
class CustomValidationProvider extends AbstractValidationProvider {}
$customRules = [
'books' => ['required', 'array', 'min:1', 'max:2',],
'books' => ['required', 'array', 'min:1', 'max:2'],
];
$customMessages = [
'books.required' => 'Provide :attribute'
Expand All @@ -352,7 +384,7 @@ $customAttributes = [
$validationProvider = new CustomValidationProvider($customRules, $customMessages, $customAttributes);
$validationProvider->rules();
// [
// 'books' => ['required', 'array', 'min:1', 'max:2',],
// 'books' => ['required', 'array', 'min:1', 'max:2'],
// ]
```

Expand All @@ -365,13 +397,31 @@ class ExcludeAttributesValidationProvider extends AbstractValidationProvider {}
$validationProvider = new ExcludeAttributesValidationProvider(
['one'],
new CustomValidationProvider([
'one' => ['required',],
'two' => ['required',],
'one' => ['required'],
'two' => ['required']
])
);
$validationProvider->rules();
// [
// 'two' => ['required',],
// 'two' => ['required'],
// ]
```

#### Map Attributes Validation Provider

- Sometimes you may want to rename an attribute.

```php
class MapAttributesValidationProvider extends AbstractValidationProvider {}
$validationProvider = new MapAttributesValidationProvider(
['one' => 'two'],
new CustomValidationProvider([
'one' => ['required'],
])
);
$validationProvider->rules();
// [
// 'two' => ['required'],
// ]
```

Expand All @@ -387,6 +437,7 @@ $validationProvider->rules();
| `nestedArray(string $nestedKey)` | `ArrayValidationProvider` |
| <code>with(string&#124;ValidationProvider $validationProvider)</code> | `AggregateValidationProvider` |
| `exclude(array $attributes)` | `ExcludeAttributesValidationProvider` |
| `map(array $attributes)` | `MapAttributesValidationProvider` |

#### Using Facade

Expand Down Expand Up @@ -471,16 +522,6 @@ Let's look at the example of 3 routes and how you could reuse your Validation Pr
- Route: address
- Stores address information
- Uses `AddressValidationProvider`
- Route: contact-details
- Stores contact information
- Uses `ContactValidationProvider`
- Route: profile
- Stores address **and** contact information.
- Uses
- `NestedValidationProvider`
- `AggregateValidationProvider`
- `AddressValidationProvider`
- `ContactValidationProvider`

```php
/*
Expand All @@ -501,7 +542,9 @@ class StoreAddressRequest extends ValidationProviderFormRequest
}
}
```

- Route: contact-details
- Stores contact information
- Uses `ContactValidationProvider`
```php
/*
* ------------------
Expand All @@ -521,6 +564,13 @@ class StoreContactDetailsRequest extends ValidationProviderFormRequest
}
}
```
- Route: profile
- Stores address **and** contact information.
- Uses
- `AddressValidationProvider`
- `ContactValidationProvider`
- `NestedValidationProvider` (under the hood from Facade)
- `AggregateValidationProvider` (under the hood from Facade)

```php
/*
Expand All @@ -537,21 +587,6 @@ class StoreProfileRequest extends ValidationProviderFormRequest
{
public function prepareForValidation()
{
$this->validationProvider = new NestedValidationProvider(
'profile',
new AggregateValidationProvider(
new NestedValidationProvider(
'address',
new AddressValidationProvider()
),
new NestedValidationProvider(
'contact',
new ContactValidationProvider()
),
)
);

// or using Facade
$this->validationProvider = ValidationProvider::make([
'profile' => [
'address' => AddressValidationProvider::class
Expand Down Expand Up @@ -630,6 +665,7 @@ class NestedValidationProvider extends AbstractValidationProvider {}
class ArrayValidationProvider extends NestedValidationProvider {}
class CustomValidationProvider extends AbstractValidationProvider {}
class ExcludeAttributesValidationProvider extends AbstractValidationProvider {}
class MapAttributesValidationProvider extends AbstractValidationProvider {}

// Form Request
class ValidationProviderFormRequest extends \Illuminate\Foundation\Http\FormRequest {}
Expand Down
1 change: 1 addition & 0 deletions src/Contracts/ValidationProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ public function nested(string $nestedKey): ValidationProvider;
public function nestedArray(string $nestedKey): ValidationProvider;
public function with(string|ValidationProvider $validationProvider): ValidationProvider;
public function exclude(array $attributes): ValidationProvider;
public function map(array $attributes): ValidationProvider;
}
26 changes: 26 additions & 0 deletions src/Helpers/ArrayHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace IndexZer0\LaravelValidationProvider\Helpers;

class ArrayHelper
{
/*
* Copy of Arr::mapWithKeys() because function isn't in laravel 10 lowest version.
*/
public static function mapWithKeys(array $array, callable $callback)
{
$result = [];

foreach ($array as $key => $value) {
$assoc = $callback($value, $key);

foreach ($assoc as $mapKey => $mapValue) {
$result[$mapKey] = $mapValue;
}
}

return $result;
}
}
Loading

0 comments on commit 2f60fa0

Please sign in to comment.