Skip to content

Commit

Permalink
Improve the "Feature Guide" documentation
Browse files Browse the repository at this point in the history
Because the document was quite big already, I split the exception
handling part to another document.
  • Loading branch information
henriquemoody committed Dec 4, 2024
1 parent eb459ad commit 3833ad7
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 220 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
The most awesome validation engine ever created for PHP.

- Complex rules made simple: `v::numericVal()->positive()->between(1, 255)->isValid($input)`.
- [Granularity control](docs/02-feature-guide.md#validation-methods) for advanced reporting.
- [More than 150](docs/08-list-of-rules-by-category.md) (fully tested) validation rules.
- [A concrete API](docs/05-concrete-api.md) for non fluent usage.
- [Granularity control](docs/03-handling-exceptions.md) for advanced reporting.
- [More than 150](docs/09-list-of-rules-by-category.md) (fully tested) validation rules.
- [A concrete API](docs/06-concrete-api.md) for non fluent usage.

Learn More:

Expand Down
2 changes: 1 addition & 1 deletion bin/update-doc-links
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ create_list_of_rules()
echo "## Alphabetically"
echo
ls -1 "${DOCS_DIREECTORY}/rules/" | sort | sed -E 's,^(.+).md$,- [\1](rules/\1.md),'
} > "${DOCS_DIREECTORY}/08-list-of-rules-by-category.md"
} > "${DOCS_DIREECTORY}/09-list-of-rules-by-category.md"
}

link_related_rules()
Expand Down
258 changes: 51 additions & 207 deletions docs/02-feature-guide.md
Original file line number Diff line number Diff line change
@@ -1,258 +1,102 @@
# Feature Guide

## Namespace import

Respect\Validation is namespaced, but you can make your life easier by importing
a single class into your context:
We'll use `v` as an alias for `Respect\Validation\Validator` to keep things simple:

```php
use Respect\Validation\Validator as v;
```

## Simple validation

The Hello World validator is something like this:

```php
$number = 123;
v::numericVal()->isValid($number); // true
```

## Chained validation

It is possible to use validators in a chain. Sample below validates a string
containing numbers and letters, no whitespace and length between 1 and 15.

```php
$usernameValidator = v::alnum()->noWhitespace()->length(1, 15);
$usernameValidator->isValid('alganet'); // true
```

## Validating object properties

Given this simple object:

```php
$user = new stdClass;
$user->name = 'Alexandre';
$user->birthdate = '1987-07-01';
```

Is possible to validate its properties in a single chain:

```php
$userValidator = v::property('name', v::stringType()->length(1, 32))
->property('birthdate', v::dateTimeDiff(v::greaterThanOrEqual(18), 'years'));

$userValidator->isValid($user); // true
```

Validating array keys is also possible using `v::key()`

Note that we used `v::stringType()` and `v::dateTime()` in the beginning of the validator.
Although is not mandatory, it is a good practice to use the type of the
validated object as the first node in the chain.

## Validating array keys and values

Validating array keys into another array is also possible using [Key](rules/Key.md).

If we got the array below:

```php
$data = [
'parentKey' => [
'field1' => 'value1',
'field2' => 'value2'
'field3' => true,
]
];
```
## Validating using booleans

Using the next combination of rules, we can validate child keys.
With the `isValid()` method, determine if your input meets a specific validation rule.

```php
v::key(
'parentKey',
v::key('field1', v::stringType())
->key('field2', v::stringType())
->key('field3', v::boolType())
)
->assert($data); // You can also use check() or validate()
if (v::intType()->positive()->isValid($input)) {
echo 'The input you gave me is a positive integer';
} else {
echo 'The input you gave me is not a positive integer';
}
```

## Input optional
Note that you can combine multiple rules for a complex validation.
## Validating using exceptions

On oldest versions of Respect\Validation all validators treat input as optional
and accept an empty string input as valid. Even though a useful feature that
caused a lot of troubles for our team and neither was an obvious behavior. Also
there was some people who likes to accept `null` as optional value, not only an
empty string.
The `assert()` method throws an exception when validation fails. You can handle those exceptions with `try/catch` for more robust error handling.

For that reason all rules are mandatory now but if you want to treat a value as
optional you can use `v::optional()` rule:
### Basic example

```php
v::alpha()->isValid(''); // false input required
v::alpha()->isValid(null); // false input required

v::optional(v::alpha())->isValid(''); // true
v::optional(v::alpha())->isValid(null); // true
v::intType()->positive()->assert($input);
```

By _optional_ we consider `null` or an empty string (`''`).

See more on [Optional](rules/UndefOr.md).

## Negating rules
### Custom templates

You can use the `v::not()` to negate any rule:
Define your own error message when the validation fails:

```php
v::not(v::intVal())->isValid(10); // false, input must not be integer
v::between(1, 256)->assert($input, '{{name}} is not what I was expecting');
```

## Validator reuse
### Custom templates per rule

Once created, you can reuse your validator anywhere. Remember `$usernameValidator`?
Provide unique messages for each rule in a chain:

```php
$usernameValidator->isValid('respect'); //true
$usernameValidator->isValid('alexandre gaigalas'); // false
$usernameValidator->isValid('#$%'); //false
v::alnum()->lowercase()->assert($input, [
'alnum' => 'Your username must contain only letters and digits',
'lowercase' => 'Your username must be lowercase',
]);
```

