Skip to content

Commit

Permalink
Fix #206: Combine flat and each rules in attributes (#207)
Browse files Browse the repository at this point in the history
  • Loading branch information
arogachev committed Apr 13, 2022
1 parent 8f2b336 commit 1f21d2e
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 12 deletions.
22 changes: 22 additions & 0 deletions README.md
Expand Up @@ -266,6 +266,28 @@ final class Coordinates
}
```

To combine both flat rules and "each" rules, specify `Each` explicitly and place flat rules above "each" ones:

```php
use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Attribute\HasMany;
use Yiisoft\Validator\Attribute\HasOne;
use Yiisoft\Validator\Rule\Number;

final class Point
{
#[HasOne(Coordinates::class)]
private $coordinates;
#[Count(exactly: 3)]
#[Each()]
#[Number(min: 0, max: 255)]
private array $rgb;
}
```

In this example `Count` will be applied to the whole value and `Number` - for each item.

Here are some technical details:

- `HasOne` uses `Nested` rule.
Expand Down
26 changes: 14 additions & 12 deletions src/DataSet/AttributeDataSet.php
Expand Up @@ -14,8 +14,6 @@
use Yiisoft\Validator\RuleInterface;
use Yiisoft\Validator\RulesProviderInterface;

use function in_array;

/**
* This data set makes use of attributes introduced in PHP 8. It simplifies rules configuration process, especially for
* nested data and relations. Please refer to the guide for example.
Expand Down Expand Up @@ -79,33 +77,37 @@ private function handleAttributes(ReflectionClass $classMeta): array
}
}

$flatRules = [];
$eachRuleFound = false;
$eachRules = [];
$attributes = $property->getAttributes();
foreach ($attributes as $attribute) {
if (!is_subclass_of($attribute->getName(), RuleInterface::class)) {
continue;
}

if (in_array($attribute->getName(), [Each::class, Nested::class])) {
if ($attribute->getName() === Each::class) {
$eachRuleFound = true;

continue;
}

$flatRules[] = $attribute->newInstance();
}
if ($attribute->getName() === Nested::class) {
continue;
}

if (!$flatRules) {
continue;
/** @psalm-suppress UndefinedMethod */
$eachRuleFound
? $eachRules[] = $attribute->newInstance()
: $rules[$property->getName()][] = $attribute->newInstance();
}

if ((string) $property->getType() !== 'array') {
$rules[$property->getName()] = $flatRules;

if (!$eachRules || (string) $property->getType() !== 'array') {
continue;
}

/** @psalm-suppress UndefinedMethod */
$rules[$property->getName()][] = new Each(
$flatRules,
$eachRules,
...(($property->getAttributes(Each::class)[0] ?? null)?->getArguments() ?? [])
);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/Data/Charts/Point.php
Expand Up @@ -5,6 +5,7 @@
namespace Yiisoft\Validator\Tests\Data\Charts;

use Yiisoft\Validator\Attribute\HasOne;
use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Nested;
use Yiisoft\Validator\Rule\Number;
Expand All @@ -14,6 +15,7 @@ final class Point
#[Nested(errorWhenPropertyPathIsNotFound: true, propertyPathIsNotFoundMessage: 'Custom message 4.')]
#[HasOne(Coordinates::class)]
private $coordinates;
#[Count(exactly: 3)]
#[Each(incorrectInputMessage: 'Custom message 5.', message: 'Custom message 6.')]
#[Number(min: 0, max: 255)]
private array $rgb;
Expand Down
2 changes: 2 additions & 0 deletions tests/DataSet/AttributeDataSetTest.php
Expand Up @@ -6,6 +6,7 @@

use PHPUnit\Framework\TestCase;
use Yiisoft\Validator\DataSet\AttributeDataSet;
use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Nested;
Expand Down Expand Up @@ -33,6 +34,7 @@ public function testGetRules(): void
propertyPathIsNotFoundMessage: 'Custom message 4.'
),
'rgb' => [
new Count(exactly: 3),
new Each([
new Number(min: 0, max: 255),
], incorrectInputMessage: 'Custom message 5.', message: 'Custom message 6.'),
Expand Down

0 comments on commit 1f21d2e

Please sign in to comment.