All packages
Money for Laravel
Migrating from a custom Money class
The package lives in its own RoundlyConsulting\Money namespace and reuses none of the common legacy class names, so the old and the new class can live side by side while you move call sites over:
| Legacy call | Money for Laravel | Note |
|---|---|---|
| Money::of($minor, 'EUR') | Money::ofMinor($minor, 'EUR') | There is deliberately no Money::of(). |
| getMinorAmount() / getAmount() returning an int | minor() | A canonical string — use minorInt() only where an int is genuinely required. |
| getAmount() returning minor units as a string | minor() | Not toDecimal(). |
| (int) $row->price | Money::ofMinor($row->price, $code) | Never cast raw minors — PHP loses precision above int64. |
| $a->getAmount() > $b->getAmount() | $a->isGreaterThan($b) | PHP compares numeric strings through floats above int64. |
| getCurrency()->getCode() | currency()->code | — |
| plus() / minus() / times() | add() / subtract() / multiply() | — |
| A subtract() that clamps at zero | Discount::applyTo() | Or Money::max([$x->subtract($y), Money::zero($c)]). |
| multiply(float) / divide(float) | multiply('1.19') / divide(Ratio::of(…)) | Or percentage('20') — no floats. |
| fromMajor(float) | Money::ofMajor('10.50', $code) | A string or an int; a float is a TypeError. |
| format($locale) dividing by 100 | format($locale) | Exponent-aware: JPY 0, BHD 3, custom up to 18. |
| allocate(list<int>) | allocate(...$ratios) | Variadic ints, integer strings or decimals. |
| A float grossDivisor() on a tax rate | TaxRate::fromBasisPoints($bp)->netFromGross($gross) | One exact rational, one rounding. |
| A hand-written minor-unit rescale | MinorUnits::rescale() | Or MinorUnits::toDecimal(). |
| Config ints such as PHP_ROUND_HALF_UP | RoundingModes::fromValue(config('pkg.rounding'), 'pkg.rounding') | snake_case RoundingMode names. |
Database columns
- Existing bigint money columns keep working with AsMoney; beyond int64 the engine refuses the write.
- New columns: $table->money('price', currency: 'currency') — a decimal(38, 0) amount plus a currency column.
- A cast stores only the currency code, so every persisted currency must be registered (the ISO list or currencies.custom).
Floats at the boundary
Replace (float) $request->input('price') with $request->money('price', 'EUR') — JSON 1.1 becomes 110 cents, not 111 — and validate with MoneyAmount::in('EUR') first.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.