From 9d77e16fcda7a27bd65f69d075e666256376a1e8 Mon Sep 17 00:00:00 2001 From: Alexey Rogachev Date: Mon, 26 Aug 2024 22:55:30 +0500 Subject: [PATCH] Add `Hint` and `Placeholder` attributes (#65) --- README.md | 2 +- docs/guide/en/form-model.md | 5 +- src/Attribute/Hint.php | 21 +++++++ src/Attribute/Placeholder.php | 21 +++++++ src/{ => Attribute}/Safe.php | 2 +- src/FormModel.php | 60 +++++++++++++++---- tests/FormModelTest.php | 42 +++++++++++-- tests/SafeTest.php | 2 +- tests/Support/Form/AttributeForm.php | 44 ++++++++++++++ .../Support/Form/FormWithNestedStructures.php | 2 +- tests/Support/Form/LabelForm.php | 24 -------- tests/Support/Form/LoginForm.php | 2 +- .../Support/Form/NestedRuleForm/MainForm.php | 2 +- 13 files changed, 180 insertions(+), 49 deletions(-) create mode 100644 src/Attribute/Hint.php create mode 100644 src/Attribute/Placeholder.php rename src/{ => Attribute}/Safe.php (95%) create mode 100644 tests/Support/Form/AttributeForm.php delete mode 100644 tests/Support/Form/LabelForm.php diff --git a/README.md b/README.md index 33cd30b..5c9442b 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ composer require yiisoft/form-model Define a [form model](docs/guide/en/form-model.md): ```php +use Yiisoft\FormModel\Attribute\Safe; use Yiisoft\FormModel\FormModel; -use Yiisoft\FormModel\Safe; use Yiisoft\Validator\Rule\Email; use Yiisoft\Validator\Rule\Length; use Yiisoft\Validator\Rule\Required; diff --git a/docs/guide/en/form-model.md b/docs/guide/en/form-model.md index d89b40a..1ff447d 100644 --- a/docs/guide/en/form-model.md +++ b/docs/guide/en/form-model.md @@ -250,11 +250,8 @@ final class LoginForm extends FormModel This package also provides `Safe` rule that marks a model property as safe for filling from user input. ```php +use Yiisoft\FormModel\Attribute\Safe; use Yiisoft\FormModel\FormModel; -use Yiisoft\FormModel\Safe; -use Yiisoft\Validator\Rule\Email; -use Yiisoft\Validator\Rule\Length; -use Yiisoft\Validator\Rule\Required; final class LoginForm extends FormModel { diff --git a/src/Attribute/Hint.php b/src/Attribute/Hint.php new file mode 100644 index 0000000..fffc5d3 --- /dev/null +++ b/src/Attribute/Hint.php @@ -0,0 +1,21 @@ +hint; + } +} diff --git a/src/Attribute/Placeholder.php b/src/Attribute/Placeholder.php new file mode 100644 index 0000000..bf01660 --- /dev/null +++ b/src/Attribute/Placeholder.php @@ -0,0 +1,21 @@ +placeholder; + } +} diff --git a/src/Safe.php b/src/Attribute/Safe.php similarity index 95% rename from src/Safe.php rename to src/Attribute/Safe.php index 1cc22dc..738d3bf 100644 --- a/src/Safe.php +++ b/src/Attribute/Safe.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Yiisoft\FormModel; +namespace Yiisoft\FormModel\Attribute; use Attribute; use Yiisoft\Validator\Result; diff --git a/src/FormModel.php b/src/FormModel.php index afcf827..bdce85f 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -8,6 +8,9 @@ use ReflectionAttribute; use ReflectionClass; use ReflectionException; +use ReflectionProperty; +use Yiisoft\FormModel\Attribute\Hint; +use Yiisoft\FormModel\Attribute\Placeholder; use Yiisoft\FormModel\Exception\PropertyNotSupportNestedValuesException; use Yiisoft\FormModel\Exception\StaticObjectPropertyException; use Yiisoft\FormModel\Exception\UndefinedArrayElementException; @@ -239,16 +242,9 @@ private function readPropertyMetaValue(int $metaKey, string $path): ?string return null; } - /** - * Try to get label from {@see Label} PHP attribute. - */ - if ($metaKey === self::META_LABEL) { - $attributes = $property->getAttributes(Label::class, ReflectionAttribute::IS_INSTANCEOF); - foreach ($attributes as $attribute) { - /** @var Label $instance */ - $instance = $attribute->newInstance(); - return $instance->getLabel(); - } + $valueByAttribute = $this->getPropertyMetaValueByAttribute($metaKey, $property); + if ($valueByAttribute !== null) { + return $valueByAttribute; } $value = $property->getValue($value); @@ -319,4 +315,48 @@ private function makePropertyPathString(array $keys): string } return $path; } + + /** + * @psalm-param self::META_* $metaKey + */ + private function getPropertyMetaValueByAttribute(int $metaKey, ReflectionProperty $property): ?string + { + switch ($metaKey) { + /** Try to get label from {@see Label} PHP attribute. */ + case self::META_LABEL: + $attributes = $property->getAttributes(Label::class, ReflectionAttribute::IS_INSTANCEOF); + if (!empty($attributes)) { + /** @var Label $instance */ + $instance = $attributes[0]->newInstance(); + + return $instance->getLabel(); + } + + break; + /** Try to get label from {@see Hint} PHP attribute. */ + case self::META_HINT: + $attributes = $property->getAttributes(Hint::class, ReflectionAttribute::IS_INSTANCEOF); + if (!empty($attributes)) { + /** @var Hint $instance */ + $instance = $attributes[0]->newInstance(); + + return $instance->getHint(); + } + + break; + /** Try to get label from {@see Placeholder} PHP attribute. */ + case self::META_PLACEHOLDER: + $attributes = $property->getAttributes(Placeholder::class, ReflectionAttribute::IS_INSTANCEOF); + if (!empty($attributes)) { + /** @var Placeholder $instance */ + $instance = $attributes[0]->newInstance(); + + return $instance->getPlaceholder(); + } + + break; + } + + return null; + } } diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index 5e6009c..9938dc9 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -9,19 +9,19 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use stdClass; +use Yiisoft\FormModel\Attribute\Safe; use Yiisoft\FormModel\Exception\PropertyNotSupportNestedValuesException; use Yiisoft\FormModel\Exception\StaticObjectPropertyException; use Yiisoft\FormModel\Exception\UndefinedObjectPropertyException; use Yiisoft\FormModel\FormModel; use Yiisoft\FormModel\FormModelInputData; use Yiisoft\FormModel\FormModelInterface; -use Yiisoft\FormModel\Safe; use Yiisoft\FormModel\Tests\Support\Dto\Coordinates; use Yiisoft\FormModel\Tests\Support\Form\CustomFormNameForm; use Yiisoft\FormModel\Tests\Support\Form\DefaultFormNameForm; use Yiisoft\FormModel\Tests\Support\Form\FormWithNestedProperty; use Yiisoft\FormModel\Tests\Support\Form\FormWithNestedStructures; -use Yiisoft\FormModel\Tests\Support\Form\LabelForm; +use Yiisoft\FormModel\Tests\Support\Form\AttributeForm; use Yiisoft\FormModel\Tests\Support\Form\LoginForm; use Yiisoft\FormModel\Tests\Support\Form\NestedForm; use Yiisoft\FormModel\Tests\Support\Form\NestedMixedForm\NestedMixedForm; @@ -686,17 +686,49 @@ public function testAddErrorWithoutValidation(): void public static function dataLabel(): iterable { - yield ['AgeFromAttribute', 'age']; - yield ['NameFromGetter', 'name']; + yield ['AgeLabelFromAttribute', 'age']; + yield ['NameLabelFromGetter', 'name']; } #[DataProvider('dataLabel')] public function testLabel(string $expected, string $property): void { - $form = new LabelForm(); + $form = new AttributeForm(); $label = $form->getPropertyLabel($property); $this->assertSame($expected, $label); } + + public static function dataHint(): iterable + { + yield ['AgeHintFromAttribute', 'age']; + yield ['NameHintFromGetter', 'name']; + } + + #[DataProvider('dataHint')] + public function testHint(string $expected, string $property): void + { + $form = new AttributeForm(); + + $label = $form->getPropertyHint($property); + + $this->assertSame($expected, $label); + } + + public static function dataPlaceholder(): iterable + { + yield ['AgePlaceholderFromAttribute', 'age']; + yield ['NamePlaceholderFromGetter', 'name']; + } + + #[DataProvider('dataPlaceholder')] + public function testPlaceholder(string $expected, string $property): void + { + $form = new AttributeForm(); + + $label = $form->getPropertyPlaceholder($property); + + $this->assertSame($expected, $label); + } } diff --git a/tests/SafeTest.php b/tests/SafeTest.php index 367f00c..1ec0d08 100644 --- a/tests/SafeTest.php +++ b/tests/SafeTest.php @@ -5,7 +5,7 @@ namespace Yiisoft\FormModel\Tests; use PHPUnit\Framework\TestCase; -use Yiisoft\FormModel\Safe; +use Yiisoft\FormModel\Attribute\Safe; final class SafeTest extends TestCase { diff --git a/tests/Support/Form/AttributeForm.php b/tests/Support/Form/AttributeForm.php new file mode 100644 index 0000000..9e6d801 --- /dev/null +++ b/tests/Support/Form/AttributeForm.php @@ -0,0 +1,44 @@ + 'NameLabelFromGetter', + ]; + } + + public function getPropertyHints(): array + { + return [ + 'name' => 'NameHintFromGetter', + ]; + } + + public function getPropertyPlaceholders(): array + { + return [ + 'name' => 'NamePlaceholderFromGetter', + ]; + } +} diff --git a/tests/Support/Form/FormWithNestedStructures.php b/tests/Support/Form/FormWithNestedStructures.php index 72a91ac..6a5c14f 100644 --- a/tests/Support/Form/FormWithNestedStructures.php +++ b/tests/Support/Form/FormWithNestedStructures.php @@ -4,8 +4,8 @@ namespace Yiisoft\FormModel\Tests\Support\Form; +use Yiisoft\FormModel\Attribute\Safe; use Yiisoft\FormModel\FormModel; -use Yiisoft\FormModel\Safe; use Yiisoft\FormModel\Tests\Support\Dto\Coordinates; final class FormWithNestedStructures extends FormModel diff --git a/tests/Support/Form/LabelForm.php b/tests/Support/Form/LabelForm.php deleted file mode 100644 index 4abf176..0000000 --- a/tests/Support/Form/LabelForm.php +++ /dev/null @@ -1,24 +0,0 @@ - 'NameFromGetter', - ]; - } -} diff --git a/tests/Support/Form/LoginForm.php b/tests/Support/Form/LoginForm.php index 2fbc4b9..45c1a20 100644 --- a/tests/Support/Form/LoginForm.php +++ b/tests/Support/Form/LoginForm.php @@ -4,8 +4,8 @@ namespace Yiisoft\FormModel\Tests\Support\Form; +use Yiisoft\FormModel\Attribute\Safe; use Yiisoft\FormModel\FormModel; -use Yiisoft\FormModel\Safe; use Yiisoft\Validator\Rule\Email; use Yiisoft\Validator\Rule\Length; use Yiisoft\Validator\Rule\Required; diff --git a/tests/Support/Form/NestedRuleForm/MainForm.php b/tests/Support/Form/NestedRuleForm/MainForm.php index 3aed68b..0877094 100644 --- a/tests/Support/Form/NestedRuleForm/MainForm.php +++ b/tests/Support/Form/NestedRuleForm/MainForm.php @@ -4,8 +4,8 @@ namespace Yiisoft\FormModel\Tests\Support\Form\NestedRuleForm; +use Yiisoft\FormModel\Attribute\Safe; use Yiisoft\FormModel\FormModel; -use Yiisoft\FormModel\Safe; use Yiisoft\Validator\Rule\Nested; use Yiisoft\Validator\Rule\Required; use Yiisoft\Validator\RulesProviderInterface;