您在Doctrine中存储货币价值的策略是什么?Symfony的money字段非常方便,但如何将其映射到Doctrine的列呢?是否有提供DBAL类型的bundle可用于此?
使用float或int列类型是不够的,因为当您处理货币时通常也会涉及货币。我正在使用两个字段来处理此问题,但手动处理很麻烦。
我建议使用像Money\Money这样的值对象。
# app/Resources/Money/doctrine/Money.orm.yml
Money\Money:
type: embeddable
fields:
amount:
type: integer
embedded:
currency:
class: Money\Currency
# app/Resources/Money/doctrine/Currency.orm.yml
Money\Currency:
type: embeddable
fields:
code:
type: string
length: 3
# app/config.yml
doctrine:
orm:
mappings:
Money:
type: yml
dir: "%kernel.root_dir%/../app/Resources/Money/doctrine"
prefix: Money
class YourEntity
{
/**
* @ORM\Embedded(class="\Money\Money")
*/
private $value;
public function __construct(string $currencyCode)
{
$this->value = new \Money\Money(0, new \Money\Currency($currencyCode));
}
public function getValue(): \Money\Money
{
return $this->value;
}
}
考虑使用 decimal
类型:
/**
* @ORM\Column(type="decimal", precision=7, scale=2)
*/
protected $price = 0;
请注意,有一些货币具有三位小数。如果您打算使用这样的货币,则应将scale
参数设置为3
。如果您打算混合具有两个和三个小数位的货币,则在只有两个小数位的情况下添加一个尾随的0
。$price
在PHP中将是一个字符串。您可以将其转换为float
或将其乘以100(对于具有三位小数的货币,则为1000),然后将其转换为int
。
ManyToOne
关系。echo "<?php var_dump((.1 + .1 + .1 === .3));" | php
- JonnyOrder.php:
<?php
namespace Shop\Entity;
/**
* @Entity
*/
class Order
{
/**
* @Column(type="money")
*
* @var \Shop\ValueObject\Money
*/
private $money;
/**
* ... other variables get defined here
*/
/**
* @param \Shop\ValueObject\Money $money
*/
public function setMoney(\Shop\ValueObject\Money $money)
{
$this->money = $money;
}
/**
* @return \Shop\ValueObject\Money
*/
public function getMoney()
{
return $this->money;
}
/**
* ... other getters and setters are coming here ...
*/
}
Money.php:
<?php
namespace Shop\ValueObject;
class Money
{
/**
* @param float $value
* @param string $currency
*/
public function __construct($value, $currency)
{
$this->value = $value;
$this->currency = $currency;
}
/**
* @return float
*/
public function getValue()
{
return $this->value;
}
/**
* @return string
*/
public function getCurrency()
{
return $this->currency;
}
}
MoneyType.php:
<?php
namespace Shop\Types;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Shop\ValueObject\Money;
class MoneyType extends Type
{
const MONEY = 'money';
public function getName()
{
return self::MONEY;
}
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return 'MONEY';
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
list($value, $currency) = sscanf($value, 'MONEY(%f %d)');
return new Money($value, $currency);
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value instanceof Money) {
$value = sprintf('MONEY(%F %D)', $value->getValue(), $value->getCurrency());
}
return $value;
}
public function canRequireSQLConversion()
{
return true;
}
public function convertToPHPValueSQL($sqlExpr, AbstractPlatform $platform)
{
return sprintf('AsText(%s)', $sqlExpr);
}
public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)
{
return sprintf('PointFromText(%s)', $sqlExpr);
}
}
// preparing everything for example getting the EntityManager...
// Store a Location object
use Shop\Entity\Order;
use Shop\ValueObject\Money;
$order = new Order();
// set whatever needed
$order->setMoney(new Money(99.95, 'EUR'));
// other setters get called here.
$em->persist($order);
$em->flush();
$em->clear();
MoneyType.php
示例毫无意义。MySQL 中没有 MONEY
函数。你只是复制了 Doctrine 的示例。这是误导性的。 - jkucharovic/** @Entity */
class Order
{
/** @Id */
private $id;
/** @Embedded(class = "Money") */
private $money;
}
/** @Embeddable */
class Money
{
/** @Column(type = "int") */ // better than decimal see the mathiasverraes/money documentation
private $amount;
/** @Column(type = "string") */
private $currency;
}
100-EUR
的形式将值保存到数据库中,表示1欧元。
int
(金额)+string
(货币),但这太尴尬了。 - SiliconMind+
符号?你有一个字段用于金额和一个字段用于货币吗?如果你需要处理不同的货币,我建议你在问题中添加一条注释,因为这会使问题变得比只有一个货币更加复杂。 - A.L+
符号并不重要,因为金额存储为int
,它可以是正值或负值。至少在我的情况下是如此,尽管浮点数也是一样的。 - SiliconMind