Weave Code
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 Laravel Package

brick/money

Immutable money & currency library for PHP with exact arithmetic, explicit rounding control, and support for any-sized amounts. Built on brick/math to avoid floating-point errors; works with ISO currencies and integrates well with GMP/BCMath for speed.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require brick/money
    

    Ensure PHP 8.2+ is used (or downgrade for older versions if needed).

  2. First Use Case: Create a Money object from a numeric value and currency code:

    use Brick\Money\Money;
    
    $price = Money::of(19.99, 'USD'); // USD 19.99
    
  3. Key Classes to Know:

    • Money: Immutable money object.
    • MoneyBag: Container for mixed-currency amounts.
    • Currency: Represents currencies (e.g., Currency::of('USD')).
    • Context\*: Customize rounding (e.g., CashContext, AutoContext).
  4. Where to Look First:


Implementation Patterns

Core Workflows

  1. Immutable Operations: Always return new Money instances for operations (e.g., plus(), minus()).

    $total = $subtotal->plus($tax);
    
  2. Currency Safety: Validate currencies before operations to avoid CurrencyMismatchException:

    if ($order->currency !== $payment->currency) {
        throw new \InvalidArgumentException("Currency mismatch");
    }
    
  3. Rounding Modes: Pass RoundingMode (e.g., RoundingMode::Up) for precision-critical operations:

    $rounded = $amount->dividedBy(3, RoundingMode::Down);
    
  4. Contexts for Special Cases:

    • Use CashContext for currencies with cash rounding rules (e.g., CHF):
      $chf = Money::of(10, 'CHF', new CashContext(step: 5));
      
    • Use AutoContext for dynamic precision:
      $dynamic = Money::of(1.1, 'USD', new AutoContext());
      
  5. MoneyBag for Mixed Currencies: Aggregate amounts across currencies, then convert to a target currency:

    $bag = new MoneyBag([$eur, $jpy]);
    $totalUsd = $bag->getTotal('USD'); // Converts EUR/JPY to USD
    
  6. Allocation for Splitting: Distribute amounts proportionally (e.g., revenue splits):

    [$share1, $share2] = $profit->allocate([60, 40], AllocationMode::FloorToFirst);
    

Integration Tips

  • Database Storage: Store Money as minor units (e.g., cents) in a decimal column:
    $minor = $money->getAmount()->toScale(2)->toString(); // "1999" for USD 19.99
    
  • API Requests/Responses: Serialize as JSON with currency and amount:
    $data = [
        'amount' => $money->getAmount()->toString(),
        'currency' => $money->getCurrency()->getCode(),
    ];
    
  • Testing: Use RationalMoney for exact arithmetic in tests:
    $expected = Money::of(1, 'USD')->toRational()->dividedBy(3);
    $actual = $money->dividedBy(3);
    $this->assertTrue($expected->isEqualTo($actual->toRational()));
    

Gotchas and Tips

Pitfalls

  1. Rounding Mode Scope:

    • Rounding modes apply only to the operation they’re passed to, not persistently.
    • Example: Creating a Money with RoundingMode::Up doesn’t affect later operations.
  2. Currency Updates:

    • ISO 4217 updates (e.g., new currencies) may break code using Currency::of().
    • Mitigation: Pin to a specific brick/money version (e.g., 0.13.*) or handle exceptions:
      try {
          $currency = Currency::of('XBD'); // New currency
      } catch (UnknownCurrencyException $e) {
          // Fallback logic
      }
      
  3. Floating-Point Assumptions:

    • Avoid converting Money to float/double for storage/calculation—use getAmount()->toString() or minor units.
  4. AutoContext Limitations:

    • AutoContext throws exceptions for infinite decimals (e.g., 1/3).
    • Workaround: Use RationalMoney for intermediate steps.
  5. MoneyBag Precision:

    • Currency conversion in MoneyBag uses exchange rates from Brick\Money\ExchangeRateProvider.
    • Tip: Mock the provider in tests to control rates.
  6. Thread Safety:

    • Money is immutable, but MoneyBag or shared ExchangeRateProvider instances may need synchronization in concurrent environments.

Debugging Tips

  1. Unexpected Rounding:

    • Check if the operation requires rounding (e.g., dividedBy(3) for USD 1.00 → 0.33).
    • Fix: Pass RoundingMode::Up/Down explicitly.
  2. Currency Mismatch:

    • Verify currencies match before operations (e.g., assertSame($a->getCurrency(), $b->getCurrency())).
  3. Performance:

    • Enable GMP/BCMath extensions for faster arithmetic:
      // In php.ini
      extension=gmp
      extension=bcmath
      
  4. Serialization:

    • Use Money::parse() to reconstruct objects from strings:
      $money = Money::parse('USD 19.99');
      

Extension Points

  1. Custom Contexts: Extend Context for domain-specific rules (e.g., TaxContext):

    class TaxContext extends Context {
        public function __construct(public int $taxRate) {}
        public function round(Money $money): Money {
            return $money->multipliedBy(1 + $this->taxRate / 100);
        }
    }
    
  2. Exchange Rate Providers: Implement ExchangeRateProvider for custom rates:

    class ApiExchangeRateProvider implements ExchangeRateProvider {
        public function getRate(Currency $from, Currency $to): string {
            return $this->fetchFromApi($from->getCode(), $to->getCode());
        }
    }
    
  3. MoneyBag Aggregation: Override MoneyBag::getTotal() to apply business logic (e.g., discounts):

    class DiscountedMoneyBag extends MoneyBag {
        public function getTotal(Currency $currency): Money {
            $total = parent::getTotal($currency);
            return $total->minus($this->calculateDiscount($total));
        }
    }
    
  4. Rounding Strategies: Create custom RoundingMode implementations for specific rounding rules:

    class BankersRoundingMode implements RoundingMode {
        public function round(Money $money, int $scale): Money {
            // Custom logic (e.g., round to even)
        }
    }
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4