Skip to content

Commit

Permalink
Fix #84: Add method onlyAttributes() to ErrorSummary::class. (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
terabytesoftw committed Jan 25, 2022
1 parent 9125f77 commit 27d47de
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 43 deletions.
99 changes: 63 additions & 36 deletions docs/error-summary.md
Expand Up @@ -68,11 +68,11 @@ use Yiisoft\Form\Widget\Text;
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Text::widget()->config($formModel, 'name') ?>
<?= Text::widget()->config($formModel, 'email') ?>
<?= Text::widget()->for($formModel, 'name') ?>
<?= Text::widget()->for($formModel, 'email') ?>
<?= ErrorSummary::widget()->model($formModel) ?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= SubmitButton::widget()->attributes(['class' => 'button is-block is-info is-fullwidth'])->value('Save') ?>
<?= Form::end(); ?>
```

Expand All @@ -81,32 +81,28 @@ That would generate the following code before validation:
```html
<form action="widgets" method="POST" _csrf="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="hidden" name="_csrf" value="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="text" id="testform-name" name="TestForm[name]">
<input type="text" id="testform-name" name="TestForm[name]" minlength="4">
<input type="text" id="testform-email" name="TestForm[email]">
<div style="display:none"><p>Please fix the following errors:</p><ul></ul></div>
<hr class="mt-3">
<div>
<input type="submit" id="submit-14082948982001" class="button is-block is-info is-fullwidth" name="submit-14082948982001" value="Save">
</div>
<input type="submit" id="w1-submit" class="button is-block is-info is-fullwidth" name="w1-submit" value="Save">
</form>
```

That would generate the following code after validation:
```html
<form action="widgets" method="POST" _csrf="9moR1LM6d8JXgqYRLTD_ba2D-fQNRSyXTddnFUQIU1XFXWi61m408iDU_mhMaJ4127TIjk4fQ-MLsDUhDUkZPg==">
<input type="hidden" name="_csrf" value="9moR1LM6d8JXgqYRLTD_ba2D-fQNRSyXTddnFUQIU1XFXWi61m408iDU_mhMaJ4127TIjk4fQ-MLsDUhDUkZPg==">
<input type="text" id="testform-name" name="TestForm[name]">
<form action="widgets" method="POST" _csrf="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="hidden" name="_csrf" value="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="text" id="testform-name" name="TestForm[name]" minlength="4">
<input type="text" id="testform-email" name="TestForm[email]">
<div><p>Please fix the following errors:</p>
<div>
<p>Please fix the following errors:</p>
<ul>
<li>This value is not a valid email address.</li>
<li>Is too short.</li>
</ul>
</div>
<hr class="mt-3">
<div>
<input type="submit" id="submit-14687568353001" class="button is-block is-info is-fullwidth" name="submit-14687568353001" value="Save">
</div>
<input type="submit" id="w1-submit" class="button is-block is-info is-fullwidth" name="w1-submit" value="Save">
</form>
```

Expand All @@ -132,53 +128,83 @@ use Yiisoft\Form\Widget\Text;
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Text::widget()->config($formModel, 'name') ?>
<?= Text::widget()->config($formModel, 'email') ?>
<?= Text::widget()->for($formModel, 'name') ?>
<?= Text::widget()->for($formModel, 'email') ?>
<?= ErrorSummary::widget()
->attributes(['class' => 'has-text-danger'])
->footer('Custom Footer:')
->header('Custom Header:')
->model($formModel)
?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= SubmitButton::widget()->attributes(['class' => 'button is-block is-info is-fullwidth'])->value('Save') ?>
<?= Form::end(); ?>
```

That would generate the following code before validation:
That would generate the following code after validation:

```html
<form action="widgets" method="POST" _csrf="SNh_ZumgopPv9wpiFCMw3IioeIbaQ9w54dYmh0XRanl77wYIjPTho5ihUht1e1GE_p9J_JkZs02nsXSzDJAgEg==">
<input type="hidden" name="_csrf" value="SNh_ZumgopPv9wpiFCMw3IioeIbaQ9w54dYmh0XRanl77wYIjPTho5ihUht1e1GE_p9J_JkZs02nsXSzDJAgEg==">
<input type="text" id="testform-name" name="TestForm[name]">
<form action="widgets" method="POST" _csrf="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="hidden" name="_csrf" value="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="text" id="testform-name" name="TestForm[name]" minlength="4">
<input type="text" id="testform-email" name="TestForm[email]">
<div class="has-text-danger" style="display:none">Custom Header:<ul></ul>Custom Footer:</div>
<hr class="mt-3">
<div>
<input type="submit" id="submit-20289061442001" class="button is-block is-info is-fullwidth" name="submit-20289061442001" value="Save">
<div class="has-text-danger">
<p>Custom Header:</p>
<ul>
<li>This value is not a valid email address.</li>
<li>Is too short.</li>
</ul>
<p>Custom Footer:</p>
</div>
<hr class="mt-3">
<input type="submit" id="w1-submit" class="button is-block is-info is-fullwidth" name="w1-submit" value="Save">
</form>
```

### Only attributes

```php
<?php

declare(strict_types=1);

use Yiisoft\Form\FormModelInterface;
use Yiisoft\Form\Widget\ErrorSummary;
use Yiisoft\Form\Widget\Field;
use Yiisoft\Form\Widget\Form;
use Yiisoft\Form\Widget\Text;

/**
* @var FormModelInterface $data
* @var object $csrf
*/
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Text::widget()->for($formModel, 'name') ?>
<?= Text::widget()->for($formModel, 'email') ?>
<?= ErrorSummary::widget()->attributes(['class' => 'has-text-danger'])->onlyAttributes('email')->model($formModel) ?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= Form::end(); ?>
```


That would generate the following code after validation:

```html
<form action="widgets" method="POST" _csrf="PwqhEiBBULqjUwjVQFBIGDt2iral9LmaJNOkfEJe-BcMPdh8RRUTitQFUKwhCClATUG7zOau1u5itPZICx-yfA==">
<input type="hidden" name="_csrf" value="PwqhEiBBULqjUwjVQFBIGDt2iral9LmaJNOkfEJe-BcMPdh8RRUTitQFUKwhCClATUG7zOau1u5itPZICx-yfA==">
<input type="text" id="testform-name" name="TestForm[name]">
<form action="widgets" method="POST" _csrf="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="hidden" name="_csrf" value="vWob09HzPhcTDuhOHVU71VJQAAymEm2Hysn_8QN1Y8qOXWK9tKd9J2RYsDd8DVqNJGcxduVIAvOMrq3FSjQpoQ==">
<input type="text" id="testform-name" name="TestForm[name]" minlength="4">
<input type="text" id="testform-email" name="TestForm[email]">
<div class="has-text-danger">
Custom Header:
<p>Please fix the following errors:</p>
<ul>
<li>This value is not a valid email address.</li>
<li>Is too short.</li>
</ul>
Custom Footer:
</div>
<hr class="mt-3">
<div>
<input type="submit" id="submit-21602903539001" class="button is-block is-info is-fullwidth" name="submit-21602903539001" value="Save">
</div>
<input type="submit" id="w1-submit" class="button is-block is-info is-fullwidth" name="w1-submit" value="Save">
</form>
```

Expand All @@ -192,4 +218,5 @@ Method | Description | Default
`header(string $value)` | Set the header text for the error summary. | `''`
`model(FormModelInterface $formModel)` | Set the model for the error summary. | `null`
`showAllErrors(bool $value)` | Whether to show all errors. | `false`
`function tag(string $value)` | Set the container tag name for the error summary. | `'div'`
`onlyAttributes(array $value)` | Specific attributes to be included in error summary. | `''`
`tag(string $value)` | Set the container tag name for the error summary. | `'div'`
10 changes: 8 additions & 2 deletions src/FormErrors.php
Expand Up @@ -33,9 +33,15 @@ public function getErrors(string $attribute): array
return $this->attributesErrors[$attribute] ?? [];
}

public function getErrorSummary(): array
public function getErrorSummary(array $onlyAttributes = []): array
{
return $this->renderErrorSummary($this->getAllErrors());
$errors = $this->getAllErrors();

if ($onlyAttributes !== []) {
$errors = array_intersect_key($errors, array_flip($onlyAttributes));
}

return $this->renderErrorSummary($errors);
}

public function getErrorSummaryFirstErrors(): array
Expand Down
4 changes: 3 additions & 1 deletion src/FormErrorsInterface.php
Expand Up @@ -64,12 +64,14 @@ public function getErrors(string $attribute): array;
/**
* Returns errors for all attributes as a one-dimensional array.
*
* @param array $onlyAttributes List of attributes to return errors.
*
* @return array errors for all attributes as a one-dimensional array. Empty array is returned if no error.
*
* {@see getErrors()}
* {@see getFirstErrors(){}
*/
public function getErrorSummary(): array;
public function getErrorSummary(array $onlyAttributes): array;

/**
* Returns the first error of every attribute in the collection.
Expand Down
5 changes: 3 additions & 2 deletions src/Helper/HtmlFormErrors.php
Expand Up @@ -50,12 +50,13 @@ public static function getErrorSummaryFirstErrors(FormModelInterface $formModel)
* Returns the errors for all attributes as a one-dimensional array.
*
* @param FormModelInterface $formModel the form object.
* @param array $onlyAttributes list of attributes whose errors should be returned.
*
* @return array errors for all attributes as a one-dimensional array. Empty array is returned if no error.
*/
public static function getErrorSummary(FormModelInterface $formModel): array
public static function getErrorSummary(FormModelInterface $formModel, array $onlyAttributes = []): array
{
return $formModel->getFormErrors()->getErrorSummary();
return $formModel->getFormErrors()->getErrorSummary($onlyAttributes);
}

