Installation
composer require 99designs/money-php
Add to composer.json if not using autoloading:
"autoload": {
"psr-4": {
"App\\": "app/",
"Money\\": "vendor/99designs/money/src/"
}
}
First Use Case: Creating a Money Object
use Money\Money;
use Money\Currency;
$amount = 100; // Amount in smallest currency unit (e.g., cents for USD)
$currency = new Currency('USD');
$money = new Money($amount, $currency);
Where to Look First
src/Money/ directory for core classes (Money, Currency, Formatter).tests/ for edge cases and examples.Money Creation and Manipulation
$usd = new Money(1000, new Currency('USD')); // $10.00
$eur = new Money(900, new Currency('EUR')); // €9.00
// Addition
$total = $usd->add($eur);
// Subtraction
$change = $usd->subtract($eur);
// Multiplication/Division (returns integer amount)
$half = $usd->divideBy(2);
$doubled = $usd->multiplyBy(2);
Currency Conversion
Requires a CurrencyConverter (e.g., from moneyphp/money-bundle or custom implementation).
use Money\Converter\CurrencyConverter;
$converter = new CustomCurrencyConverter(); // Implement CurrencyConverterInterface
$converted = $converter->convert($usd, new Currency('EUR'));
Formatting for Display
use Money\Formatter\IntlMoneyFormatter;
$formatter = new IntlMoneyFormatter('en_US');
echo $formatter->format($usd); // "$10.00"
Validation and Constraints
use Money\Constraints\PositiveMoney;
$validator = new PositiveMoney();
$validator->validate($usd); // Throws \Money\Exception\InvalidMoneyException if invalid
amount (integer) and currency (string) separately.
// Migration
Schema::create('orders', function (Blueprint $table) {
$table->integer('amount');
$table->string('currency', 3);
});
// Model
public function getMoneyAttribute() {
return new Money($this->amount, new Currency($this->currency));
}
Money objects in JSON responses with a custom JSON encoder or formatter.$this->app->bind(
Money\Converter\CurrencyConverterInterface::class,
CustomCurrencyConverter::class
);
Precision Loss
float/double values.Currency Codes
USD, EUR). Invalid codes throw exceptions.new Currency('US'); // Throws \Money\Exception\InvalidCurrencyCodeException
Immutable Objects
Money objects are immutable. Use methods like add()/subtract() to create new instances.$money->amount = 200;).Converter Dependencies
CurrencyConverterInterface or use a bundle like moneyphp/money-bundle.Negative Amounts
Money allows negative amounts. Use PositiveMoney constraint to enforce positivity:
$validator = new PositiveMoney();
$validator->validate($money); // Throws if amount < 0
assertEquals for testing:
use PHPUnit\Framework\TestCase;
$this->assertEquals(
new Money(1000, new Currency('USD')),
$usd->add(new Money(0, new Currency('USD')))
);
amount and currency for debugging:
\Log::debug("Money amount: {$money->getAmount()}, currency: {$money->getCurrency()->getCode()}");
Custom Formatters
Extend Money\Formatter\MoneyFormatterInterface for locale-specific formatting:
class CustomFormatter implements MoneyFormatterInterface {
public function format(Money $money) {
return sprintf('%s %s', $money->getAmount() / 100, $money->getCurrency()->getCode());
}
}
Custom Constraints
Implement Money\Constraints\MoneyConstraintInterface for business rules:
class MaxOrderConstraint implements MoneyConstraintInterface {
public function validate(Money $money) {
if ($money->getAmount() > 100000) {
throw new \RuntimeException('Order exceeds maximum allowed amount.');
}
}
}
Custom CurrencyConverter
Implement Money\Converter\CurrencyConverterInterface for exchange rates:
class ApiCurrencyConverter implements CurrencyConverterInterface {
public function convert(Money $money, Currency $toCurrency) {
$rate = $this->fetchRate($money->getCurrency(), $toCurrency);
return new Money(
(int) round($money->getAmount() * $rate),
$toCurrency
);
}
}
Laravel Integration
public function getPriceAttribute() {
return new Money($this->amount, new Currency($this->currency));
}
public function setPriceAttribute($value) {
$this->amount = $value->getAmount();
$this->currency = $value->getCurrency()->getCode();
}
public function handle($request, Closure $next) {
$request->merge(['currency' => 'EUR']);
return $next($request);
}
How can I help you explore Laravel packages today?