add force_full_scale option to handle all cases
This commit is contained in:
parent
40f25121c3
commit
fb2b37a8f3
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\Form\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
class NumberToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $forceFullScale;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $scale;
|
||||
|
||||
/**
|
||||
* @param bool $forceFullScale
|
||||
* @param int|null $scale
|
||||
*/
|
||||
public function __construct($forceFullScale = false, $scale = null)
|
||||
{
|
||||
$this->forceFullScale = $forceFullScale;
|
||||
$this->scale = $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$valueIsInt = is_int($value);
|
||||
if (!$valueIsInt && !is_float($value)) {
|
||||
throw new TransformationFailedException('Expected an int or a float.');
|
||||
}
|
||||
|
||||
if ($this->forceFullScale && is_int($this->scale)) {
|
||||
if ($valueIsInt) {
|
||||
$value = floatval($value);
|
||||
}
|
||||
|
||||
return number_format($value, $this->scale, '.', '');
|
||||
}
|
||||
|
||||
try {
|
||||
return (string) $value;
|
||||
} catch (\Exception $e) {
|
||||
throw new TransformationFailedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,11 @@
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\Form\Type;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\NumberToStringTransformer;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class DecimalType extends AbstractType
|
||||
{
|
||||
@ -24,27 +24,20 @@ class DecimalType extends AbstractType
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addModelTransformer(new CallbackTransformer(function ($value) {
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
$builder->addModelTransformer(new NumberToStringTransformer($options['force_full_scale'], $options['scale']));
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a string.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, function ($value) {
|
||||
if (null === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!is_int($value) && !is_float($value)) {
|
||||
throw new TransformationFailedException('Expected an int or a float.');
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}));
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults(array(
|
||||
'force_full_scale' => false
|
||||
));
|
||||
$resolver->setAllowedTypes('force_full_scale', array(
|
||||
'boolean'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,8 +21,11 @@ class Price
|
||||
/** @Id @Column(type="integer") */
|
||||
public $id;
|
||||
|
||||
/** @Column(type="decimal") */
|
||||
public $value;
|
||||
/** @Column(type="decimal", scale=2) */
|
||||
public $doesNotPreserveFullScaleValue;
|
||||
|
||||
/** @Column(type="string") */
|
||||
public $preserveFullScaleValueSimulation;
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
@ -31,6 +34,7 @@ class Price
|
||||
public function __construct(int $id, float $value)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->value = $value;
|
||||
$this->doesNotPreserveFullScaleValue = $value;
|
||||
$this->preserveFullScaleValueSimulation = number_format($value, 2, '.', '');
|
||||
}
|
||||
}
|
||||
|
@ -60,33 +60,114 @@ class DecimalTypeTest extends BaseTypeTest
|
||||
$this->em = null;
|
||||
}
|
||||
|
||||
public function testSubmitWithSameStringValue()
|
||||
// On some platforms, fetched decimal values are rounded (the full scale is not preserved)
|
||||
// eg : on SQLite, inserted float value 4.50 will be fetched as string value "4.5"
|
||||
public function testSubmitWithSameStringValueOnAPlatformThatDoesNotPreserveFullScaleValueWithoutForceFullScale()
|
||||
{
|
||||
$price = new Price(1, 1.23);
|
||||
$this->em->persist($price);
|
||||
$fullScalePrice = new Price(1, 1.23);
|
||||
$nonFullScalePrice = new Price(2, 4.50);
|
||||
$this->em->persist($fullScalePrice);
|
||||
$this->em->persist($nonFullScalePrice);
|
||||
$this->em->flush();
|
||||
|
||||
$this->em->refresh($price);
|
||||
$this->em->refresh($fullScalePrice);
|
||||
$this->em->refresh($nonFullScalePrice);
|
||||
|
||||
$this->assertInternalType('string', $price->value);
|
||||
$stringValue = $price->value;
|
||||
$this->assertInternalType('string', $fullScalePrice->doesNotPreserveFullScaleValue);
|
||||
$fullScalePriceStringValue = $fullScalePrice->doesNotPreserveFullScaleValue;
|
||||
|
||||
$formBuilder = $this->factory->createBuilder(FormType::class, $price, array(
|
||||
$formBuilder = $this->factory->createBuilder(FormType::class, $fullScalePrice, array(
|
||||
'data_class' => Price::class
|
||||
));
|
||||
$formBuilder->add('value', static::TESTED_TYPE);
|
||||
$formBuilder->add('doesNotPreserveFullScaleValue', static::TESTED_TYPE, array(
|
||||
'force_full_scale' => false
|
||||
));
|
||||
|
||||
$form = $formBuilder->getForm();
|
||||
$form->submit(array(
|
||||
'value' => $stringValue
|
||||
'doesNotPreserveFullScaleValue' => $fullScalePriceStringValue
|
||||
));
|
||||
|
||||
$this->assertSame($stringValue, $price->value);
|
||||
$this->assertSame($fullScalePriceStringValue, $fullScalePrice->doesNotPreserveFullScaleValue);
|
||||
|
||||
$this->assertInternalType('string', $nonFullScalePrice->doesNotPreserveFullScaleValue);
|
||||
$nonFullScalePriceStringValue = $nonFullScalePrice->doesNotPreserveFullScaleValue;
|
||||
|
||||
$formBuilder = $this->factory->createBuilder(FormType::class, $nonFullScalePrice, array(
|
||||
'data_class' => Price::class
|
||||
));
|
||||
$formBuilder->add('doesNotPreserveFullScaleValue', static::TESTED_TYPE, array(
|
||||
'force_full_scale' => false
|
||||
));
|
||||
|
||||
$form = $formBuilder->getForm();
|
||||
$form->submit(array(
|
||||
'doesNotPreserveFullScaleValue' => $nonFullScalePriceStringValue
|
||||
));
|
||||
|
||||
$this->assertSame($nonFullScalePriceStringValue, $nonFullScalePrice->doesNotPreserveFullScaleValue);
|
||||
|
||||
$unitOfWork = $this->em->getUnitOfWork();
|
||||
$unitOfWork->computeChangeSets();
|
||||
|
||||
$this->assertSame(array(), $unitOfWork->getEntityChangeSet($price));
|
||||
$this->assertSame(array(), $unitOfWork->getEntityChangeSet($fullScalePrice));
|
||||
$this->assertSame(array(), $unitOfWork->getEntityChangeSet($nonFullScalePrice));
|
||||
}
|
||||
|
||||
// On some platforms, fetched decimal values are not rounded at all (the full scale is preserved)
|
||||
// eg : on PostgreSQL, inserted float value 4.50 will be fetched as string value "4.50"
|
||||
public function testSubmitWithSameStringValueOnAPlatformThatPreserveFullScaleValueWithForceFullScale()
|
||||
{
|
||||
$fullScalePrice = new Price(1, 1.23);
|
||||
$nonFullScalePrice = new Price(2, 4.50);
|
||||
$this->em->persist($fullScalePrice);
|
||||
$this->em->persist($nonFullScalePrice);
|
||||
$this->em->flush();
|
||||
|
||||
$this->em->refresh($fullScalePrice);
|
||||
$this->em->refresh($nonFullScalePrice);
|
||||
|
||||
$this->assertInternalType('string', $fullScalePrice->preserveFullScaleValueSimulation);
|
||||
$fullScalePriceStringValue = $fullScalePrice->preserveFullScaleValueSimulation;
|
||||
|
||||
$formBuilder = $this->factory->createBuilder(FormType::class, $fullScalePrice, array(
|
||||
'data_class' => Price::class
|
||||
));
|
||||
$formBuilder->add('preserveFullScaleValueSimulation', static::TESTED_TYPE, array(
|
||||
'force_full_scale' => true,
|
||||
'scale' => 2
|
||||
));
|
||||
|
||||
$form = $formBuilder->getForm();
|
||||
$form->submit(array(
|
||||
'preserveFullScaleValueSimulation' => $fullScalePriceStringValue
|
||||
));
|
||||
|
||||
$this->assertSame($fullScalePriceStringValue, $fullScalePrice->preserveFullScaleValueSimulation);
|
||||
|
||||
$this->assertInternalType('string', $nonFullScalePrice->preserveFullScaleValueSimulation);
|
||||
$nonFullScalePriceStringValue = $nonFullScalePrice->preserveFullScaleValueSimulation;
|
||||
|
||||
$formBuilder = $this->factory->createBuilder(FormType::class, $nonFullScalePrice, array(
|
||||
'data_class' => Price::class
|
||||
));
|
||||
$formBuilder->add('preserveFullScaleValueSimulation', static::TESTED_TYPE, array(
|
||||
'force_full_scale' => true,
|
||||
'scale' => 2
|
||||
));
|
||||
|
||||
$form = $formBuilder->getForm();
|
||||
$form->submit(array(
|
||||
'preserveFullScaleValueSimulation' => $nonFullScalePriceStringValue
|
||||
));
|
||||
|
||||
$this->assertSame($nonFullScalePriceStringValue, $nonFullScalePrice->preserveFullScaleValueSimulation);
|
||||
|
||||
$unitOfWork = $this->em->getUnitOfWork();
|
||||
$unitOfWork->computeChangeSets();
|
||||
|
||||
$this->assertSame(array(), $unitOfWork->getEntityChangeSet($fullScalePrice));
|
||||
$this->assertSame(array(), $unitOfWork->getEntityChangeSet($nonFullScalePrice));
|
||||
}
|
||||
|
||||
public function testSubmitNull($expected = null, $norm = null, $view = null)
|
||||
|
Reference in New Issue
Block a user