您使用Doctrine存储货币价值的策略是什么? Symfony的money字段非常方便,但如何将其映射到Doctrine的专栏?是否有一个提供DBAL类型的捆绑包?
float
或int
列类型不足,因为当您处理资金时,您经常处理货币。我为此使用了两个字段,但手动处理起来很不方便。
答案 0 :(得分:12)
考虑使用decimal
类型:
/**
* @ORM\Column(type="decimal", precision=7, scale=2)
*/
protected $price = 0;
请注意,有些货币有三个小数位。如果您打算使用此类货币,scale
参数应为3
。如果您打算将货币与两个和三个小数位混合,如果只有两个小数位,则添加一个尾随0
。
注意:$price
将是PHP中的字符串。您可以将其转换为float
或将其乘以100(或者在具有三个小数位的货币的情况下为1000)并将其转换为int
。
货币本身是一个单独的领域;它可以是带有三个字母货币代码的字符串。或者 - 干净的方式 - 您可以创建一个包含您正在使用的所有货币的表格,然后为货币条目创建ManyToOne
关系。
答案 1 :(得分:8)
我建议使用像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;
}
}
答案 2 :(得分:2)
只要您告诉学说如何处理它,您就可以定义自己的字段类型。为了解释这一点,我组建了一个'''''和''''''使用'' -ValueObject的地方。
首先,我们需要一个实体和另一个在实体中使用的ValueObject:
Order.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;
}
}
到目前为止没什么特别的。 &#34;魔术&#34;进来了:
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();
您可以编写一个映射器,将来自Symfony的money字段的输入映射到Money-ValueObject,以进一步简化此操作。
未经测试,但之前我使用过这个概念并且有效。如果您有疑问,请告诉我。
答案 3 :(得分:1)
我正在寻找解决这个问题的方法,然后谷歌搜索this page。
在那里,说明了自Doctrine 2.5以来可用的Embeddable field。
通过这样的方式,您可以将价值观视为具有更多&#34; params&#34;的货币价值。
一个例子:
/** @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;
}
希望这会有所帮助。
更新
我写了PHP library that contains some useful value objects。
还有一个值对象来管理货币值(包装了很好的MoneyPHP library)并使用Doctrine type将它们保存到数据库中。
此类型以100-EUR
的形式将值保存到数据库,代表1欧元。