## Exception types

- `Respect\Validation\Exceptions\Exception`:
- All exceptions implement this interface;
- `Respect\Validation\Exceptions\ValidationException`:
- Implements the `Respect\Validation\Exceptions\Exception` interface
- Thrown when the `check()` fails
- All validation exceptions extend this class
- Available methods:
- `getMessage()`;
- `updateMode($mode)`;
- `updateTemplate($template)`;
- `Respect\Validation\Exceptions\NestedValidationException`:
- Extends the `Respect\Validation\Exceptions\ValidationException` class
- Usually thrown when the `assert()` fails
- Available methods:
- `getFullMessage()`;
- `getMessages()`;

## Informative exceptions

When something goes wrong, Validation can tell you exactly what's going on. For this,
we use the `assert()` method instead of `validate()`:
### Custom exception objects

Integrate your own exception objects when the validation fails:
```php
use Respect\Validation\Exceptions\NestedValidationException;

try {
$usernameValidator->assert('really messed up screen#name');
} catch(NestedValidationException $exception) {
echo $exception->getFullMessage();
}
v::alnum()->assert($input, new DomainException('Not a valid username'));
```

The printed message is exactly this, as a nested Markdown list:

```no-highlight
- All of the required rules must pass for "really messed up screen#name"
- "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
- "really messed up screen#name" must not contain whitespace
- "really messed up screen#name" must have a length between 1 and 15
```
## Inverting validation rules

## Getting all messages as an array

If you want to get all the messages as an array you can use `getMessages()` for
that. The `getMessages()` method returns an array with all the messages.
Use the `not` prefix to invert a validation rule.

```php
try {
$usernameValidator->assert('really messed up screen#name');
} catch(NestedValidationException $exception) {
print_r($exception->getMessages());
}
v::notEquals('main')->assert($input);
```

The `getMessages()` returns an array in which the keys are the name of the
validators, or its reference in case you are using [Key](rules/Key.md) or
[Property](rules/Property.md) rule:
For more details, check the [Not](rules/Not.md) rule documentation.

```no-highlight
Array
(
[alnum] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
[noWhitespace] => "really messed up screen#name" must not contain whitespace
[length] => "really messed up screen#name" must have a length between 1 and 15
)
```
## Reusing validators

## Custom messages

Getting messages as an array is fine, but sometimes you need to customize them
in order to present them to the user. This is possible using the `getMessages()`
method as well by passing the templates as an argument:
Validators can be created once and reused across multiple inputs.

```php
try {
$usernameValidator->assert('really messed up screen#name', [
'alnum' => '{{name}} must contain only letters and digits',
'noWhitespace' => '{{name}} cannot contain spaces',
'length' => '{{name}} must not have more than 15 chars',
]);
} catch(NestedValidationException $exception) {
print_r($exception->getMessages());
}
```

For all messages, the `{{name}}` variable is available for templates. If you do
not define a name it uses the input to replace this placeholder.
$validator = v::alnum()->lowercase();

The result of the code above will be:

```no-highlight
Array
(
[alnum] => "really messed up screen#name" must contain only letters and digits
[noWhitespace] => "really messed up screen#name" cannot contain spaces
[length] => "really messed up screen#name" must not have more than 15 chars
)
$validator->assert('respect');
$validator->assert('validation');
$validator->assert('alexandre gaigalas');
```

Note that `getMessage()` will only return a message when the specific validation
in the chain fails.

## Validator name
## Customising validator names

On `v::property()` and `v::key()`, `{{name}}` is the property/key name. For others,
is the same as the input. You can customize a validator name using:
Template messages include the placeholder `{{name}}`, which defaults to the input. Use `setName()` to replace it with a more descriptive label.

```php
v::dateTime('Y-m-d')->between('1980-02-02', 'now')->setName('Member Since');
v::dateTime('Y-m-d')
->between('1980-02-02', 'now')
->setName('Age')
->assert($input);
```

## Validation methods
## Smart input handling

We've seen `validate()` that returns true or false and `assert()` that throws a complete
validation report. There is also a `check()` method that returns an Exception
only with the first error found:
Respect\Validation offers over 150 rules, many of which are designed to address common input handling scenarios. Here’s a quick guide to some specific use cases and the rules that make validation straightforward.

```php
use Respect\Validation\Exceptions\ValidationException;

try {
$usernameValidator->check('really messed up screen#name');
} catch(ValidationException $exception) {
echo $exception->getMessage();
}
```

Message:

```no-highlight
"really messed up screen#name" must contain only letters (a-z) and digits (0-9)
```
* Validating arrays: [Key](rules/Key.md), [KeyOptional](rules/KeyOptional.md), and [KeyExists](rules/KeyExists.md).
* Validating array structures: [KeySet](rules/KeySet.md).
* Validating object properties: [Property](rules/Property.md), [PropertyOptional](rules/PropertyOptional.md), and [PropertyExists](rules/PropertyExists.md).
* Validating only when input is not `null`: [NullOr](rules/NullOr.md).
* Validating only when input is not `null` or an empty string: [UndefOr](rules/UndefOr.md).
* Validating the length of the input: [Length](rules/Length.md).
* Validating the maximum value of the input: [Max](rules/Max.md).
* Validating the minimum value of the input: [Min](rules/Min.md).
Loading

0 comments on commit 3833ad7

Please sign in to comment.