Skip to content

Commit

Permalink
Add ScalarToEnumTransformer
Browse files Browse the repository at this point in the history
  • Loading branch information
ostrolucky committed Apr 8, 2018
1 parent ea73095 commit 9683e83
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

/*
* This file is part of the "elao/enum" package.
*
* Copyright (C) Elao
*
* @author Elao <contact@elao.com>
*/

namespace Elao\Enum\Bridge\Symfony\Form\DataTransformer;

use Elao\Enum\EnumInterface;
use Elao\Enum\Exception\InvalidValueException;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\InvalidArgumentException;
use Symfony\Component\Form\Exception\TransformationFailedException;

class ScalarToEnumTransformer implements DataTransformerInterface
{
/** @var string|EnumInterface */
private $enumClass;

public function __construct(string $enumClass)
{
if (!is_a($enumClass, EnumInterface::class, true)) {
throw new InvalidArgumentException(sprintf(
'"%s" is not an instance of "%s"',
$enumClass,
EnumInterface::class
));
}

$this->enumClass = $enumClass;
}

/**
* Transforms EnumInterface object to a scalar value.
*
* @param EnumInterface $value EnumInterface instance
*
* @throws TransformationFailedException When the transformation fails
*
* @return int|string Value of EnumInterface
*/
public function transform($value)
{
if ($value === null) {
return null;
}

if (!$value instanceof $this->enumClass) {
throw new TransformationFailedException(sprintf(
'Expected instance of "%s". Got "%s".',
$this->enumClass,
is_object($value) ? get_class($value) : gettype($value)
));
}

return $value->getValue();
}

/**
* Transforms scalar enum value to enumeration instance.
*
* @param int|string $value Value accepted by EnumInterface
*
* @throws TransformationFailedException When the transformation fails
*
* @return EnumInterface|null A single FlaggedEnum instance or null
*/
public function reverseTransform($value)
{
if ($value === null) {
return null;
}

try {
return $this->enumClass::get($value);
} catch (InvalidValueException $exception) {
throw new TransformationFailedException($exception->getMessage(), $exception->getCode(), $exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@

namespace Elao\Enum\Tests\Fixtures\Integration\Symfony\TestBundle\Controller;

use Elao\Enum\Bridge\Symfony\Form\DataTransformer\ScalarToEnumTransformer;
use Elao\Enum\Bridge\Symfony\Form\Type\EnumType;
use Elao\Enum\Bridge\Symfony\Form\Type\FlaggedEnumType;
use Elao\Enum\Tests\Fixtures\Enum\Gender;
use Elao\Enum\Tests\Fixtures\Enum\Permissions;
use Elao\Enum\Tests\Fixtures\Enum\SimpleEnum;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;

Expand Down Expand Up @@ -57,4 +61,35 @@ public function flaggedEnumFormAction(Request $request)
'form' => $form->createView(),
]);
}

public function enumFormUtilizingScalarTransformerAction(Request $request)
{
$data = [
'gender' => Gender::get(Gender::MALE),
'simpleEnum' => SimpleEnum::get(SimpleEnum::SECOND),
];

$options = interface_exists(ChoiceListInterface::class) ? ['choices_as_values' => true] : [];

$builder = $this->createFormBuilder($data)
->add('gender', ChoiceType::class, [
'choices' => ['customMaleLabel' => Gender::MALE, 'customFemaleLabel' => Gender::FEMALE],
] + $options)
->add('simpleEnum', ChoiceType::class, [
'choices' => ['customOneLabel' => SimpleEnum::FIRST, 'customSecondLabel' => SimpleEnum::SECOND],
] + $options)
->add('submit', SubmitType::class)
;

$builder->get('gender')->addModelTransformer(new ScalarToEnumTransformer(Gender::class));
$builder->get('simpleEnum')->addModelTransformer(new ScalarToEnumTransformer(SimpleEnum::class));

$form = $builder->getForm();

$form->handleRequest($request);

return $this->render('TestBundle::enum_type.html.twig', [
'form' => $form->createView(),
]);
}
}
4 changes: 4 additions & 0 deletions tests/Fixtures/Integration/Symfony/app/config/routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ readable_enum_form:
flagged_enum_form:
path: '/form-type/flagged-enum'
defaults: { _controller: 'TestBundle:FormType:flaggedEnumForm' }

form_with_scalar_transformer:
path: '/form-type/scalar-transformer-enum'
defaults: { _controller: 'TestBundle:FormType:enumFormUtilizingScalarTransformer' }
30 changes: 30 additions & 0 deletions tests/Integration/Bridge/Symfony/Form/EnumTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,34 @@ public function testFlaggedEnumForm()

$this->assertSame(['form[permissions]' => []], $form->getValues());
}

