Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Money Bundle Laravel Package

headsnet/money-bundle

A Symfony bundle that integrates the moneyphp/money library, adding Doctrine types, form types, validators, Twig extensions and serialization support so your app can handle monetary values safely and consistently.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Require the package with composer require headsnet/money-bundle. As of v0.2.0 the minimum PHP version is 8.1 — if you are still on PHP 8.0 you must pin ^0.1.12.
  • The bundle is a thin Symfony wrapper around moneyphp/money. In Laravel you will not register the bundle itself; instead pull in moneyphp/money (a transitive dependency) and wire the two useful services yourself: MoneyFactory (create Money from user input/strings) and MoneyFormatter (render Money for display).
  • Where to look first: src/ for the factory/formatter classes, and vendor/moneyphp/money/src/Money.php for the arithmetic API you will actually call day-to-day.
  • First use case: converting a stored integer amount into a Money object and formatting it for an invoice or price display:
$money = MoneyFactory::create(1999, 'GBP'); // or from '19.99'
echo MoneyFormatter::format($money);         // "£19.99"
  • Verify your environment has ext-intl enabled (php -m | grep intl) — the formatter relies on it.

Implementation Patterns

  • Bind as singletons in a service provider so the factory/formatter respect a single app-wide default currency and locale:
$this->app->singleton(MoneyFormatter::class, fn ($app) => new MoneyFormatter(
    currency: config('money.default_currency', 'GBP'),
    locale: config('app.locale', 'en_GB'),
));
  • Eloquent cast is the highest-leverage pattern. Store amounts in minor units (integer column) and cast on the way out:
class MoneyCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes): ?Money
    {
        return $value === null ? null : Money::{$attributes['currency'] ?? 'GBP'} ((int) $value);
    }

    public function set($model, $key, $value, $attributes): array
    {
        return [$key => $value instanceof Money ? $value->getAmount() : (int) $value];
    }
}
  • Validation boundary: accept user input as a string ('19.99') and parse with the factory rather than doing float math. Never let a float reach Money — it will throw or silently mis-scale.
  • Arithmetic is immutable; every operation returns a new instance. Chain freely: $price->multiply($qty, RoundingMode::HALF_UP)->subtract($discount).
  • Allocation is the correct way to split totals (tax split, refunds, instalments): $total->allocate([1, 1, 1]) guarantees the parts sum back to the original, unlike naive division.
  • Comparison via equals(), greaterThan(), lessThan() — do not compare getAmount() integers across different currencies, it will throw InvalidArgumentException.
  • Serialization: Money is not JSON-serializable by default. Add a jsonSerialize() wrapper or an API Resource that emits ['amount' => ..., 'currency' => ...] explicitly.
  • Blade directive for formatting keeps views tidy: @money($invoice->total) registered via Blade::directive('money', ...).
  • Queue/console jobs need the formatter locale set explicitly if you override app.locale per request — locale is stateful inside intl.

Gotchas and Tips

  • v0.2.0 is a breaking change for old runtimes. PHP 8.1 is now the floor and 8.4 is supported. Update your CI matrix (8.1, 8.2, 8.3, 8.4) and any composer.json platform constraint in one go — otherwise composer update will silently hold you on 0.1.x.
  • The Symfony bundle machinery is dead weight in Laravel. Do not try to register Headsnet\MoneyBundle\...Bundle in config/app.php; extend Illuminate\Support\ServiceProvider and instantiate the services directly.
  • PHP 8.4 deprecation surface: implicit-nullable parameters (string $x = null) are deprecated in 8.4. If you have subclassed or wrapped formatter/factory signatures, make nullability explicit (?string $x = null) before upgrading.
  • Currency scale is not always 2. JPY has 0 decimals, KWD has 3. Never hardcode 100 as a divisor — use $money->getCurrency()->getSubunit().
  • Rounding mode is a required argument on multiply()/divide(). Omitting it (or defaulting to UNNECESSARY) throws on non-exact results, which surfaces randomly in tax calculations — always pass RoundingMode::HALF_UP or HALF_EVEN deliberately.
  • Formatter output is not stable across ICU versions. If you snapshot-test formatted strings, pin the expected value or assert on getAmount() instead; CI images with different ext-intl builds will produce different non-breaking-space characters.
  • Debugging tip: when a Money constructor throws, the message names the currency but not the caller — wrap creation in your factory with a try/catch that logs the raw input value; malformed user input is the usual culprit.
  • Extension point: the bundle's MoneyFactory is a static-ish convenience. If you need multi-currency conversion or custom currencies, inject a Currencies instance from moneyphp/money and construct Money yourself — that is the seam to build on.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules
loco/loco
starfederation/datastar-php
yupmin/modern-phystrix
stephenjude/extended-artisan-commands
lumphp/laravel-macroable
lumphp/laravel-collections
lumphp/laravel-contracts
sermepa/sermepa
arkecosystem/fortify