NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
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 callMoney for LaravelNote
Money::of($minor, 'EUR')Money::ofMinor($minor, 'EUR')There is deliberately no Money::of().
getMinorAmount() / getAmount() returning an intminor()A canonical string — use minorInt() only where an int is genuinely required.
getAmount() returning minor units as a stringminor()Not toDecimal().
(int) $row->priceMoney::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 zeroDiscount::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 100format($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 rateTaxRate::fromBasisPoints($bp)->netFromGross($gross)One exact rational, one rounding.
A hand-written minor-unit rescaleMinorUnits::rescale()Or MinorUnits::toDecimal().
Config ints such as PHP_ROUND_HALF_UPRoundingModes::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 crypto

By 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.