Skip to content

Commit

Permalink
Restore annotation constraint in 8.1 (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
fre5h authored Jan 26, 2022
1 parent da47138 commit 02166fc
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 49 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@

| Bundle Version (X.Y.Z) | PHP | Symfony | Doctrine Bundle | Comment |
|:----------------------:|:----------:|:----------:|:---------------:|:--------------------|
| `8.1.*` | `>= 8.1.0` | `5.4, 6.0` | `>= 2.5` | **Current version** |
| `8.0.*` | `>= 8.0.0` | `6.0` | `>= 2.5` | Previous version |
| `7.5.*` | `>= 8.0.0` | `5.4` | `>= 2.5` | Previous version |
| `7.4.*` | `>= 7.4.0` | `5.4` | `>= 2.1` | Previous version |
| `8.1.*` | `>= 8.1.0` | `5.4, 6.0` | `>= 2.5` | **Current version** |
| `7.5.*` | `>= 8.0.0` | `5.4` | `>= 2.5` | Previous version |

#### Check the `config/bundles.php` file

Expand All @@ -46,7 +44,7 @@ return [

## Usage 🧑‍🎓

* [Example](./Resources/docs/example_usage.md "Example")
* [Example](./Resources/docs/usage_example.md "Example")

## Features 🎁

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class Player
// Note, that type of field should be same as you set in Doctrine config (in this case it is BasketballPositionType)
#[ORM\Column(type: BasketballPositionType::class)]
#[DoctrineAssert\Enum(entity: BasketballPositionType::class)]
#[DoctrineAssert\EnumType(entity: BasketballPositionType::class)]
private $position;
public function getId()
Expand Down
32 changes: 10 additions & 22 deletions Tests/Validator/EnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

namespace Fresh\DoctrineEnumBundle\Tests\Validator;

use Fresh\DoctrineEnumBundle\Exception\InvalidArgumentException;
use Fresh\DoctrineEnumBundle\Tests\Fixtures\DBAL\Types\BasketballPositionType;
use Fresh\DoctrineEnumBundle\Validator\Constraints\Enum;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Exception\MissingOptionsException;

/**
* EnumTest.
Expand All @@ -24,41 +24,29 @@
*/
final class EnumTest extends TestCase
{
public function testConstructorWithRequiredArguments(): void
public function testConstructor(): void
{
$constraint = new Enum(entity: BasketballPositionType::class);
$constraint = new Enum(['entity' => BasketballPositionType::class]);

self::assertEquals(BasketballPositionType::getValues(), $constraint->choices);
self::assertTrue($constraint->strict);
}

public function testConstructorWithAllArguments(): void
public function testMissedRequiredOption(): void
{
$constraint = new Enum(entity: BasketballPositionType::class, message: 'test', groups: ['foo'], payload: ['bar' => 'baz']);

self::assertEquals(BasketballPositionType::getValues(), $constraint->choices);
self::assertTrue($constraint->strict);
self::assertEquals(BasketballPositionType::class, $constraint->entity);
self::assertEquals('test', $constraint->message);
self::assertEquals(['foo'], $constraint->groups);
self::assertEquals(['bar' => 'baz'], $constraint->payload);
self::assertNull($constraint->callback);
self::assertFalse($constraint->multiple);
self::assertNull($constraint->min);
self::assertNull($constraint->max);
$this->expectException(MissingOptionsException::class);
self::assertEquals(['entity'], (new Enum())->getRequiredOptions());
}

public function testNotEnumType(): void
public function testGetRequiredOptions(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('stdClass is not instance of Fresh\DoctrineEnumBundle\DBAL\Types\AbstractEnumType');
$constraint = new Enum(['entity' => BasketballPositionType::class]);

new Enum(entity: \stdClass::class);
self::assertEquals(['entity'], $constraint->getRequiredOptions());
}

public function testGetDefaultOption(): void
{
$constraint = new Enum(entity: BasketballPositionType::class);
$constraint = new Enum(['entity' => BasketballPositionType::class]);

self::assertEquals('choices', $constraint->getDefaultOption());
}
Expand Down
65 changes: 65 additions & 0 deletions Tests/Validator/EnumTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the FreshDoctrineEnumBundle.
*
* (c) Artem Henvald <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Fresh\DoctrineEnumBundle\Tests\Validator;

use Fresh\DoctrineEnumBundle\Exception\InvalidArgumentException;
use Fresh\DoctrineEnumBundle\Tests\Fixtures\DBAL\Types\BasketballPositionType;
use Fresh\DoctrineEnumBundle\Validator\Constraints\EnumType;
use PHPUnit\Framework\TestCase;

/**
* EnumTypeTest.
*
* @author Artem Henvald <[email protected]>
*/
final class EnumTypeTest extends TestCase
{
public function testConstructorWithRequiredArguments(): void
{
$constraint = new EnumType(entity: BasketballPositionType::class);

self::assertEquals(BasketballPositionType::getValues(), $constraint->choices);
self::assertTrue($constraint->strict);
}

public function testConstructorWithAllArguments(): void
{
$constraint = new EnumType(entity: BasketballPositionType::class, message: 'test', groups: ['foo'], payload: ['bar' => 'baz']);

self::assertEquals(BasketballPositionType::getValues(), $constraint->choices);
self::assertTrue($constraint->strict);
self::assertEquals(BasketballPositionType::class, $constraint->entity);
self::assertEquals('test', $constraint->message);
self::assertEquals(['foo'], $constraint->groups);
self::assertEquals(['bar' => 'baz'], $constraint->payload);
self::assertNull($constraint->callback);
self::assertFalse($constraint->multiple);
self::assertNull($constraint->min);
self::assertNull($constraint->max);
}

public function testNotEnumType(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('stdClass is not instance of Fresh\DoctrineEnumBundle\DBAL\Types\AbstractEnumType');

new EnumType(entity: \stdClass::class);
}

public function testGetDefaultOption(): void
{
$constraint = new EnumType(entity: BasketballPositionType::class);

self::assertEquals('choices', $constraint->getDefaultOption());
}
}
102 changes: 102 additions & 0 deletions Tests/Validator/EnumTypeValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/*
* This file is part of the FreshDoctrineEnumBundle.
*
* (c) Artem Henvald <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Fresh\DoctrineEnumBundle\Tests\Validator;

use Fresh\DoctrineEnumBundle\Exception\RuntimeException;
use Fresh\DoctrineEnumBundle\Tests\Fixtures\DBAL\Types\BasketballPositionType;
use Fresh\DoctrineEnumBundle\Validator\Constraints\EnumType;
use Fresh\DoctrineEnumBundle\Validator\Constraints\EnumTypeValidator;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Context\ExecutionContext;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;

/**
* EnumValidatorTest.
*
* @author Artem Henvald <[email protected]>
*/
final class EnumTypeValidatorTest extends TestCase
{
/** @var ExecutionContext|MockObject */
private $context;

private EnumTypeValidator $enumValidator;

protected function setUp(): void
{
$this->enumValidator = new EnumTypeValidator();
$this->context = $this->createMock(ExecutionContext::class);
}

protected function tearDown(): void
{
unset(
$this->enumValidator,
$this->context
);
}

public function testValidateIncorrectConstraintClass(): void
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessageMatches('/^Object of class .* is not instance of .*$/');

$this->enumValidator->validate(BasketballPositionType::POINT_GUARD, new DummyConstraint());
}

public function testValidBasketballPositionType(): void
{
$constraint = new EnumType(entity: BasketballPositionType::class);

$this->context
->expects(self::never())
->method('buildViolation')
;

$this->enumValidator->initialize($this->context);
$this->enumValidator->validate(BasketballPositionType::SMALL_FORWARD, $constraint);
}

public function testInvalidBasketballPositionType(): void
{
$constraint = new EnumType(entity: BasketballPositionType::class);
$constraintValidationBuilder = $this->createMock(ConstraintViolationBuilder::class);

$constraintValidationBuilder
->expects(self::exactly(2))
->method('setParameter')
->withConsecutive(
[self::equalTo('{{ value }}'), self::equalTo('"Pitcher"')],
[self::equalTo('{{ choices }}'), self::equalTo('"PG", "SG", "SF", "PF", "C"')]
)
->willReturn($constraintValidationBuilder, $constraintValidationBuilder)
;

$constraintValidationBuilder
->expects(self::once())
->method('setCode')
->willReturnSelf()
;

$this->context
->expects(self::once())
->method('buildViolation')
->with(self::equalTo('The value you selected is not a valid choice.'))
->willReturn($constraintValidationBuilder)
;

$this->enumValidator->initialize($this->context);
$this->enumValidator->validate('Pitcher', $constraint); // It's not a baseball =)
}
}
17 changes: 13 additions & 4 deletions Tests/Validator/EnumValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Context\ExecutionContext;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;

/**
Expand All @@ -28,11 +29,11 @@
*/
final class EnumValidatorTest extends TestCase
{
private EnumValidator $enumValidator;

/** @var ExecutionContext|MockObject */
private $context;

private EnumValidator $enumValidator;

protected function setUp(): void
{
$this->enumValidator = new EnumValidator();
Expand All @@ -55,9 +56,17 @@ public function testValidateIncorrectConstraintClass(): void
$this->enumValidator->validate(BasketballPositionType::POINT_GUARD, new DummyConstraint());
}

public function testExceptionEntityNotSpecified(): void
{
$constraint = new Enum(['entity' => null]);

$this->expectException(ConstraintDefinitionException::class);
$this->enumValidator->validate(BasketballPositionType::POINT_GUARD, $constraint);
}

public function testValidBasketballPositionType(): void
{
$constraint = new Enum(entity: BasketballPositionType::class);
$constraint = new Enum(['entity' => BasketballPositionType::class]);

$this->context
->expects(self::never())
Expand All @@ -70,7 +79,7 @@ public function testValidBasketballPositionType(): void

public function testInvalidBasketballPositionType(): void
{
$constraint = new Enum(entity: BasketballPositionType::class);
$constraint = new Enum(['entity' => BasketballPositionType::class]);
$constraintValidationBuilder = $this->createMock(ConstraintViolationBuilder::class);

$constraintValidationBuilder
Expand Down
44 changes: 27 additions & 17 deletions Validator/Constraints/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,47 @@

namespace Fresh\DoctrineEnumBundle\Validator\Constraints;

use Attribute;
use Fresh\DoctrineEnumBundle\DBAL\Types\AbstractEnumType;
use Fresh\DoctrineEnumBundle\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Constraints\Choice;

/**
* ENUM Constraint.
*
* @deprecated Support for Enum annotation will be dropped in 9.0. Please switch to using \Fresh\DoctrineEnumBundle\Validator\Constraints\EnumType attribute instead.
*
* @author Artem Henvald <[email protected]>
*
* @Annotation
*/
#[Attribute(Attribute::TARGET_PROPERTY)]
class Enum extends Choice
{
/** @var string|AbstractEnumType<int|string, int|string> */
public $entity;

/**
* @param string $entity
* @param string|null $message
* @param string[]|null $groups
* @param mixed $payload
* @param array<string, array<string, string>> $options
*/
public function __construct(public string $entity, ?string $message = null, ?array $groups = null, mixed $payload = null)
public function __construct($options = null)
{
if (!\is_subclass_of($entity, AbstractEnumType::class)) {
throw new InvalidArgumentException(\sprintf('%s is not instance of %s', $entity, AbstractEnumType::class));
$this->strict = true;

if (isset($options['entity'])) {
/** @var AbstractEnumType<int|string, int|string> $entity */
$entity = $options['entity'];

if (\is_subclass_of($entity, AbstractEnumType::class)) {
$this->choices = $entity::getValues();
}
}

parent::__construct(
choices: $entity::getValues(),
strict: true,
message: $message,
groups: $groups,
payload: $payload
);
parent::__construct($options);
}

/**
* @return string[]
*/
public function getRequiredOptions(): array
{
return ['entity'];
}
}
Loading

0 comments on commit 02166fc

Please sign in to comment.