webrek/laravel-money
A Laravel package for working with money and currencies in your application. It offers convenient helpers and integrations for formatting, parsing, and managing monetary values safely, helping keep currency handling consistent across models, views, and business logic.
Install via Composer and publish any config if the package ships it:
composer require vendor/package
php artisan vendor:publish --tag=money-config # only if a config file is published
Look first at the package's Money facade/static class and any MoneyCast or Blade directive it exposes — those are the entry points you'll touch daily. The quickest first use case is summing a column of prices in a report:
use Vendor\Money\Money;
$total = Money::sum($order->items->map->price);
echo $total->format(); // "£1,234.50"
For Collection-heavy code, register the macro as noted in the package docs (typically a service provider or the package's own provider) and replace ad-hoc ->sum() calls:
$orders->sumMoney('total'); // total across all orders
$orders->sumMoney(); // total across the collection itself
// returns null for an empty collection instead of 0.00
Aggregate in the model layer, format in the view. Keep Money objects on your DTOs/models and only call format() at the presentation boundary. Money::sum() and the sumMoney() macro make this natural — they accept iterables of Money, so you never fall back to floats.
Tax and discount calculators. Money::percentage($rate) replaces hand-rolled $price * 1.2 math. Encapsulate rules in a small service so rounding behaviour is centralised:
class TaxCalculator
{
public function gross(Money $net, float $rate): Money
{
return $net->plus($net->percentage($rate));
}
}
Range checks with min/max. Use Money::min() / Money::max() for tier pricing, shipping bands, or "cheapest supplier" logic rather than sorting a collection and grabbing first().
Empty-collection handling. sumMoney() returning null for an empty collection is a deliberate signal: branch on it early ($total ?? Money::zero('GBP')) so you don't accidentally render an empty string in a Blade view. A @money-style helper or a small accessor on your model keeps templates clean.
Reporting pipelines. Chain the new helpers into existing query scopes — e.g. a StatsService that returns ['min' => ..., 'max' => ..., 'avg' => ...] — and inject the currency once rather than repeating it at every call site.
Integration with casts. If you already store amounts as integer minor units, pair the cast with these helpers; sum/min/max operate on hydrated Money instances, so as long as the cast is in place, aggregates stay currency-safe.
Money::sum() and friends require a homogeneous currency; mixing GBP and USD will throw. Normalise/converted amounts up front or group by currency.percentage() applies rounding at some defined point — verify whether tax is computed on the net and rounded once, or per line item, to match your accounting expectations.sumMoney() → null is easily missed in strict typing; add a return-type guard or a fallback so downstream ->format() calls don't fatal.percentage() takes a percentage (not a fraction) — a classic off-by-100 bug. Add a test asserting 1000->percentage(20) equals 200.Collection macro isn't available in a queue worker or a console command, check that the provider isn't deferred and registers the macro eagerly.App\Support\Money helpers so swapping the underlying implementation later requires changing one file, not hundreds of call sites.How can I help you explore Laravel packages today?