Closed zspine closed 4 years ago
Hi, AFAIK there isn't a way to map a value object to 2 database fields (value + currency) in Doctrine. However, what I usually do it store the money as 2 fields in the entity, and do the transformation to and from Money
in the getter/setter:
class Product
{
private int $price;
private string $currency;
public function setPrice(Money $price) : void
{
$this->price = $price->getMinorAmount()->toInt();
$this->currency = $price->getCurrency()->getCurrencyCode();
}
public function getPrice() : Money
{
return Money::ofMinor($this->price, $this->currency);
}
}
This has the advantage to only do the transformation when the value is requested, and not every time the entity is hydrated, even when you don't need it.
Hi, Thanks a lot for the quick response. I agree there are no options to map a value to 2 database fields, but I am not trying to create a custom mapping type. Instead, I am trying to work with doctrine embeddables (value object) so I can map both amount and currency to relevant database fields. Actually my current implementation is similar to your above example and it's working without any issues, however it make more sense to have an option to map BigDecimal, BigInteger & Money object itself as an embeddable . Here is my current implementation:
//Money.php
use Doctrine\ORM\Mapping as ORM;
use Brick\Math\RoundingMode;
use Brick\Money\Context\CustomContext;
use Brick\Money\Money as BrickMoney;
/**
* @ORM\Embeddable()
*/
final class Money
{
/**
* @ORM\Column(type="decimal", precision=14, scale=6, nullable=false, options={"default":0})
*/
private $amount = 0;
/**
* @ORM\Column(type="string", nullable=false, options={"default":"EUR"})
*/
private $currency;
/**
* @var BrickMoney
*/
private $money;
public function __construct($value = null, ?string $currency = null)
{
$this->amount = $value ?? "0";
$this->currency = (\mb_strlen($currency) > 0) ? strtoupper($currency) : "USD";
}
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;
}
public function getAmount(): string
{
return $this->amount;
}
public function getCurrency(): string
{
return $this->currency;
}
}
//Product.php
use Doctrine\ORM\Mapping as ORM;
class Product
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $name;
/**
* @ORM\Column(type="string")
*/
private $slug;
/**
* @ORM\Embedded(class="App\Entity\Embeddable\Money")
*/
private $price;
}
```php
Thank you
This would work as well, indeed! Can we close this issue?
@BenMorel Sure, Thank you for your quick response. Thanks again for this excellent library, it saved us from lots of hassle. If we managed to solve the embeddable issue will definitely share the solution.
Cheers!
Hi, AFAIK there isn't a way to map a value object to 2 database fields (value + currency) in Doctrine. However, what I usually do it store the money as 2 fields in the entity, and do the transformation to and from
Money
in the getter/setter:class Product { private int $price; private string $currency; public function setPrice(Money $price) : void { $this->price = $price->getMinorAmount()->toInt(); $this->currency = $price->getCurrency()->getCurrencyCode(); } public function getPrice() : Money { return Money::ofMinor($this->price, $this->currency); } }
This has the advantage to only do the transformation when the value is requested, and not every time the entity is hydrated, even when you don't need it.
Hi, finally we ended up following your implementation example. Using doctrine embeddable makes things harder to understand and also seems like unnecessary extra layer with brick money object. Thanks again for your input on this!
Hi
Excellent library to work with money. I tried to implement it as a value object in doctrine and it seems there are some complicated challenges and I have to use another separate proxy class to work with value objects. Do you have any recommendation docs to work with ORMs?
Cheers