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

moneyphp/money

moneyphp/money is a PHP value-object library for safe money handling without floats. Uses string-based big integers, supports arithmetic, allocation, currencies/ISO repositories, formatting (incl. intl), JSON serialization, and exchange rates. Requires BCMath.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require moneyphp/money
    

    Ensure bcmath extension is enabled in php.ini.

  2. First Use Case: Create a Money object with a currency and amount (in smallest unit, e.g., cents):

    use Money\Money;
    use Money\Currency;
    
    $eur = Money::EUR(500); // 5.00 EUR (500 cents)
    $usd = Money::USD(100); // 1.00 USD
    
  3. Key Classes to Know:

    • Money: Immutable value object for monetary amounts.
    • Currency: Represents currencies (e.g., Currency::EUR).
    • MoneyFactory: Static factory for creating Money objects.
    • Converter: Handles currency conversion (requires moneyphp/currency for exchange rates).

Implementation Patterns

Core Workflows

  1. Basic Arithmetic:

    $total = $eur->add($usd); // Adds two Money objects (requires same currency)
    $difference = $eur->subtract(Money::EUR(200));
    $allocated = $eur->allocate([1, 2]); // Splits money proportionally
    
  2. Currency Conversion:

    $converter = new \Money\Converter(new \Money\Currency\ISOCurrencies());
    $converter->addCurrencyPair(new \Money\Currency\CurrencyPair('EUR', 'USD', 1.1));
    $condollar = $converter->convert($eur, 'USD'); // Converts EUR to USD
    
  3. Formatting:

    use Money\Formatter\DecimalMoneyFormatter;
    $formatter = new DecimalMoneyFormatter();
    echo $formatter->format($eur); // "5.00"
    
  4. Serialization:

    $json = json_encode($eur); // '{"amount":500,"currency":"EUR"}'
    $decoded = json_decode($json, true);
    $restored = Money::fromArray($decoded);
    

Integration Tips

  • Laravel Models: Use accessors/mutators to convert between Money and database storage (e.g., store as cents):

    public function getPriceAttribute($value) {
        return Money::USD($value * 100);
    }
    
  • Validation: Use Money in Laravel’s FormRequest validation:

    $this->validate($request, [
        'price' => 'required|numeric|min:0',
    ]);
    $money = Money::USD((int)($request->price * 100));
    
  • Aggregations: Use Money::min(), Money::max(), or Money::sum() for collections:

    $prices = collect([Money::EUR(100), Money::EUR(200)]);
    $total = $prices->sum();
    
  • Testing: Use Money\Comparator for assertions:

    $this->assertTrue(Money::Comparator::equals($eur, Money::EUR(500)));
    

Gotchas and Tips

Pitfalls

  1. Floating-Point Traps:

    • Never use floats for monetary values. Always pass integers (e.g., cents) or numeric strings.
    • Example of wrong usage:
      $bad = Money::USD(1.99); // ❌ Avoid floats!
      
    • Correct usage:
      $good = Money::USD(199); // ✅ Use integers (cents)
      
  2. Currency Codes:

    • Must be uppercase (e.g., EUR, not eur). The library throws InvalidArgumentException otherwise.
  3. Exchange Rates:

    • Requires moneyphp/currency for dynamic rates. For testing, use FixedExchange:
      $exchange = new \Money\Exchange\FixedExchange([new \Money\Currency\CurrencyPair('EUR', 'USD', '1.1')]);
      
  4. BCMath/GMP Dependencies:

    • If bcmath is unavailable, fall back to gmp or plain PHP (slower). Configure via Money\Calculator\Calculator:
      $calculator = new \Money\Calculator\GmpCalculator();
      
  5. Allocation Edge Cases:

    • allocate() may not distribute the full amount if ratios sum to zero:
      $money->allocate([0, 0]); // Returns [Money::zero(), Money::zero()]
      
  6. Negative Values:

    • Supported, but ensure business logic handles them (e.g., debt vs. credit).

Debugging Tips

  1. Assertions: Use Money\Comparator for precise comparisons in tests:

    $this->assertTrue(Money::Comparator::equals($money1, $money2));
    
  2. Serialization Issues:

    • If JSON serialization fails, ensure amount is a numeric string or integer:
      $money->jsonSerialize(); // Returns ['amount' => '500', 'currency' => 'EUR']
      
  3. Performance:

    • For large-scale operations (e.g., batch conversions), cache CurrencyPair objects:
      $cache = new \Psr\SimpleCache\CacheItemPool();
      $converter->setCache($cache);
      

Extension Points

  1. Custom Calculators: Implement \Money\Calculator\CalculatorInterface for custom arithmetic (e.g., custom rounding):

    class CustomCalculator implements CalculatorInterface {
        public function add(string $augend, string $addend): string { ... }
        // Implement other methods...
    }
    
  2. Custom Currencies: Extend \Money\Currency\Currency for domain-specific currencies (e.g., "LOYALTY_POINTS"):

    class LoyaltyPoints extends Currency {
        public static function LOYALTY(): self { return new self('LPT'); }
    }
    
  3. Parsers/Formatters: Create custom parsers (e.g., for legacy formats) by implementing \Money\Parser\ParserInterface:

    class LegacyParser implements ParserInterface {
        public function parse(string $amount, Currency $currency): Money { ... }
    }
    
  4. Exchange Strategies: Extend \Money\Exchange\ExchangeInterface for custom exchange logic (e.g., API-based rates):

    class ApiExchange implements ExchangeInterface {
        public function getRate(CurrencyPair $currencyPair): string { ... }
    }
    

Laravel-Specific Quirks

  1. Database Storage: Store Money as cents in a bigint column to avoid precision loss:

    // Migration
    Schema::create('orders', function (Blueprint $table) {
        $table->bigInteger('amount_cents')->unsigned();
        $table->string('currency');
    });
    
  2. Eloquent Accessors: Use accessors to convert between Money and database values:

    public function getPriceAttribute($value) {
        return Money::USD($value);
    }
    
    public function setPriceAttribute($money, $value) {
        $this->attributes['price'] = $money->getAmount();
    }
    
  3. API Responses: Format Money in API responses using Money\Formatter\IntlMoneyFormatter:

    use Money\Formatter\IntlMoneyFormatter;
    $formatter = new IntlMoneyFormatter('en_US');
    return response()->json(['price' => $formatter->format($money)]);
    
  4. Caching Exchange Rates: Cache CurrencyPair objects in Laravel’s cache:

    $cacheKey = "exchange_rate_{$from}_{$to}";
    $rate = Cache::remember($cacheKey, now()->addHours(1), function () use ($from, $to) {
        return $converter->getRate(new CurrencyPair($from, $to));
    });
    
  5. Testing: Use Laravel’s RefreshDatabase with Money seed data:

    public function setUp(): void {
        parent::setUp();
        $this->seed(MoneySeeder::class);
    }
    

    Example seeder:

    class MoneySeeder implements Seeder {
        public function run() {
            DB::table('products')->insert([
                ['price_cents' => 1000, 'currency' => 'EUR'],
            ]);
        }
    }
    
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.
codraw/entity-migrator
codraw/doctrine-extra
codraw/aws-tool-kit
codraw/validator
codraw/workflow
codraw/open-api
codraw/cron-job
codraw/process
codraw/log
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony