Skip to content

Instantly share code, notes, and snippets.

@zspine
Created April 10, 2020 10:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zspine/4586587cae7dad736ddd4690eeaa6c5d to your computer and use it in GitHub Desktop.
Save zspine/4586587cae7dad736ddd4690eeaa6c5d to your computer and use it in GitHub Desktop.
Doctrine brick money value object
<?php
namespace App\Entity\Embeddable;
use App\Model\Intl\MoneyInterface;
use Brick\Math\BigNumber;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\RoundingMode;
use Brick\Money\Context\CustomContext;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Brick\Money\Money as BrickMoney;
/**
* Class Money.
*
* @ORM\Embeddable()
*/
final class Money implements MoneyInterface
{
/**
* @var string
* @Groups({"payment:read", "payment:write"})
* @Assert\NotBlank()
* @Assert\PositiveOrZero()
*
* @ORM\Column(type="decimal", precision=14, scale=6, nullable=false, options={"default":0})
*/
private $amount = 0;
/**
* @Groups({"payment:read", "payment:write"})
* @Assert\NotBlank()
* @Assert\Currency()
*
* @ORM\Column(type="string", nullable=false, options={"default":"EUR"})
*
* @var string
*/
private $currency;
/**
* @var BrickMoney
*/
private $money;
/**
* Money constructor.
*
* @param string $value
* @param string|null $currency
*/
public function __construct($value = null, ?string $currency = null)
{
if (\is_int($value)) {
$value = (string)$value;
}
if (is_float($value)) {
$value = self::floatToString($value);
} else {
$value = (string)$value;
}
if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
throw new NumberFormatException(\sprintf('The given value "%s" does not represent a valid number.', $value));
}
$this->amount = $value ?? self::DEFAULT_VALUE;
$this->currency = (\mb_strlen($currency) > 0) ? strtoupper($currency) : self::DEFAULT_CURRENCY;
}
/**
* @return string
*/
public function __toString(): string
{
return sprintf("%s %s", $this->getCurrency(), $this->getAmount());
}
/**
* @param float $float
* @return string
*/
private static function floatToString(float $float): string
{
$currentLocale = \setlocale(LC_NUMERIC, '0');
\setlocale(LC_NUMERIC, 'C');
$result = (string)$float;
\setlocale(LC_NUMERIC, $currentLocale);
return $result;
}
/**
* @param string|float|integer $amount
* @param string|null $currency
* @return Money
*/
public static function create($amount, ?string $currency = null)
{
return new self($amount, $currency);
}
/**
* @param BrickMoney $money
* @return Money
*/
public static function createFromBrick(BrickMoney $money)
{
return new self($money->getAmount(), $money->getCurrency()->getCurrencyCode());
}
/**
* @return BrickMoney
*/
public function getMoney(): BrickMoney
{
if (null === $this->money) {
$this->money = BrickMoney::of($this->getAmount(), $this->getCurrency(), new CustomContext(5), RoundingMode::HALF_UP);
}
return $this->money;
}
/**
* @return string
*/
public function getAmount(): string
{
return $this->amount;
}
/**
* @return string
*/
public function getCurrency(): string
{
return $this->currency;
}
/**
* @return bool
*/
public function isBaseCurrency(): bool
{
return self::DEFAULT_CURRENCY === $this->currency;
}
/**
* @param Money $other
* @return bool
*/
public function isSameCurrency(Money $other)
{
return $this->getCurrency() === $other->getCurrency();
}
/**
* @param Money $other
* @return bool
*/
public function equals(Money $other)
{
return $this->isSameCurrency($other) && $this->getAmount() === $other->getAmount();
}
/**
* @param $that
* @param int $roundingMode
* @return Money
* @throws \Brick\Money\Exception\MoneyMismatchException
*/
public function plus($that, int $roundingMode = RoundingMode::HALF_UP): Money
{
if (\is_object($that)) {
$that = $this->getBrickObject($that);
}
$amount = $this->getMoney()->plus($that, $roundingMode);
return self::createFromBrick($amount);
}
/**
* @param BigNumber|int|float|string $number
* @return Money
*/
public function multipliedBy($number)
{
$amount = $this->getMoney()->multipliedBy($number, RoundingMode::HALF_UP);
return self::createFromBrick($amount);
}
/**
* @param MoneyInterface|BrickMoney|BigNumber|number|string $that
* @return bool
* @throws \Brick\Money\Exception\MoneyMismatchException
*/
public function isLessThan($that): bool
{
if (\is_object($that)) {
$that = $this->getBrickObject($that);
}
return $this->getMoney()->isLessThan($that);
}
/**
* @param $money
* @return BrickMoney
*/
private function getBrickObject($money)
{
if ($money instanceof BrickMoney) {
return $money;
} else if ($money instanceof MoneyInterface) {
return $money->getMoney();
}
throw new \InvalidArgumentException(\sprintf("Given value is not a valid brick money object"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment