Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update some templates and improve tests #1516

Merged
merged 1 commit into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/03-handling-exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ try {
The code above generates the following output:

```no-highlight
- All the required rules must pass for "The Respect Panda"
- "The Respect Panda" must pass all the rules
- "The Respect Panda" must contain only letters (a-z) and digits (0-9)
- "The Respect Panda" must contain only lowercase letters
```
Expand All @@ -44,7 +44,7 @@ The code above generates the following output:
```no-highlight
Array
(
[__root__] => All the required rules must pass for "The Respect Panda"
[__root__] => "The Respect Panda" must pass all the rules
[alnum] => "The Respect Panda" must contain only letters (a-z) and digits (0-9)
[lowercase] => "The Respect Panda" must contain only lowercase letters
)
Expand Down
22 changes: 13 additions & 9 deletions docs/rules/AllOf.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ v::allOf(v::intVal(), v::positive())->isValid(15); // true

### `AllOf::TEMPLATE_SOME`

| Mode | Template |
|------------|----------------------------------------|
| `default` | These rules must pass for {{name}} |
| `inverted` | These rules must not pass for {{name}} |
Used when some rules must be failed.

### `AllOf::TEMPLATE_NONE`
| Mode | Template |
|------------|------------------------------|
| `default` | {{name}} must pass the rules |
| `inverted` | {{name}} must pass the rules |

| Mode | Template |
|------------|-----------------------------------------------|
| `default` | All the required rules must pass for {{name}} |
| `inverted` | None of these rules must pass for {{name}} |
### `AllOf::TEMPLATE_ALL`

Used when all rules have failed.

| Mode | Template |
|------------|----------------------------------|
| `default` | {{name}} must pass all the rules |
| `inverted` | {{name}} must pass all the rules |

## Template placeholders

Expand Down
8 changes: 4 additions & 4 deletions docs/rules/AnyOf.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ so `AnyOf()` returns true.

### `AnyOf::TEMPLATE_STANDARD`

| Mode | Template |
|------------|--------------------------------------------------------|
| `default` | At least one of these rules must pass for {{name}} |
| `inverted` | At least one of these rules must not pass for {{name}} |
| Mode | Template |
|------------|----------------------------------------------|
| `default` | {{name}} must pass at least one of the rules |
| `inverted` | {{name}} must pass at least one of the rules |

## Template placeholders

Expand Down
4 changes: 2 additions & 2 deletions docs/rules/Attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ v::attributes()->assert(new Person('John Doe', '[email protected]', '2020-06-23

v::attributes()->assert(new Person('', 'not an email', 'not a date', 'not a phone number'));
// Full message:
// - All the required rules must pass for `Person { +$name="" +$email="not an email" +$birthdate="not a date" +$phone="not a phone number" }`
// - `Person { +$name="" +$email="not an email" +$birthdate="not a date" +$phone="not a phone number" }` must pass all the rules
// - name must not be empty
// - email must be a valid email address
// - All the required rules must pass for birthdate
// - birthdate must pass all the rules
// - birthdate must be a valid date in the format "2005-12-30"
// - For comparison with now, birthdate must be a valid datetime
// - phone must be a valid telephone number or must be null
Expand Down
21 changes: 16 additions & 5 deletions docs/rules/NoneOf.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,23 @@ In the sample above, 'foo' isn't a integer nor a float, so noneOf returns true.

## Templates

### `NoneOf::TEMPLATE_STANDARD`
### `NoneOf::TEMPLATE_SOME`

| Mode | Template |
|------------|--------------------------------------------|
| `default` | None of these rules must pass for {{name}} |
| `inverted` | All of these rules must pass for {{name}} |
Used when some rules have passed.

| Mode | Template |
|------------|------------------------------|
| `default` | {{name}} must pass the rules |
| `inverted` | {{name}} must pass the rules |

### `NoneOf::TEMPLATE_ALL`

Used when all rules have passed.

| Mode | Template |
|------------|----------------------------------|
| `default` | {{name}} must pass all the rules |
| `inverted` | {{name}} must pass all the rules |

## Template placeholders

Expand Down
21 changes: 16 additions & 5 deletions docs/rules/OneOf.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,23 @@ character, one or the other, but not neither nor both.

## Templates

### `OneOf::TEMPLATE_STANDARD`
### `OneOf::TEMPLATE_NONE`

| Mode | Template |
|------------|----------------------------------------------------|
| `default` | Only one of these rules must pass for {{name}} |
| `inverted` | Only one of these rules must not pass for {{name}} |
Used when none of the rules have passed.

| Mode | Template |
|------------|-------------------------------------|
| `default` | {{name}} must pass one of the rules |
| `inverted` | {{name}} must pass one of the rules |

### `OneOf::TEMPLATE_MORE_THAN_ONE`

Used when more than one rule has passed.

| Mode | Template |
|------------|------------------------------------------|
| `default` | {{name}} must pass only one of the rules |
| `inverted` | {{name}} must pass only one of the rules |

## Template placeholders

Expand Down
14 changes: 7 additions & 7 deletions library/Rules/AllOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'These rules must pass for {{name}}',
'These rules must not pass for {{name}}',
'{{name}} must pass the rules',
'{{name}} must pass the rules',
self::TEMPLATE_SOME,
)]
#[Template(
'All the required rules must pass for {{name}}',
'None of these rules must pass for {{name}}',
self::TEMPLATE_NONE,
'{{name}} must pass all the rules',
'{{name}} must pass all the rules',
self::TEMPLATE_ALL,
)]
final class AllOf extends Composite
{
public const TEMPLATE_NONE = '__none__';
public const TEMPLATE_ALL = '__all__';
public const TEMPLATE_SOME = '__some__';

public function evaluate(mixed $input): Result
Expand All @@ -43,7 +43,7 @@ public function evaluate(mixed $input): Result
$failed = array_filter($children, static fn (Result $result): bool => !$result->isValid);
$template = self::TEMPLATE_SOME;
if (count($children) === count($failed)) {
$template = self::TEMPLATE_NONE;
$template = self::TEMPLATE_ALL;
}

return (new Result($valid, $input, $this, [], $template))->withChildren(...$children);
Expand Down
4 changes: 2 additions & 2 deletions library/Rules/AnyOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'At least one of these rules must pass for {{name}}',
'At least one of these rules must not pass for {{name}}',
'{{name}} must pass at least one of the rules',
'{{name}} must pass at least one of the rules',
)]
final class AnyOf extends Composite
{
Expand Down
27 changes: 20 additions & 7 deletions library/Rules/NoneOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,37 @@
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Composite;

use function array_filter;
use function array_map;
use function array_reduce;
use function count;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'None of these rules must pass for {{name}}',
'All of these rules must pass for {{name}}',
'{{name}} must pass the rules',
'{{name}} must pass the rules',
self::TEMPLATE_SOME,
)]
#[Template(
'{{name}} must pass all the rules',
'{{name}} must pass all the rules',
self::TEMPLATE_ALL,
)]
final class NoneOf extends Composite
{
public const TEMPLATE_ALL = '__all__';
public const TEMPLATE_SOME = '__some__';

public function evaluate(mixed $input): Result
{
$children = array_map(
static fn (Rule $rule) => $rule->evaluate($input)->withInvertedMode(),
$this->rules
);
$children = array_map(static fn (Rule $rule) => $rule->evaluate($input)->withInvertedMode(), $this->rules);
$valid = array_reduce($children, static fn (bool $carry, Result $result) => $carry && $result->isValid, true);
$failed = array_filter($children, static fn (Result $result): bool => !$result->isValid);
$template = self::TEMPLATE_SOME;
if (count($children) === count($failed)) {
$template = self::TEMPLATE_ALL;
}

return (new Result($valid, $input, $this))->withChildren(...$children);
return (new Result($valid, $input, $this, [], $template))->withChildren(...$children);
}
}
30 changes: 27 additions & 3 deletions library/Rules/OneOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,45 @@
use Respect\Validation\Rule;
use Respect\Validation\Rules\Core\Composite;

use function array_filter;
use function array_map;
use function array_reduce;
use function count;
use function usort;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'Only one of these rules must pass for {{name}}',
'Only one of these rules must not pass for {{name}}',
'{{name}} must pass one of the rules',
'{{name}} must pass one of the rules',
self::TEMPLATE_NONE,
)]
#[Template(
'{{name}} must pass only one of the rules',
'{{name}} must pass only one of the rules',
self::TEMPLATE_MORE_THAN_ONE,
)]
final class OneOf extends Composite
{
public const TEMPLATE_NONE = '__none__';
public const TEMPLATE_MORE_THAN_ONE = '__more_than_one__';

public function evaluate(mixed $input): Result
{
$children = array_map(static fn (Rule $rule) => $rule->evaluate($input), $this->rules);
$valid = array_reduce($children, static fn (bool $carry, Result $result) => $carry xor $result->isValid, false);
$validChildren = array_filter($children, static fn (Result $result): bool => $result->isValid);
$template = self::TEMPLATE_NONE;
if (count($validChildren) > 1) {
// Put the failed children at the top, so it makes sense in the main error message
usort($children, static fn (Result $a, Result $b): int => $a->isValid <=> $b->isValid);

$template = self::TEMPLATE_MORE_THAN_ONE;
$children = array_map(
static fn (Result $child) => $child->isValid ? $child->withInvertedValidation() : $child,
$children
);
}

return (new Result($valid, $input, $this))->withChildren(...$children);
return (new Result($valid, $input, $this, [], $template))->withChildren(...$children);
}
}
6 changes: 3 additions & 3 deletions tests/feature/AssertWithKeysTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@
],
]),
<<<'FULL_MESSAGE'
- All the required rules must pass for the given data
- All the required rules must pass for mysql
- the given data must pass all the rules
- mysql must pass all the rules
- host must be a string
- user must be present
- password must be present
- schema must be a string
- All the required rules must pass for postgresql
- postgresql must pass all the rules
- host must be present
- user must be a string
- password must be a string
Expand Down
6 changes: 3 additions & 3 deletions tests/feature/AssertWithPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ function (): void {
->assert($object);
},
<<<'FULL_MESSAGE'
- All the required rules must pass for the given data
- These rules must pass for mysql
- the given data must pass all the rules
- mysql must pass the rules
- host must be a string
- These rules must pass for postgresql
- postgresql must pass the rules
- user must be a string
FULL_MESSAGE,
));
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
test('Scenario #1', expectFullMessage(
fn() => Validator::stringType()->lengthBetween(2, 15)->assert(0),
<<<'FULL_MESSAGE'
- All the required rules must pass for 0
- 0 must pass all the rules
- 0 must be a string
- 0 must be a countable value or a string
FULL_MESSAGE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function (): void {
->assert(['username' => 'u', 'birthdate' => 'Not a date', 'password' => '']);
},
[
'__root__' => 'All the required rules must pass for `["username": "u", "birthdate": "Not a date", "password": ""]`',
'__root__' => '`["username": "u", "birthdate": "Not a date", "password": ""]` must pass all the rules',
'username' => 'The length of username must be between 2 and 32',
'birthdate' => 'birthdate must be a valid date/time',
'password' => 'password must not be empty',
Expand Down
6 changes: 3 additions & 3 deletions tests/feature/GetMessagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@
->key('schema', v::stringType()))
->assert(['mysql' => ['host' => 42, 'schema' => 42], 'postgresql' => ['user' => 42, 'password' => 42]]),
[
'__root__' => 'All the required rules must pass for `["mysql": ["host": 42, "schema": 42], "postgresql": ["user": 42, "password": 42]]`',
'__root__' => '`["mysql": ["host": 42, "schema": 42], "postgresql": ["user": 42, "password": 42]]` must pass all the rules',
'mysql' => [
'__root__' => 'All the required rules must pass for mysql',
'__root__' => 'mysql must pass all the rules',
'host' => 'host must be a string',
'user' => 'user must be present',
'password' => 'password must be present',
'schema' => 'schema must be a string',
],
'postgresql' => [
'__root__' => 'All the required rules must pass for postgresql',
'__root__' => 'postgresql must pass all the rules',
'host' => 'host must be present',
'user' => 'user must be a string',
'password' => 'password must be a string',
Expand Down
6 changes: 3 additions & 3 deletions tests/feature/GetMessagesWithReplacementsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ function (): void {
);
},
[
'__root__' => 'All the required rules must pass for `["mysql": ["host": 42, "schema": 42], "postgresql": ["user": 42, "password": 42]]`',
'__root__' => '`["mysql": ["host": 42, "schema": 42], "postgresql": ["user": 42, "password": 42]]` must pass all the rules',
'mysql' => [
'__root__' => 'All the required rules must pass for mysql',
'__root__' => 'mysql must pass all the rules',
'host' => '`host` should be a MySQL host',
'user' => 'Value should be a MySQL username',
'password' => 'password must be present',
'schema' => 'schema must be a string',
],
'postgresql' => [
'__root__' => 'All the required rules must pass for postgresql',
'__root__' => 'postgresql must pass all the rules',
'host' => 'host must be present',
'user' => 'user must be a string',
'password' => 'password must be a string',
Expand Down
8 changes: 4 additions & 4 deletions tests/feature/Issues/Issue1289Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@
]),
'default must be a string',
<<<'FULL_MESSAGE'
- These rules must pass for `["default": 2, "description": [], "children": ["nope"]]`
- Only one of these rules must pass for default
- `["default": 2, "description": [], "children": ["nope"]]` must pass the rules
- default must pass one of the rules
- default must be a string
- default must be a boolean
- description must be a string value
FULL_MESSAGE,
[
0 => [
'__root__' => 'These rules must pass for `["default": 2, "description": [], "children": ["nope"]]`',
'__root__' => '`["default": 2, "description": [], "children": ["nope"]]` must pass the rules',
'default' => [
'__root__' => 'Only one of these rules must pass for default',
'__root__' => 'default must pass one of the rules',
'stringType' => 'default must be a string',
'boolType' => 'default must be a boolean',
],
Expand Down
4 changes: 2 additions & 2 deletions tests/feature/Issues/Issue1333Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
fn() => v::noWhitespace()->email()->setName('User Email')->assert('not email'),
'User Email must not contain whitespaces',
<<<'FULL_MESSAGE'
- All the required rules must pass for User Email
- User Email must pass all the rules
- User Email must not contain whitespaces
- User Email must be a valid email address
FULL_MESSAGE,
[
'__root__' => 'All the required rules must pass for User Email',
'__root__' => 'User Email must pass all the rules',
'noWhitespace' => 'User Email must not contain whitespaces',
'email' => 'User Email must be a valid email address',
],
Expand Down
Loading
Loading