Skip to content

Commit

Permalink
Allow Enum constraint to be used as PHP 8.0 Attribute (#50)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
yann-eugone authored Jun 28, 2021
1 parent 258f25e commit 01744ff
Show file tree
Hide file tree
Showing 15 changed files with 173 additions and 71 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ jobs:
strategy:
matrix:
include:
- php-version: 7.4
- php-version: 7.1
symfony-version: 4.4.*
- php-version: 8.0
symfony-version: 4.4.*
- php-version: 7.4
symfony-version: 5.2.*
- php-version: 7.2
symfony-version: 5.3.*
- php-version: 8.0
symfony-version: 5.2.*
symfony-version: 5.3.*

steps:
- name: "Checkout"
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,19 @@ use Yokai\EnumBundle\Validator\Constraints\Enum;

class Member
{
/**
* @Enum(StatusEnum::class)
*/
#[Enum(enum: StatusEnum::class)]
public ?string $status = null;
}
```

> **note** both PHP Attributes & Annotations are supported :
> ```php
> /**
> * @Enum(StatusEnum::class)
> */
> public ?string $status = null;
> ```
### Setting up the form
Now that validation is configured, the only thing we have to do is to add a field on our form :
Expand All @@ -112,7 +118,7 @@ class MemberType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
// Because we added the @Enum constraint to Member::$status property
// Because we added the #[Enum] constraint to Member::$status property
// the bundle will be able to find out the appropriate form type automatically
->add('status')
;
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
},
"require-dev": {
"doctrine/annotations": "^1.3",
"myclabs/php-enum": "^1.8",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.4",
"myclabs/php-enum": "^1.7",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"sensio/framework-extra-bundle": "^5.5|^6.1",
"squizlabs/php_codesniffer": "^3.5",
"symfony/form": "^4.4|^5.0",
"symfony/translation": "^4.4|^5.0",
Expand Down
24 changes: 8 additions & 16 deletions docs/migrating-from-symfony-standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,11 @@ class Member
public const SUBSCRIBE_NEWSLETTER = 'newsletter';
public const SUBSCRIBE_COMMERCIAL = 'commercial';

/**
* @Assert\NotNull()
* @Assert\Choice({Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED})
*/
#[Assert\NotNull]
#[Assert\Choice(choices: [Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED])]
public string $status = self::STATUS_NEW;

/**
* @Assert\Choice({Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL}, multiple=true)
*/
#[Assert\Choice(choices: [Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL], multiple: true)]
public array $subscriptions = [];
}
```
Expand Down Expand Up @@ -192,17 +188,13 @@ class Member
public const SUBSCRIBE_NEWSLETTER = 'newsletter';
public const SUBSCRIBE_COMMERCIAL = 'commercial';

/**
* @Assert\NotNull()
- * @Assert\Choice({Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED})
+ * @Enum(MemberStatusEnum::class)
*/
#[Assert\NotNull]
- #[Assert\Choice(choices: [Member::STATUS_NEW, Member::STATUS_ACTIVE, Member::STATUS_DISABLED])]
+ #[Enum(enum: MemberStatusEnum::class)]
public string $status = self::STATUS_NEW;

/**
- * @Assert\Choice({Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL}, multiple=true)
+ * @Enum(MemberSubscriptionEnum::class, multiple=true)
*/
- #[Assert\Choice(choices: [Member::SUBSCRIBE_NEWSLETTER, Member::SUBSCRIBE_COMMERCIAL], multiple: true)]
+ #[Enum(enum: MemberSubscriptionEnum::class, multiple: true)]
public array $subscriptions = [];
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/sonata-admin-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MemberAdmin extends AbstractAdmin
protected function configureFormFields(FormMapper $form): void
{
$form
// Because we added the @Enum constraint to Member::$status property
// Because we added the #[Enum] constraint to Member::$status property
// the bundle will be able to find out the appropriate form type automatically
->add('status')
;
Expand Down
43 changes: 43 additions & 0 deletions src/Validator/Constraints/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,56 @@
*
* @author Yann Eugoné <[email protected]>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
final class Enum extends Choice
{
/**
* @var string
*/
public $enum;

public function __construct(
$enum = null,
$callback = null,
bool $multiple = null,
bool $strict = null,
int $min = null,
int $max = null,
string $message = null,
string $multipleMessage = null,
string $minMessage = null,
string $maxMessage = null,
$groups = null,
$payload = null,
array $options = []
) {
if (\is_array($enum)) {
// Symfony 4.4 Constraints has single constructor argument containing all options
parent::__construct($enum);
} else {
if (\is_string($enum)) {
$this->enum = $enum;
}

// Symfony 5.x Constraints has many constructor arguments for PHP 8.0 Attributes support
parent::__construct(
null,
$callback,
$multiple,
$strict,
$min,
$max,
$message,
$multipleMessage,
$minMessage,
$maxMessage,
$groups,
$payload,
$options
);
}
}

/**
* @inheritdoc
*/
Expand Down
6 changes: 6 additions & 0 deletions tests/Integration/config/packages/annotations.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
framework:
annotations: true

sensio_framework_extra:
router:
annotations: true
5 changes: 4 additions & 1 deletion tests/Integration/config/packages/framework.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
framework: ~
framework:
test: true
form: true
property_access: true
7 changes: 0 additions & 7 deletions tests/Integration/src/Form/PullRequestType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Yokai\EnumBundle\Tests\Integration\App\Model\PullRequest;

/**
* @author Yann Eugoné <[email protected]>
Expand All @@ -19,9 +17,4 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
$builder->add('status');
$builder->add('labels');
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('data_class', PullRequest::class);
}
}
18 changes: 12 additions & 6 deletions tests/Integration/src/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@

namespace Yokai\EnumBundle\Tests\Integration\App;

use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Yokai\EnumBundle\YokaiEnumBundle;

/**
* @author Yann Eugoné <[email protected]>
Expand All @@ -16,8 +14,11 @@ final class Kernel extends BaseKernel
{
public function registerBundles(): iterable
{
yield new FrameworkBundle();
yield new YokaiEnumBundle();
yield new \Symfony\Bundle\FrameworkBundle\FrameworkBundle();
yield new \Yokai\EnumBundle\YokaiEnumBundle();
if (\PHP_VERSION_ID < 80000) {
yield new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle();
}
}

public function getProjectDir(): string
Expand All @@ -27,7 +28,12 @@ public function getProjectDir(): string

public function registerContainerConfiguration(LoaderInterface $loader): void
{
$loader->load($this->getProjectDir() . '/config/packages/');
$loader->load($this->getProjectDir() . '/config/services.yaml');
$loader->load(__DIR__ . '/../config/packages/framework.yaml');
$loader->load(__DIR__ . '/../config/packages/translation.yaml');
if (\PHP_VERSION_ID < 80000) {
$loader->load(__DIR__ . '/../config/packages/annotations.yaml');
}

$loader->load(__DIR__ . '/../config/services.yaml');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/**
* @author Yann Eugoné <[email protected]>
*/
final class PullRequest
final class PullRequestUsingAnnotations
{
/**
* @var Status
Expand Down
27 changes: 27 additions & 0 deletions tests/Integration/src/Model/PullRequestUsingAttributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Yokai\EnumBundle\Tests\Integration\App\Model;

use Yokai\EnumBundle\Validator\Constraints\Enum;
use Yokai\EnumBundle\Tests\Integration\App\Enum\PullRequestStatusEnum;
use Yokai\EnumBundle\Tests\Integration\App\Enum\PullRequestLabelEnum;

/**
* @author Yann Eugoné <[email protected]>
*/
final class PullRequestUsingAttributes
{
/**
* @var Status
*/
#[Enum(enum: PullRequestStatusEnum::class)]
public $status;

/**
* @var string[]
*/
#[Enum(enum: PullRequestLabelEnum::class, multiple: true)]
public $labels;
}
Loading

0 comments on commit 01744ff

Please sign in to comment.