Skip to content

Commit

Permalink
Add Hint and Placeholder attributes (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
arogachev authored Aug 26, 2024
1 parent 35c023f commit 9d77e16
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 49 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 1 addition & 4 deletions docs/guide/en/form-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
21 changes: 21 additions & 0 deletions src/Attribute/Hint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Yiisoft\FormModel\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_PROPERTY)]
final class Hint
{
public function __construct(
private string $hint,
) {
}

public function getHint(): string
{
return $this->hint;
}
}
21 changes: 21 additions & 0 deletions src/Attribute/Placeholder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Yiisoft\FormModel\Attribute;

use Attribute;

#[Attribute(Attribute::TARGET_PROPERTY)]
final class Placeholder
{
public function __construct(
private string $placeholder,
) {
}

public function getPlaceholder(): string
{
return $this->placeholder;
}
}
2 changes: 1 addition & 1 deletion src/Safe.php → src/Attribute/Safe.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Yiisoft\FormModel;
namespace Yiisoft\FormModel\Attribute;

use Attribute;
use Yiisoft\Validator\Result;
Expand Down
60 changes: 50 additions & 10 deletions src/FormModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
42 changes: 37 additions & 5 deletions tests/FormModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion tests/SafeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
44 changes: 44 additions & 0 deletions tests/Support/Form/AttributeForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Yiisoft\FormModel\Tests\Support\Form;

use Yiisoft\FormModel\Attribute\Hint;
use Yiisoft\FormModel\Attribute\Placeholder;
use Yiisoft\FormModel\FormModel;
use Yiisoft\Validator\Label;

final class AttributeForm extends FormModel
{
#[Label('AgeLabelFromAttribute')]
#[Hint('AgeHintFromAttribute')]
#[Placeholder('AgePlaceholderFromAttribute')]
public string $age;

#[Label('NameLabelFromAttribute')]
#[Hint('NameHintFromAttribute')]
#[Placeholder('NamePlaceholderFromAttribute')]
public string $name;

public function getPropertyLabels(): array
{
return [
'name' => 'NameLabelFromGetter',
];
}

public function getPropertyHints(): array
{
return [
'name' => 'NameHintFromGetter',
];
}

public function getPropertyPlaceholders(): array
{
return [
'name' => 'NamePlaceholderFromGetter',
];
}
}
2 changes: 1 addition & 1 deletion tests/Support/Form/FormWithNestedStructures.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 0 additions & 24 deletions tests/Support/Form/LabelForm.php

This file was deleted.

2 changes: 1 addition & 1 deletion tests/Support/Form/LoginForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tests/Support/Form/NestedRuleForm/MainForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 9d77e16

Please sign in to comment.