/**
Expand Down
19 changes: 18 additions & 1 deletion src/Widget/ErrorSummary.php
Expand Up @@ -25,6 +25,7 @@ final class ErrorSummary extends Widget
{
private array $attributes = [];
private bool $encode = true;
private array $onlyAttributes = [];
private FormModelInterface $formModel;
private string $footer = '';
private array $footerAttributes = [];
Expand Down Expand Up @@ -152,6 +153,20 @@ public function showAllErrors(bool $value): self
return $new;
}

/**
* Specific attributes to be filtered out when rendering the error summary.
*
* @param array $values The attributes to be included in error summary.
*
* @return static
*/
public function onlyAttributes(string ...$values): self
{
$new = clone $this;
$new->onlyAttributes = $values;
return $new;
}

/**
* Set the container tag name for the error summary.
*
Expand Down Expand Up @@ -179,7 +194,9 @@ private function collectErrors(): array
$errorMessages = [];

if ($this->showAllErrors) {
$errors = HtmlFormErrors::getErrorSummary($this->formModel);
$errors = HtmlFormErrors::getErrorSummary($this->formModel, $this->onlyAttributes);
} elseif ($this->onlyAttributes !== []) {
$errors = array_intersect_key($errors, array_flip($this->onlyAttributes));
}

/**
Expand Down
16 changes: 16 additions & 0 deletions tests/Helper/HtmlFormErrorsTest.php
Expand Up @@ -97,4 +97,20 @@ public function testHasError(): void
$this->assertFalse($validator->validate($formModel)->isValid());
$this->assertTrue(HtmlFormErrors::hasErrors($formModel));
}

public function testGetErrorSummaryOnlyAttributes(): void
{
$formModel = new LoginForm();
$validator = $this->createValidatorMock();
$this->assertTrue($formModel->load($this->data));
$this->assertFalse($validator->validate($formModel)->isValid());
$this->assertSame(
['This value is not a valid email address.'],
HtmlFormErrors::getErrorSummary($formModel, ['login']),
);
$this->assertSame(
['Is too short.'],
HtmlFormErrors::getErrorSummary($formModel, ['password']),
);
}
}
50 changes: 49 additions & 1 deletion tests/Widget/ErrorSummaryTest.php
Expand Up @@ -32,6 +32,7 @@ public function dataProviderErrorSummary(): array
'',
[],
true,
[],
<<<HTML
<div>
<p class="text-danger">Please fix the following errors:</p>
Expand All @@ -54,6 +55,7 @@ public function dataProviderErrorSummary(): array
'Custom footer',
['class' => 'text-primary'],
true,
[],
<<<HTML
<div>
<p class="text-danger">Custom header</p>
Expand All @@ -66,6 +68,48 @@ public function dataProviderErrorSummary(): array
</div>
HTML,
],
// Set only attributes with showAllErros its `true`.
[
'jac',
'jack@.com',
'A258*f',
[],
'',
['class' => 'text-danger'],
'',
['class' => 'text-primary'],
true,
['name'],
<<<HTML
<div>
<p class="text-danger">Please fix the following errors:</p>
<ul>
<li>Is too short.</li>
</ul>
</div>
HTML,
],
// Set only attributes with showAllErros `false`.
[
'jac',
'jack@.com',
'A258*f',
[],
'',
['class' => 'text-danger'],
'',
['class' => 'text-primary'],
false,
['password'],
<<<HTML
<div>
<p class="text-danger">Please fix the following errors:</p>
<ul>
<li>Must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters.</li>
</ul>
</div>
HTML,
],
];
}

Expand All @@ -80,6 +124,7 @@ public function testImmutability(): void
$this->assertNotSame($errorSummary, $errorSummary->footer(''));
$this->assertNotSame($errorSummary, $errorSummary->header(''));
$this->assertNotSame($errorSummary, $errorSummary->model(new PersonalForm()));
$this->assertNotSame($errorSummary, $errorSummary->onlyAttributes(''));
$this->assertNotSame($errorSummary, $errorSummary->showAllErrors(false));
$this->assertNotSame($errorSummary, $errorSummary->tag('div'));
}
Expand All @@ -96,6 +141,7 @@ public function testImmutability(): void
* @param string $footer
* @param array $footerAttributes
* @param bool $showAllErrors
* @param array $onlyAttributes
* @param string $expected
*
* @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
Expand All @@ -110,6 +156,7 @@ public function testErrorSummary(
string $footer,
array $footerAttributes,
bool $showAllErrors,
array $onlyAttributes,
string $expected
): void {
$formModel = new PersonalForm();
Expand All @@ -129,10 +176,11 @@ public function testErrorSummary(

$errorSummary = ErrorSummary::widget()
->attributes($attributes)
->model($formModel)
->onlyAttributes(...$onlyAttributes)
->footer($footer)
->footerAttributes($footerAttributes)
->headerAttributes($headerAttributes)
->model($formModel)
->showAllErrors($showAllErrors);

$errorSummary = $header !== '' ? $errorSummary->header($header) : $errorSummary;
Expand Down

0 comments on commit 27d47de

Please sign in to comment.