Skip to content

Commit 01744ff

Browse files
authored
Allow Enum constraint to be used as PHP 8.0 Attribute (#50)
* Allow Enum constraint to be used as PHP 8.0 Attribute * Review testing matrix for wider versions range : PHP 7.1 & Symfony 5.3 * Separate testing model for annotations & attributes testing * Replace annotations with attributes in doc
1 parent 258f25e commit 01744ff

File tree

15 files changed

+173
-71
lines changed

15 files changed

+173
-71
lines changed

.github/workflows/tests.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ jobs:
1414
strategy:
1515
matrix:
1616
include:
17-
- php-version: 7.4
17+
- php-version: 7.1
1818
symfony-version: 4.4.*
1919
- php-version: 8.0
2020
symfony-version: 4.4.*
21-
- php-version: 7.4
22-
symfony-version: 5.2.*
21+
- php-version: 7.2
22+
symfony-version: 5.3.*
2323
- php-version: 8.0
24-
symfony-version: 5.2.*
24+
symfony-version: 5.3.*
2525

2626
steps:
2727
- name: "Checkout"

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,19 @@ use Yokai\EnumBundle\Validator\Constraints\Enum;
8484

8585
class Member
8686
{
87-
/**
88-
* @Enum(StatusEnum::class)
89-
*/
87+
#[Enum(enum: StatusEnum::class)]
9088
public ?string $status = null;
9189
}
9290
```
9391

92+
> **note** both PHP Attributes & Annotations are supported :
93+
> ```php
94+
> /**
95+
> * @Enum(StatusEnum::class)
96+
> */
97+
> public ?string $status = null;
98+
> ```
99+
94100
### Setting up the form
95101
96102
Now that validation is configured, the only thing we have to do is to add a field on our form :
@@ -112,7 +118,7 @@ class MemberType extends AbstractType
112118
public function buildForm(FormBuilderInterface $builder, array $options): void
113119
{
114120
$builder
115-
// Because we added the @Enum constraint to Member::$status property
121+
// Because we added the #[Enum] constraint to Member::$status property
116122
// the bundle will be able to find out the appropriate form type automatically
117123
->add('status')
118124
;

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
},
1616
"require-dev": {
1717
"doctrine/annotations": "^1.3",
18-
"myclabs/php-enum": "^1.8",
19-
"phpspec/prophecy-phpunit": "^2.0",
20-
"phpunit/phpunit": "^9.4",
18+
"myclabs/php-enum": "^1.7",
19+
"phpunit/phpunit": "^7.5|^8.5|^9.5",
20+
"sensio/framework-extra-bundle": "^5.5|^6.1",
2121
"squizlabs/php_codesniffer": "^3.5",
2222
"symfony/form": "^4.4|^5.0",
2323
"symfony/translation": "^4.4|^5.0",

docs/migrating-from-symfony-standard.md

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,11 @@ class Member
2828
public const SUBSCRIBE_NEWSLETTER = 'newsletter';
2929
public const SUBSCRIBE_COMMERCIAL = 'commercial';
3030

31-
/**
32-
* @Assert\NotNull()
33-
* @Assert\Choice({Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED})
34-
*/
31+
#[Assert\NotNull]
32+
#[Assert\Choice(choices: [Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED])]
3533
public string $status = self::STATUS_NEW;
3634

37-
/**
38-
* @Assert\Choice({Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL}, multiple=true)
39-
*/
35+
#[Assert\Choice(choices: [Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL], multiple: true)]
4036
public array $subscriptions = [];
4137
}
4238
```
@@ -192,17 +188,13 @@ class Member
192188
public const SUBSCRIBE_NEWSLETTER = 'newsletter';
193189
public const SUBSCRIBE_COMMERCIAL = 'commercial';
194190

195-
/**
196-
* @Assert\NotNull()
197-
- * @Assert\Choice({Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED})
198-
+ * @Enum(MemberStatusEnum::class)
199-
*/
191+
#[Assert\NotNull]
192+
- #[Assert\Choice(choices: [Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED])]
193+
+ #[Enum(enum: MemberStatusEnum::class)]
200194
public string $status = self::STATUS_NEW;
201195

202-
/**
203-
- * @Assert\Choice({Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL}, multiple=true)
204-
+ * @Enum(MemberSubscriptionEnum::class, multiple=true)
205-
*/
196+
- #[Assert\Choice(choices: [Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL], multiple: true)]
197+
+ #[Enum(enum: MemberSubscriptionEnum::class, multiple: true)]
206198
public array $subscriptions = [];
207199
}
208200
```

docs/sonata-admin-integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class MemberAdmin extends AbstractAdmin
5454
protected function configureFormFields(FormMapper $form): void
5555
{
5656
$form
57-
// Because we added the @Enum constraint to Member::$status property
57+
// Because we added the #[Enum] constraint to Member::$status property
5858
// the bundle will be able to find out the appropriate form type automatically
5959
->add('status')
6060
;

src/Validator/Constraints/Enum.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,56 @@
1212
*
1313
* @author Yann Eugoné <[email protected]>
1414
*/
15+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
1516
final class Enum extends Choice
1617
{
1718
/**
1819
* @var string
1920
*/
2021
public $enum;
2122

23+
public function __construct(
24+
$enum = null,
25+
$callback = null,
26+
bool $multiple = null,
27+
bool $strict = null,
28+
int $min = null,
29+
int $max = null,
30+
string $message = null,
31+
string $multipleMessage = null,
32+
string $minMessage = null,
33+
string $maxMessage = null,
34+
$groups = null,
35+
$payload = null,
36+
array $options = []
37+
) {
38+
if (\is_array($enum)) {
39+
// Symfony 4.4 Constraints has single constructor argument containing all options
40+
parent::__construct($enum);
41+
} else {
42+
if (\is_string($enum)) {
43+
$this->enum = $enum;
44+
}
45+
46+
// Symfony 5.x Constraints has many constructor arguments for PHP 8.0 Attributes support
47+
parent::__construct(
48+
null,
49+
$callback,
50+
$multiple,
51+
$strict,
52+
$min,
53+
$max,
54+
$message,
55+
$multipleMessage,
56+
$minMessage,
57+
$maxMessage,
58+
$groups,
59+
$payload,
60+
$options
61+
);
62+
}
63+
}
64+
2265
/**
2366
* @inheritdoc
2467
*/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
framework:
2+
annotations: true
3+
4+
sensio_framework_extra:
5+
router:
6+
annotations: true
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
framework: ~
1+
framework:
2+
test: true
3+
form: true
4+
property_access: true

tests/Integration/src/Form/PullRequestType.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
use Symfony\Component\Form\AbstractType;
88
use Symfony\Component\Form\FormBuilderInterface;
9-
use Symfony\Component\OptionsResolver\OptionsResolver;
10-
use Yokai\EnumBundle\Tests\Integration\App\Model\PullRequest;
119

1210
/**
1311
* @author Yann Eugoné <[email protected]>
@@ -19,9 +17,4 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
1917
$builder->add('status');
2018
$builder->add('labels');
2119
}
22-
23-
public function configureOptions(OptionsResolver $resolver): void
24-
{
25-
$resolver->setDefault('data_class', PullRequest::class);
26-
}
2720
}

tests/Integration/src/Kernel.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
namespace Yokai\EnumBundle\Tests\Integration\App;
66

7-
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
87
use Symfony\Component\Config\Loader\LoaderInterface;
98
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
10-
use Yokai\EnumBundle\YokaiEnumBundle;
119

1210
/**
1311
* @author Yann Eugoné <[email protected]>
@@ -16,8 +14,11 @@ final class Kernel extends BaseKernel
1614
{
1715
public function registerBundles(): iterable
1816
{
19-
yield new FrameworkBundle();
20-
yield new YokaiEnumBundle();
17+
yield new \Symfony\Bundle\FrameworkBundle\FrameworkBundle();
18+
yield new \Yokai\EnumBundle\YokaiEnumBundle();
19+
if (\PHP_VERSION_ID < 80000) {
20+
yield new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle();
21+
}
2122
}
2223

2324
public function getProjectDir(): string
@@ -27,7 +28,12 @@ public function getProjectDir(): string
2728

2829
public function registerContainerConfiguration(LoaderInterface $loader): void
2930
{
30-
$loader->load($this->getProjectDir() . '/config/packages/');
31-
$loader->load($this->getProjectDir() . '/config/services.yaml');
31+
$loader->load(__DIR__ . '/../config/packages/framework.yaml');
32+
$loader->load(__DIR__ . '/../config/packages/translation.yaml');
33+
if (\PHP_VERSION_ID < 80000) {
34+
$loader->load(__DIR__ . '/../config/packages/annotations.yaml');
35+
}
36+
37+
$loader->load(__DIR__ . '/../config/services.yaml');
3238
}
3339
}

0 commit comments

Comments
 (0)