joseftraxler / laravel-money
Lightweight money and currency utilities for Laravel applications.
Requires
- php: >=8.4
- ext-intl: *
- illuminate/contracts: >=12
- illuminate/database: >=12
- illuminate/support: >=12
- moneyphp/money: ^4.8
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- pestphp/pest: ^4.6
- phpstan/phpstan: ^2.1
Conflicts
- illuminate/database: <12
- dev-main
- v1.1.0
- v1.0.1
- v1.0.0
- v1.0.0-rc.1
- v1.0.0-beta.1
- v1.0.0-alpha.14
- v1.0.0-alpha.13
- v1.0.0-alpha.12
- v1.0.0-alpha.11
- v1.0.0-alpha.10
- v1.0.0-alpha.9
- v1.0.0-alpha.8
- v1.0.0-alpha.7
- v1.0.0-alpha.6
- v1.0.0-alpha.5
- v1.0.0-alpha.4
- v1.0.0-alpha.3
- v1.0.0-alpha.2
- v1.0.0-alpha.1
- dev-bugfix/negative-zero-starting-money
- dev-v1.x-rc
This package is auto-updated.
Last update: 2026-05-13 09:13:36 UTC
README
A lightweight Laravel package for working with money values, currencies, formatting, arithmetic operations, and Eloquent casts.
Laravel Money provides a small immutable Money value object with currency-aware precision handling and convenient integration for Laravel applications.
Features
- Immutable
Moneyvalue object - ISO 4217-style currency code validation
- Currency-specific precision support
- Decimal and cents-based factory methods
- Arithmetic operations powered by
moneyphp/money - Human-readable formatting using PHP
intl - Eloquent casts for decimal and cents database columns
- Support for fixed currency and currency-column casts
- Laravel auto-discovery
- Publishable configuration
Requirements
- PHP 8.4+
- Laravel 12+
- PHP
intlextension
Installation
Install the package via Composer:
composer require joseftraxler/laravel-money
The service provider is registered automatically through Laravel package discovery.
Configuration
The package works without publishing configuration. If you want to customize default currency or precision settings, publish the config file:
php artisan vendor:publish --tag=money-config
This publishes: config/money.php
Example configuration:
return [ 'default_currency' => 'USD', // default currency used by casts when no currency is provided 'default_precision' => null, // null = automatically detected 'currencies' => [ 'JPY' => ['precision' => 0], 'BHD' => ['precision' => 3], ], ];
Basic usage
use JosefTraxler\LaravelMoney\Money; $money = Money::EUR('10.45'); $money->getDecimal(); // "10.45" $money->getCents(); // "1045" $money->getCurrency()->getCode(); // "EUR" $twoPieces = $money->multiply(2)->getDecimal(); // "20.90" $product->price = $money; $product->save(); $product->price->toHumanReadable(locale: 'en_US'); // e.g.: "€10.45"
In Blade:
{{ $product->price }}
Currency codes are strict ISO 4217-style codes. They must contain exactly three letters.
Currency precision
Precision is resolved in this order:
money.currencies.{CODE}.precisionmoney.default_precision- package-detected default precision
Precision values are validated and cached per currency for performance.
⚠️ When a decimal value contains more fractional digits than the currency precision allows, the value is truncated to the configured precision.
Creating money values
From decimal
use JosefTraxler\LaravelMoney\Currency; use JosefTraxler\LaravelMoney\Money; new Money('123.45', 'EUR'); Money::fromDecimal('123.45', 'EUR'); Money::EUR('123.45'); Money::fromDecimal('123.45', Currency::of('EUR'));
From cents
Money::fromCents(12345, 'EUR'); // 123.45 EUR
Numeric strings are recommended
The package accepts integers, floats, and numeric strings for convenience:
Money::fromDecimal(10, 'EUR'); Money::fromDecimal(10.50, 'EUR'); Money::fromDecimal('10.50', 'EUR'); // recommended
For exact monetary input, prefer numeric strings:
Money::fromDecimal('10.50', 'EUR');
Using custom currency class/enum
You can also use a custom currency class/enum that implements JosefTraxler\LaravelMoney\Contracts\Currencyable:
enum MyCurrency: string implements JosefTraxler\LaravelMoney\Contracts\Currencyable { case EUR = 'EUR'; case USD = 'USD'; public function getCode(): string { return $this->value; } } Money::fromDecimal('123.45', MyCurrency::EUR);
or some model e.g.:
class Currency extends Model implements JosefTraxler\LaravelMoney\Contracts\Currencyable { public function getCode(): string { return $this->code; } } $currency = new Currency(['code' => 'EUR']); Money::fromDecimal('123.45', $currency);
Arithmetic
Money values support common arithmetic operations.
$ten = Money::EUR('10.00'); $fiveFifty = Money::EUR('5.50'); $ten->add($fiveFifty)->getDecimal(); // "15.50" $ten->subtract($fiveFifty)->getDecimal(); // "4.50" $ten->multiply(2)->getDecimal(); // "20.00" $ten->divide(2)->getDecimal(); // "5.00"
Arithmetic operations are currency-aware and delegated to moneyphp/money.
Comparisons
$ten = Money::EUR('10.00'); $twenty = Money::EUR('20.00'); $ten->equals($twenty); // false $ten->lessThan($twenty); // true $twenty->greaterThan($ten); // true $ten->compare($twenty); // -1
Predicates are also available:
Money::EUR('10.00')->isPositive(); // true Money::EUR('-10.00')->isNegative(); // true Money::EUR('0.00')->isZero(); // true
Formatting
In Laravel Blade, money values are automatically formatted according to the current locale.
For example, in en_US, this may render as €12.35:
{{ Money::EUR('12.35') }}
Formatting uses PHP NumberFormatter, so the intl extension is required.
You can specify a custom locale:
{{ Money::EUR('12.34')->toHumanReadable(locale: 'cs_CZ') }}
For cs_CZ, this may render as 12,34 €.
Eloquent casts
Laravel Money provides casts for storing money values as decimal or cents.
Cast definitions are specified by:
- currency code source
- fixed: defined by a three-letter uppercase currency code
- dynamic: defined by a currency column name
- amount source
- decimal: amount in major units
- cents: amount in minor units
Fixed currency decimal cast
Use this when the database column stores only the decimal amount and the currency is fixed:
use Illuminate\Database\Eloquent\Model; use JosefTraxler\LaravelMoney\Money; class Product extends Model { protected function casts(): array { return [ 'price' => Money::class . ':EUR', ]; } }
Usage:
$product = Product::find(1); $product->price->getDecimal(); // "19.99" $product->price->getCurrency()->getCode(); // "EUR"
⚠️ When using a fixed currency cast, assigning a Money instance with a different currency throws MoneyMismatchCurrencyException.
Dynamic currency column
Use this when the amount and currency are stored in separate columns:
use Illuminate\Database\Eloquent\Model; use JosefTraxler\LaravelMoney\Money; class Order extends Model { protected function casts(): array { return [ 'price' => Money::class . ':currency', 'currency' => 'string', ]; } }
Usage:
$order = new Order(); $order->price = Money::USD('19.99'); $order->price->getDecimal(); // "19.99" $order->price->getCurrency()->getCode(); // "USD" $order->currency; // "USD"
When using a dynamic currency column, the package stores the currency code automatically.
When assigning null to a money attribute using a dynamic currency column, only the amount column is set to null.
The currency column is preserved intentionally, because the same currency column may be shared by multiple money attributes.
Cents storage
If you store money as cents, pass cents as the second-cast argument:
use Illuminate\Database\Eloquent\Model; use JosefTraxler\LaravelMoney\Money; class Payment extends Model { protected function casts(): array { return [ 'amount_in_cents' => Money::class . ':currency,cents', 'currency' => 'string', ]; } }
The example below stores 1999 in amount_in_cents and "EUR" in currency:
$payment = new Payment(); $payment->amount_in_cents = Money::EUR('19.99'); $payment->save();
When retrieving the value, the package automatically converts cents to Money using Money::fromCents().
Design notes
Strict currency codes
Currency codes are validated as three-letter ISO 4217-style codes. Whitespace is not trimmed automatically. This is intentional: invalid or unnormalized input should be handled before creating a currency value.
Precision caching
Currency precision is resolved from configuration and cached per currency code. This keeps the package efficient when many money objects are created during a request.
Decimal normalization
Money amounts are normalized according to currency precision:
Money::EUR('10')->getDecimal(); // "10.00" Money::JPY('10')->getDecimal(); // "10"
Testing
Run the test suite:
composer test
Run static analysis:
composer stan
Check code style:
composer cs
Fix code style:
composer cs-fix
Contributing
Contributions are welcome.
Before opening a pull request, please ensure:
- tests pass:
composer test - code style passes:
composer cs - static analysis passes:
composer stan
License
MIT License © Josef Traxler
See the LICENSE file for details.
Support
- Issues: GitHub Issues
- Source: GitHub Repository