public function testEnumFormUtilizingScalarTransformer()
{
$client = static::createClient();
$crawler = $client->request(Request::METHOD_GET, '/form-type/scalar-transformer-enum');
$response = $client->getResponse();

$this->assertSame(Response::HTTP_OK, $response->getStatusCode());
$this->assertSame([
['selected', 'male', 'customMaleLabel'],
['', 'female', 'customFemaleLabel'],
], $crawler->filter('form select[name="form[gender]"] option')->extract(['selected', 'value', '_text']));
$this->assertSame([
['', '1', 'customOneLabel'],
['selected', '2', 'customSecondLabel'],
], $crawler->filter('form select[name="form[simpleEnum]"] option')->extract(['selected', 'value', '_text']));

$form = $crawler->filter('form')->form();
$form['form[gender]'] = 'female';
$form['form[simpleEnum]'] = '1';

$crawler = $client->submit($form);
$response = $client->getResponse();

$this->assertSame(Response::HTTP_OK, $response->getStatusCode());

$form = $crawler->filter('form')->form();

$this->assertSame(['form[gender]' => 'female', 'form[simpleEnum]' => '1'], $form->getValues());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of the "elao/enum" package.
*
* Copyright (C) Elao
*
* @author Elao <contact@elao.com>
*/

namespace Elao\Enum\Tests\Unit\Bridge\Symfony\Form\DataTransformer;

use Elao\Enum\Bridge\Symfony\Form\DataTransformer\ScalarToEnumTransformer;
use Elao\Enum\Tests\Fixtures\Enum\SimpleEnum;
use PHPUnit\Framework\TestCase;

class ScalarToEnumTransformerTest extends TestCase
{
/**
* @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
* @expectedExceptionMessage "stdClass" is not an instance of "Elao\Enum\EnumInterface"
*/
public function testThrowsExceptionIfNotEnumInstance()
{
new ScalarToEnumTransformer(\stdClass::class);
}

public function testTransform()
{
$transformer = new ScalarToEnumTransformer(SimpleEnum::class);

$this->assertSame(SimpleEnum::FIRST, $transformer->transform(SimpleEnum::FIRST()));
}

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage Expected instance of "Elao\Enum\Tests\Fixtures\Enum\SimpleEnum". Got "string".
*/
public function testTransformThrowsExceptionOnNonEnum()
{
$transformer = new ScalarToEnumTransformer(SimpleEnum::class);

$transformer->transform('foo');
}

public function testReverseTransform()
{
$transformer = new ScalarToEnumTransformer(SimpleEnum::class);

$this->assertSame(SimpleEnum::FIRST(), $transformer->reverseTransform(SimpleEnum::FIRST));
}

/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage "1" is not an acceptable value for "Elao\Enum\Tests\Fixtures\Enum\SimpleEnum
*/
public function testReverseTransformThrowsExceptionOnValueNotInEnum()
{
$transformer = new ScalarToEnumTransformer(SimpleEnum::class);

$transformer->reverseTransform('1');
}
}
36 changes: 36 additions & 0 deletions tests/Unit/Bridge/Symfony/Form/Type/EnumTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@

namespace Elao\Enum\Tests\Unit\Bridge\Symfony\Form\Type;

use Elao\Enum\Bridge\Symfony\Form\DataTransformer\ScalarToEnumTransformer;
use Elao\Enum\Bridge\Symfony\Form\Type\EnumType;
use Elao\Enum\Tests\Fixtures\Enum\Gender;
use Elao\Enum\Tests\Fixtures\Enum\SimpleEnum;
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Test\FormIntegrationTestCase;

Expand Down Expand Up @@ -447,6 +450,39 @@ public function testChoicesAsValueCanBeLimitedUsingChoicesOption()
$this->assertSame(Gender::UNKNOW, $field->getViewData());
}

public function testTransformationOfScalarChoices()
{
$options = interface_exists(ChoiceListInterface::class) ? ['choices_as_values' => true] : [];

$builder = $this->factory->createBuilder(
ChoiceType::class,
Gender::FEMALE(),
[
'choices' => ['maleLabel' => Gender::MALE, 'femaleLabel' => Gender::FEMALE],
] + $options
);

$field = $builder->addModelTransformer(new ScalarToEnumTransformer(Gender::class))->getForm();

$view = $field->createView();
/** @var ChoiceView[] $choices */
$choices = $view->vars['choices'];

$this->assertCount(2, $choices);

$choice = $choices[0];
$this->assertSame('maleLabel', $choice->label);
$this->assertSame(Gender::MALE, $choice->value);
$this->assertSame(Gender::MALE, $choice->data);
$this->assertSame(Gender::FEMALE(), $field->getData());

$field->submit(Gender::MALE);

$this->assertTrue($field->isSynchronized());
$this->assertSame(Gender::MALE(), $field->getData());
$this->assertSame(Gender::MALE()->getValue(), $field->getViewData());
}

private function assertSubForm(FormInterface $form, $data, $viewData)
{
$this->assertSame($data, $form->getData(), '->getData() of sub form #' . $form->getName());
Expand Down

0 comments on commit 9683e83

Please sign in to comment.