Symfony钱场类型和学说

Sil*_*ind 14 symfony doctrine-orm

您使用Doctrine存储货币价值的策略是什么?Symfony的资金领域非常方便,但如何将其映射到Doctrine的专栏?是否有一个提供DBAL类型的包?

floatint列类型不足,因为当你处理钱时,你经常处理货币.我正在使用两个字段,但手动处理很尴尬.

lxg*_*lxg 13

考虑使用decimal类型:

/**
 * @ORM\Column(type="decimal", precision=7, scale=2)
 */
protected $price = 0;
Run Code Online (Sandbox Code Playgroud)

请注意,有些货币有三个小数位.如果您打算使用此类货币,则scale参数应为3.如果您打算将货币与两个和三个小数位混合,0如果只有两个小数位,则添加一个尾随.

注意:$price将是PHP中的字符串.您可以将其float强制转换为或将其乘以100(或者在具有三个小数位的货币的情况下为1000)并将其强制转换为int.


货币本身是一个单独的领域; 它可以是带有三个字母货币代码的字符串.或者 - 干净的方式 - 您可以创建一个包含您正在使用的所有货币的表格,然后ManyToOne为货币条目创建关系.

  • 不要考虑使用`decimal`类型!PHP不能准确地表示小数,这在金钱方面肯定是个问题!始终以最小的单位(美分,便士等)存钱. (4认同)
  • 因为货币价值通常由两部分组成(例如美元/美分)。并且第二部分的位数可能会有所不同。存储为整数的货币值始终需要有关第二部分的位数的信息。使用小数字段,您不必关心位数,因为如果需要,可以修剪它们。 (2认同)

Jon*_*nny 8

我建议使用像Money\Money这样的值对象.

# app/Resources/Money/doctrine/Money.orm.yml
Money\Money:
  type: embeddable
  fields:
    amount:
      type: integer
  embedded:
    currency:
      class: Money\Currency
Run Code Online (Sandbox Code Playgroud)
# app/Resources/Money/doctrine/Currency.orm.yml
Money\Currency:
  type: embeddable
  fields:
    code:
      type: string
      length: 3
Run Code Online (Sandbox Code Playgroud)
# app/config.yml
doctrine:
  orm:
    mappings:
      Money:
        type: yml
        dir: "%kernel.root_dir%/../app/Resources/Money/doctrine"
        prefix: Money
Run Code Online (Sandbox Code Playgroud)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)


spe*_*ius 4

您可以定义自己的字段类型,只要您告诉该学说如何处理它即可。为了解释这一点,我创建了一个“商店”和“订单”,其中使用了“金钱”-ValueObject。

首先,我们需要一个 Entity 和另一个 ValueObject,它在实体中使用:

订单.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 ...
     */
}
Run Code Online (Sandbox Code Playgroud)

钱.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止没有什么特别的。“魔力”就在这里:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用下面的代码:

// 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();
Run Code Online (Sandbox Code Playgroud)

您可以编写一个映射器,将来自 Symfony 货币字段的输入映射到 Money-ValueObject 中,以进一步简化这一过程。

这里解释了更多细节:http://doctrine-orm.readthedocs.org/en/latest/cookbook/advanced-field-value-conversion-using-custom-mapping-types.html

未经测试,但我之前使用过这个概念并且它有效。如果您有疑问,请告诉我。

  • 您的“MoneyType.php”示例毫无意义。MySQL 中没有“MONEY”函数。您刚刚复制了 Dotrine 示例。这是误导性的。 (2认同)