gpt4 book ai didi

php - Symfony2 MoneyType 与除数 : integer conversion leads to wrong database values

转载 作者:IT老高 更新时间:2023-10-28 13:22:51 26 4
gpt4 key购买 nike

我们将所有与货币相关的值都以美分的形式存储在我们的数据库中(ODM 但 ORM 的行为可能相同)。我们使用 MoneyType 将面向用户的值 (12,34€) 转换为他们的美分表示 (1234c)。 typical float precision这里出现了问题:由于精度不足,许多情况下会产生舍入误差,而这些误差仅在调试时可见。 MoneyType 会将传入的字符串转换为可能不精确的 float ("1765"=> 1764.9999999998)。

一旦你坚持这些值(value)观,事情就会变得糟糕:

class Price {
/**
* @var int
* @MongoDB\Field(type="int")
**/
protected $cents;
}

将转换传入的值(float!),如:

namespace Doctrine\ODM\MongoDB\Types;
class IntType extends Type
{
public function convertToDatabaseValue($value)
{
return $value !== null ? (integer) $value : null;
}
}

(整数)强制转换将去掉值的​​尾数而不是四舍五入,实际上导致将错误的值写入数据库(当“1765”内部为 1764.9999999998 时,是 1764 而不是 1765)。

这是一个应该在任何 Symfony2 容器中显示问题的单元测试:

//for better debugging: set ini_set('precision', 17); 

class PrecisionTest extends WebTestCase
{
private function buildForm() {
$builder = $this->getContainer()->get('form.factory')->createBuilder(FormType::class, null, []);
$form = $builder->add('money', MoneyType::class, [
'divisor' => 100
])->getForm();
return $form;
}

// high-level symptom
public function testMoneyType() {
$form = $this->buildForm();
$form->submit(['money' => '12,34']);

$data = $form->getData();

$this->assertEquals(1234, $data['money']);
$this->assertEquals(1234, (int)$data['money']);

$form = $this->buildForm();
$form->submit(['money' => '17,65']);

$data = $form->getData();

$this->assertEquals(1765, $data['money']);
$this->assertEquals(1765, (int)$data['money']); //fails: data[money] === 1764
}

//root cause
public function testParsedIntegerPrecision() {

$string = "17,65";
$transformer = new MoneyToLocalizedStringTransformer(2, false,null, 100);
$value = $transformer->reverseTransform($string);

$int = (integer) $value;
$float = (float) $value;

$this->assertEquals(1765, (float)$float);
$this->assertEquals(1765, $int); //fails: $int === 1764
}

}

请注意,此问题并不总是可见的!如您所见,“12,34”运行良好,“17,65”或“18,65”将失败。

在这里工作的最佳方法是什么(就 Symfony Forms/Doctrine 而言)? NumberTransformer 或 MoneyType 不应该返回整数值 - 人们可能还想保存 float ,因此我们无法解决那里的问题。我考虑过在持久层中覆盖 IntType,有效地舍入每个传入的整数值,而不是强制转换。另一种方法是将字段存储为 MongoDB 中的 float ...

PHP 基本问题 is discussed here .

最佳答案

现在我决定使用我自己的 MoneyType,它在内部对整数调用“round”。

<?php

namespace AcmeBundle\Form;

use Symfony\Component\Form\FormBuilderInterface;


class MoneyToLocalizedStringTransformer extends \Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer {

public function reverseTransform($value)
{
return round(parent::reverseTransform($value));
}
}

class MoneyType extends \Symfony\Component\Form\Extension\Core\Type\MoneyType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->addViewTransformer(new MoneyToLocalizedStringTransformer(
$options['scale'],
$options['grouping'],
null,
$options['divisor']
))
;
}
}

关于php - Symfony2 MoneyType 与除数 : integer conversion leads to wrong database values,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41286474/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com