diff --git a/src/Field.php b/src/Field.php index 0cf71e6..ee6a3df 100644 --- a/src/Field.php +++ b/src/Field.php @@ -222,6 +222,27 @@ public function radiolist(FormModelInterface $formModel, string $attribute, arra return $new; } + /** + * Renders a range widget. + * + * @param FormModelInterface $formModel The model object. + * @param string $attribute The attribute name or expression. + * @param array $config The config array definition for the factory widget. + * Available methods: + * [ + * 'outputTag()' => ['p'], + * 'outputAttributes()' => [['class' => 'test-class']], + * ] + * + * @return static the field object itself. + */ + public function range(FormModelInterface $formModel, string $attribute, array $config = []): self + { + $new = clone $this; + $new->widget = Range::widget($config)->for($formModel, $attribute); + return $new; + } + /** * Renders a reset button widget. * diff --git a/src/Range.php b/src/Range.php new file mode 100644 index 0000000..e798c23 --- /dev/null +++ b/src/Range.php @@ -0,0 +1,126 @@ +attributes['max'] = $value; + return $new; + } + + /** + * The expected lower bound for the element’s value. + * + * @param int $value + * + * @return static + * + * @link https://www.w3.org/TR/2012/WD-html-markup-20120329/input.range.html#input.range.attrs.min + */ + public function min(int $value): self + { + $new = clone $this; + $new->attributes['min'] = $value; + return $new; + } + + /** + * The HTML attributes for output tag. The following special options are recognized. + * + * @param array $value + * + * @return static + * + * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered. + */ + public function outputAttributes(array $value): self + { + $new = clone $this; + $new->outputAttributes = $value; + return $new; + } + + /** + * The tag name of the output tag. + * + * Empty to render error messages without container {@see Html::tag()}. + * + * @param string $value + * + * @return static + */ + public function outputTag(string $value): self + { + $new = clone $this; + $new->outputTag = $value; + return $new; + } + + /** + * @return string the generated input tag. + */ + protected function run(): string + { + $attributes = $this->build($this->attributes); + $outputAttributes = $this->outputAttributes; + + if (empty($this->outputTag)) { + throw new InvalidArgumentException('The output tag name it cannot be empty value.'); + } + + /** @link https://www.w3.org/TR/2012/WD-html-markup-20120329/input.range.html#input.range.attrs.value */ + $value = $attributes['value'] ?? $this->getAttributeValue(); + unset($attributes['value']); + + if (!is_numeric($value) && null !== $value) { + throw new InvalidArgumentException('Range widget must be a numeric or null value.'); + } + + $nameOutput = Html::generateId(); + /** @var string|null */ + $outputAttributes['for'] = $attributes['name'] ?? null; + $outputAttributes['name'] = $nameOutput; + $attributes['oninput'] = "$nameOutput.value=this.value"; + + return + Input::tag() + ->type('range') + ->attributes($attributes) + ->value($value > 0 ? $value : 0)->render() . PHP_EOL . + CustomTag::name($this->outputTag) + ->attributes($outputAttributes) + ->content($value > 0 ? (string)$value : '0') + ->id($nameOutput) + ->render(); + } +} diff --git a/tests/CheckboxListTest.php b/tests/CheckboxListTest.php index c23887e..3e69bdd 100644 --- a/tests/CheckboxListTest.php +++ b/tests/CheckboxListTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/CheckboxTest.php b/tests/CheckboxTest.php index ab1f9f2..00fd078 100644 --- a/tests/CheckboxTest.php +++ b/tests/CheckboxTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/Field/FieldCheckBoxListTest.php b/tests/Field/FieldCheckBoxListTest.php index 50237fd..208d5ed 100644 --- a/tests/Field/FieldCheckBoxListTest.php +++ b/tests/Field/FieldCheckBoxListTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/Field/FieldRadioListTest.php b/tests/Field/FieldRadioListTest.php index 3f36bb8..d76f7dd 100644 --- a/tests/Field/FieldRadioListTest.php +++ b/tests/Field/FieldRadioListTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/Field/FieldRangeTest.php b/tests/Field/FieldRangeTest.php new file mode 100644 index 0000000..af9fe39 --- /dev/null +++ b/tests/Field/FieldRangeTest.php @@ -0,0 +1,395 @@ +setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->autofocus()->range(new TypeForm(), 'int')->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testDisabled(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->disabled()->range(new TypeForm(), 'int')->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testGetValidatorAttributeNumber(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new ValidatorForm(), 'number')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testId(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->id('id-test')->range(new TypeForm(), 'int')->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testMax(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->range(new TypeForm(), 'int', ['max()' => [8]])->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testMin(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->range(new TypeForm(), 'int', ['min()' => [4]])->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testName(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->name('name-test')->range(new TypeForm(), 'int')->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testOutputAttributes(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget() + ->range(new TypeForm(), 'int', ['outputAttributes()' => [['class' => 'test-class']]]) + ->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testOutputTag(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + +

0

+ + HTML; + $this->assertEqualsWithoutLE( + $expected, + Field::widget()->range(new TypeForm(), 'int', ['outputTag()' => ['p']])->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testOutputTagException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The output tag name it cannot be empty value.'); + Field::widget()->range(new TypeForm(), 'int', ['outputTag()' => ['']])->render(); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testRequired(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new TypeForm(), 'int')->required()->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testRender(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new TypeForm(), 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testTabindex(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new TypeForm(), 'int')->tabindex(1)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testValue(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value int `1`. + $expected = << + + + 1 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new TypeForm(), 'int')->value(1)->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value string numeric `1`. + $expected = << + + + 1 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new TypeForm(), 'string')->value('1')->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value `null`. + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range(new TypeForm(), 'int')->value(null)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testValueException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Range widget must be a numeric or null value.'); + Field::widget()->range(new TypeForm(), 'array')->render(); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testValueWithFormModel(): void + { + $formModel = new TypeForm(); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value int `1`. + $formModel->setAttribute('int', '1'); + $expected = << + + + 1 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range($formModel, 'int')->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value string numeric `1`. + $formModel->setAttribute('string', '1'); + $expected = << + + + 1 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range($formModel, 'string')->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value `null`. + $formModel->setAttribute('int', null); + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->range($formModel, 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testWithoutId(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->id(null)->range(new TypeForm(), 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testWithoutName(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + + + 0 + + HTML; + $this->assertEqualsWithoutLE($expected, Field::widget()->name(null)->range(new TypeForm(), 'int')->render()); + } +} diff --git a/tests/RadioListTest.php b/tests/RadioListTest.php index 93a17d1..a7a2fbf 100644 --- a/tests/RadioListTest.php +++ b/tests/RadioListTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/RadioTest.php b/tests/RadioTest.php index 250bdde..15ab0b8 100644 --- a/tests/RadioTest.php +++ b/tests/RadioTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/RangeTest.php b/tests/RangeTest.php new file mode 100644 index 0000000..5351206 --- /dev/null +++ b/tests/RangeTest.php @@ -0,0 +1,327 @@ +setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->autofocus()->for(new TypeForm(), 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testDisabled(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->disabled()->for(new TypeForm(), 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testGetValidatorAttributeNumber(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new ValidatorForm(), 'number')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testId(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->id('id-test')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testImmutability(): void + { + $range = Range::widget(); + $this->assertNotSame($range, $range->max(0)); + $this->assertNotSame($range, $range->min(0)); + $this->assertNotSame($range, $range->outputAttributes([])); + $this->assertNotSame($range, $range->outputTag('')); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testMax(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->max(8)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testMin(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 1]); + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->min(4)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testName(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE( + $expected, + Range::widget()->for(new TypeForm(), 'int')->name('name-test')->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testOutputAttributes(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 2]); + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE( + $expected, + Range::widget()->for(new TypeForm(), 'int')->outputAttributes(['class' => 'test-class'])->render(), + ); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testOutputTag(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 3]); + $expected = << +

0

+ HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->outputTag('p')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testOutputTagException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The output tag name it cannot be empty value.'); + Range::widget()->for(new TypeForm(), 'int')->outputTag('')->render(); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testRequired(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->required()->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testRender(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testTabindex(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->tabindex(1)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testValue(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value string numeric `1`. + $expected = << + 1 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'string')->value('1')->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value int `1`. + $expected = << + 1 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->value(1)->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value `null`. + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->value(null)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testValueException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Range widget must be a numeric or null value.'); + Range::widget()->for(new TypeForm(), 'array')->render(); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testValueWithForm(): void + { + $formModel = new TypeForm(); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value int `1`. + $formModel->setAttribute('int', 1); + + $expected = << + 1 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for($formModel, 'int')->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value string numeric `1`. + $formModel->setAttribute('string', '1'); + + $expected = << + 1 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for($formModel, 'string')->render()); + + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + // Value `null`. + $formModel->setAttribute('int', null); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for($formModel, 'int')->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testWithoutId(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->id(null)->render()); + } + + /** + * @throws InvalidConfigException|NotFoundException|NotInstantiableException|CircularReferenceException + */ + public function testWithoutName(): void + { + $this->setInaccessibleProperty(new Html(), 'generateIdCounter', []); + + $expected = << + 0 + HTML; + $this->assertEqualsWithoutLE($expected, Range::widget()->for(new TypeForm(), 'int')->name(null)->render()); + } +} diff --git a/tests/SelectTest.php b/tests/SelectTest.php index 4a1e87c..da9780b 100644 --- a/tests/SelectTest.php +++ b/tests/SelectTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use InvalidArgumentException; use PHPUnit\Framework\TestCase; diff --git a/tests/SubmitButtonTest.php b/tests/SubmitButtonTest.php index c835fb0..1dd3ed0 100644 --- a/tests/SubmitButtonTest.php +++ b/tests/SubmitButtonTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\Form\Tests\Widget; +namespace Yii\Extension\Simple\Forms\Tests; use PHPUnit\Framework\TestCase; use Yii\Extension\Simple\Forms\SubmitButton; diff --git a/tests/TestSupport/Form/ValidatorForm.php b/tests/TestSupport/Form/ValidatorForm.php index ff8a02e..d1d2172 100644 --- a/tests/TestSupport/Form/ValidatorForm.php +++ b/tests/TestSupport/Form/ValidatorForm.php @@ -7,6 +7,7 @@ use Yii\Extension\Simple\Model\FormModel; use Yiisoft\Validator\Rule\HasLength; use Yiisoft\Validator\Rule\MatchRegularExpression; +use Yiisoft\Validator\Rule\Number; use Yiisoft\Validator\Rule\Required; use Yiisoft\Validator\Rule\Url; @@ -15,6 +16,7 @@ final class ValidatorForm extends FormModel private string $matchregular = ''; private string $maxlength = ''; private string $minlength = ''; + private int $number = 0; private string $required = ''; private string $url = ''; @@ -24,6 +26,7 @@ public function getRules(): array 'matchregular' => [MatchRegularExpression::rule('/\w+/')], 'maxlength' => [HasLength::rule()->max(50)], 'minlength' => [HasLength::rule()->min(15)], + 'number' => [Number::rule()->min(3)->max(5)], 'required' => [Required::rule()], 'url' => [Url::rule()], ];