Skip to content

Commit

Permalink
Adopt to last changes yiisoft\validator (#176)
Browse files Browse the repository at this point in the history
* Add support of checking nested attributes, add more tests
  • Loading branch information
terabytesoftw committed Feb 11, 2022
1 parent 9843468 commit c7a7a96
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 10 deletions.
3 changes: 1 addition & 2 deletions composer.json
Expand Up @@ -52,8 +52,7 @@
"config": {
"sort-packages": true,
"allow-plugins": {
"infection/extension-installer": true,
"composer/package-versions-deprecated": true
"infection/extension-installer": true
}
},
"scripts": {
Expand Down
22 changes: 15 additions & 7 deletions src/FormModel.php
Expand Up @@ -18,6 +18,7 @@
use function array_key_exists;
use function explode;
use function is_subclass_of;
use function property_exists;
use function strpos;

/**
Expand Down Expand Up @@ -133,7 +134,9 @@ public function getFormName(): string

public function hasAttribute(string $attribute): bool
{
return array_key_exists($attribute, $this->attributes);
[$attribute, $nested] = $this->getNestedAttribute($attribute);

return $nested !== null ? true : array_key_exists($attribute, $this->attributes);
}

/**
Expand Down Expand Up @@ -201,9 +204,10 @@ public function processValidationResult(Result $result): void
$this->validated = false;

foreach ($result->getErrorMessagesIndexedByAttribute() as $attribute => $errors) {
$this->formErrors->clear($attribute);
/** @psalm-suppress InvalidArgument */
$this->addErrors([$attribute => $errors]);
if ($this->hasAttribute($attribute)) {
$this->formErrors->clear($attribute);
$this->addErrors([$attribute => $errors]);
}
}

$this->validated = true;
Expand Down Expand Up @@ -241,7 +245,7 @@ protected function collectAttributes(): array
}

/**
* @psalm-param array<string, array<array-key, string>> $items
* @psalm-param non-empty-array<string, non-empty-list<string>> $items
*/
private function addErrors(array $items): void
{
Expand Down Expand Up @@ -355,13 +359,17 @@ private function getNestedAttribute(string $attribute): array

[$attribute, $nested] = explode('.', $attribute, 2);

/** @var object|string */
$attributeNested = $this->attributes[$attribute] ?? null;
/** @var string */
$attributeNested = $this->attributes[$attribute] ?? '';

if (!is_subclass_of($attributeNested, self::class)) {
throw new InvalidArgumentException("Attribute \"$attribute\" is not a nested attribute.");
}

if (!property_exists($attributeNested, $nested)) {
throw new InvalidArgumentException("Undefined property: \"$attributeNested::$nested\".");
}

return [$attribute, $nested];
}

Expand Down
19 changes: 19 additions & 0 deletions tests/FormModelTest.php
Expand Up @@ -153,6 +153,25 @@ public function testHasAttribute(): void
$this->assertFalse($form->hasAttribute('extraField'));
}

public function testHasNestedAttribute(): void
{
$form = new FormWithNestedAttribute();

$this->assertTrue($form->hasAttribute('user.login'));
$this->assertTrue($form->hasAttribute('user.password'));
$this->assertTrue($form->hasAttribute('user.rememberMe'));
$this->assertFalse($form->hasAttribute('noexist'));
}

public function testHasNestedAttributeException(): void
{
$form = new FormWithNestedAttribute();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Undefined property: "Yiisoft\Form\Tests\TestSupport\Form\LoginForm::noexist".');
$form->hasAttribute('user.noexist');
}

public function testLoad(): void
{
$form = new LoginForm();
Expand Down
20 changes: 20 additions & 0 deletions tests/Helper/HtmlFormErrorsTest.php
Expand Up @@ -7,6 +7,7 @@
use PHPUnit\Framework\TestCase;
use Yiisoft\Form\Helper\HtmlFormErrors;
use Yiisoft\Form\Tests\TestSupport\Form\LoginForm;
use Yiisoft\Form\Tests\TestSupport\Form\FormWithNestedAttribute;
use Yiisoft\Form\Tests\TestSupport\TestTrait;

final class HtmlFormErrorsTest extends TestCase
Expand Down Expand Up @@ -113,4 +114,23 @@ public function testGetErrorSummaryOnlyAttributes(): void
HtmlFormErrors::getErrorSummary($formModel, ['password']),
);
}

public function testGetErrorNestedAttribute(): void
{
$formModel = new FormWithNestedAttribute();
$validator = $this->createValidatorMock();
$this->assertTrue($formModel->load(['FormWithNestedAttribute' => ['user.login' => 'ad']]));
$this->assertFalse($validator->validate($formModel)->isValid());
$this->assertSame(
['id' => 'Value cannot be blank.', 'user.login' => 'Is too short.'],
HtmlFormErrors::getFirstErrors($formModel),
);
$this->assertSame(['Is too short.'], HtmlFormErrors::getErrors($formModel, 'user.login'));
$this->assertSame(['Is too short.'], HtmlFormErrors::getErrorSummary($formModel, ['user.login']));
$this->assertSame(
['id' => 'Value cannot be blank.', 'user.login' => 'Is too short.'],
HtmlFormErrors::getErrorSummaryFirstErrors($formModel),
);
$this->assertSame('Is too short.', HtmlFormErrors::getFirstError($formModel, 'user.login'));
}
}
20 changes: 20 additions & 0 deletions tests/TestSupport/Form/EachForm.php
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Form\Tests\TestSupport\Form;

use Yiisoft\Form\FormModel;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\RuleSet;

final class EachForm extends FormModel
{
private array $names = [];

public function getRules(): array
{
return ['names' => [Each::rule(new RuleSet([HasLength::rule()->max(10)]))]];
}
}
7 changes: 6 additions & 1 deletion tests/TestSupport/Form/FormWithNestedAttribute.php
Expand Up @@ -5,6 +5,7 @@
namespace Yiisoft\Form\Tests\TestSupport\Form;

use Yiisoft\Form\FormModel;
use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Required;

final class FormWithNestedAttribute extends FormModel
Expand Down Expand Up @@ -42,7 +43,11 @@ public function getAttributePlaceholders(): array
public function getRules(): array
{
return [
'id' => Required::rule(),
'id' => [Required::rule()],
'user.login' => [
Required::rule(),
HasLength::rule()->min(3)->tooShortMessage('Is too short.'),
],
];
}

Expand Down
32 changes: 32 additions & 0 deletions tests/Validator/EachValidatorTest.php
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Form\Tests\Validator;

use PHPUnit\Framework\TestCase;
use Yiisoft\Form\Helper\HtmlFormErrors;
use Yiisoft\Form\Tests\TestSupport\Form\EachForm;
use Yiisoft\Form\Tests\TestSupport\TestTrait;

final class EachValidatorTest extends TestCase
{
use TestTrait;

/**
* @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
*/
public function testEach(): void
{
$eachForm = new EachForm();
$validator = $this->createValidatorMock();

$eachForm->setAttribute('names', ['wilmer', 'pedrovitelek', 'samdark']);
$result = $validator->validate($eachForm);

$this->assertSame(
['names' => ['This value should contain at most {max, number} {max, plural, one{character} other{characters}}. pedrovitelek given.']],
HtmlFormErrors::getAllErrors($eachForm),
);
}
}

0 comments on commit c7a7a96

Please sign in to comment.