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

Brick\Money is a PHP library for precise, immutable money and currency values. It provides exact arithmetic (no float errors), explicit rounding control, and supports large amounts via brick/math, with optional GMP/BCMath acceleration.

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:

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

    • Money: Core class for immutable monetary values.
    • MoneyBag: For aggregating multiple currencies.
    • RoundingMode: For handling precision (e.g., RoundingMode::Up).
    • Context\*: Customize behavior (e.g., CashContext for cash rounding).
  4. Where to Look First:


Implementation Patterns

Core Workflows

  1. Immutable Operations: All methods return new Money instances. Avoid modifying state:

    $total = $subtotal->plus($tax)->minus($discount);
    
  2. Currency Safety: Validate currencies early to avoid runtime errors:

    try {
        $total = $usd->plus($eur); // Throws CurrencyMismatchException
    } catch (CurrencyMismatchException $e) {
        // Handle or log
    }
    
  3. Rounding Strategies: Pass RoundingMode explicitly for operations requiring precision:

    $rounded = $money->dividedBy(3, RoundingMode::HalfUp);
    
  4. Context-Driven Logic: Use contexts for domain-specific rules (e.g., cash rounding for CHF):

    $chf = Money::of(10, 'CHF', new CashContext(step: 5));
    

Integration Tips

  1. Laravel-Specific:

    • Service Provider: Bind Money to the container for dependency injection:
      $this->app->bind(Money::class, function () {
          return Money::of(0, 'USD'); // Default zero-money
      });
      
    • Request Validation: Use brick/money in Form Requests:
      use Brick\Money\Money;
      use Brick\Money\Exception\CurrencyMismatchException;
      
      public function rules()
      {
          return [
              'price' => ['required', function ($attribute, $value, $fail) {
                  try {
                      $money = Money::of($value, 'USD');
                  } catch (Exception $e) {
                      $fail('Invalid price format.');
                  }
              }],
          ];
      }
      
  2. Database Storage: Store minor units (cents) in integers to avoid floating-point issues:

    $money = Money::ofMinor(1234, 'USD'); // Store 1234 in DB
    $retrieved = Money::ofMinor($storedValue, 'USD');
    
  3. API Responses: Serialize Money to JSON with currency:

    $money->getAmount()->toString(); // "19.99"
    $money->getCurrency()->getSymbol(); // "$"
    
  4. Testing: Use Money::ofMinor() for predictable test data:

    $this->assertEquals(
        Money::ofMinor(100, 'USD'),
        Money::of(1, 'USD')->multipliedBy(100)
    );
    

Gotchas and Tips

Pitfalls

  1. Rounding Mode Scope:

    • Rounding modes apply only to the operation, not persistently. Reapply for each operation:
      // Wrong: Assumes rounding persists
      $money->plus('0.999', RoundingMode::Up)->minus('0.001'); // May fail
      
      // Correct: Explicit rounding per operation
      $rounded = $money->plus('0.999', RoundingMode::Up);
      
  2. Currency Updates:

    • ISO 4217 updates may break code using alpha codes (e.g., EUR). Pin to a minor version (e.g., 0.13.*) to avoid surprises:
      composer require brick/money:^0.13
      
  3. Floating-Point Assumptions:

    • Avoid converting Money to float/double for calculations. Use getAmount() (returns Number) or toRational() for exact arithmetic.
  4. Context Inheritance:

    • Operations preserve the original Context. To change it, explicitly pass a new context:
      $money->toContext(new CustomContext(scale: 4));
      
  5. Negative Values:

    • Money supports negatives, but some methods (e.g., allocate()) may behave unexpectedly. Test edge cases:
      $negative = Money::of(-100, 'USD');
      $negative->isNegative(); // true
      

Debugging Tips

  1. Precision Issues:

    • Use toRational() for complex calculations to avoid intermediate rounding:
      $result = $money->toRational()
          ->dividedBy(7)
          ->toContext($money->getContext(), RoundingMode::Down);
      
  2. CurrencyMismatchException:

    • Check for mixed currencies in MoneyBag or operations. Use isSameValueAs() for currency-agnostic comparisons.
  3. RoundingNecessaryException:

    • Ensure all operations specify a RoundingMode when precision is required. Default to RoundingMode::HalfUp for financial rounding.
  4. Performance:

    • Enable GMP or BCMath extensions for large calculations:
      sudo apt-get install php-gmp php-bcmath
      sudo systemctl restart apache2
      

Extension Points

  1. Custom Rounding:

    • Extend RoundingMode or implement RoundingBehavior for domain-specific rules:
      use Brick\Math\RoundingMode;
      
      class FinancialRoundingMode extends RoundingMode {
          public static function create(): self { /* ... */ }
      }
      
  2. New Contexts:

    • Create custom contexts by extending Context:
      use Brick\Money\Context;
      
      class TaxContext extends Context {
          public function __construct(public int $taxRate) {}
          // Override methods as needed
      }
      
  3. MoneyBag Extensions:

    • Add custom aggregation logic by extending MoneyBag:
      class TaxedMoneyBag extends MoneyBag {
          public function getTotalWithTax(int $taxRate): Money {
              return $this->getTotal()->multipliedBy(1 + $taxRate / 100);
          }
      }
      
  4. Currency Providers:

    • Replace the default IsoCurrencyProvider for custom currency logic:
      use Brick\Money\Currency\CurrencyProvider;
      
      $provider = new CustomCurrencyProvider();
      $currency = $provider->getCurrency('XYZ');
      
  5. Laravel Helpers:

    • Create a facade for common operations:
      // app/Facades/MoneyFacade.php
      namespace App\Facades;
      
      use Brick\Money\Money;
      use Illuminate\Support\Facades\Facade;
      
      class MoneyFacade extends Facade {
          protected static function getFacadeAccessor() { return 'money'; }
      }
      
      // app/Providers/AppServiceProvider.php
      public function register() {
          $this->app->singleton('money', function () {
              return new class {
                  public function usd(float $amount): Money {
                      return Money::of($amount, 'USD');
                  }
              };
          });
      }
      
      Usage:
      use App\Facades\Money;
      
      $price = Money::usd(19.99);
      
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai