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

mathiasverraes/money

Small PHP money library that treats monetary values as immutable value objects to avoid floating-point errors. Includes currency support, arithmetic and comparisons, and formatting helpers—useful for modeling prices, totals, and discounts in a robust, domain-driven way.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**
   ```bash
   composer require mathiasverraes/money

Add to composer.json if using strict mode:

"config": {
    "preferred-install": "dist"
}
  1. First Use Case: Creating a Money Object

    use Mathiasverraes\Money\Money;
    
    $amount = Money::EUR(100); // 100 EUR
    $amount->getAmount(); // Returns 100 (integer)
    $amount->getCurrency(); // Returns "EUR"
    
    // New in v4.9.0: Zero currency shortcut
    $zero = Currency::zero(); // Returns a zero-amount Money object for the default currency
    $zeroEUR = Currency::EUR()->zero(); // Returns a zero-amount Money object for EUR
    
  2. Key Classes to Know

    • Money: Core class for monetary values.
    • Currency: Represents currencies (e.g., Currency::EUR() or new Currency::zero()).
    • MoneyFactory: For creating Money objects (e.g., Money::EUR(100)).
  3. Where to Look First

    • README (if repo is later found).
    • src/Money.php for core functionality.
    • src/Currency.php for currency logic (including new zero() method).

Implementation Patterns

Common Workflows

1. Basic Arithmetic

$price = Money::EUR(100);
$tax = Money::EUR(20);
$total = $price->add($tax); // 120 EUR
$discounted = $price->subtract(Money::EUR(10)); // 90 EUR

2. Currency Conversion

$usd = Money::USD(100);
$eur = $usd->convertTo(Currency::EUR()); // Uses default exchange rate
  • Note: Requires Mathiasverraes\Money\Currency\ExchangeRateProvider. Use a custom provider (e.g., API-based) for real-world rates.

3. Formatting for Display

$amount = Money::EUR(1234.56);
echo $amount->format(); // "€1,234.56"
echo $amount->format('%!'); // "€1,234.56" (with currency symbol)

4. Validation

use Mathiasverraes\Money\Exception\InvalidMoneyException;

try {
    $invalid = Money::EUR(-100); // Throws InvalidMoneyException
} catch (InvalidMoneyException $e) {
    // Handle error
}

// New in v4.9.0: Zero validation
$zero = Currency::EUR()->zero();
$this->assertEquals(0, $zero->getAmount());

5. Database Storage

// Store as two columns: amount (integer) and currency (string)
$amount = Money::EUR(100);
$storedAmount = $amount->getAmount(); // 100
$storedCurrency = $amount->getCurrency()->getCode(); // "EUR"

// Reconstruct on retrieval
$reconstructed = Money::of($storedAmount, $storedCurrency);

// Zero handling
$zero = Currency::zero(); // New shortcut
$zeroStoredAmount = $zero->getAmount(); // 0
$zeroStoredCurrency = $zero->getCurrency()->getCode(); // Default currency

6. Integration with Laravel

  • Request Validation:

    use Mathiasverraes\Money\Money;
    use Illuminate\Validation\Rule;
    
    $rules = [
        'price' => [
            'required',
            function ($attribute, $value, $fail) {
                try {
                    Money::of($value, 'EUR');
                } catch (\Exception $e) {
                    $fail('Invalid money format. Use `amount,currency` (e.g., `100,EUR`).');
                }
            },
        ],
    ];
    
  • Eloquent Casting:

    use Mathiasverraes\Money\Money;
    use Illuminate\Database\Eloquent\Casts\Attribute;
    
    public function price(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => Money::of($value['amount'], $value['currency']),
            set: fn ($value) => [
                'amount' => $value->getAmount(),
                'currency' => $value->getCurrency()->getCode(),
            ],
        );
    }
    

    // Zero handling in casting public function zeroPrice(): Attribute { return Attribute::make( get: fn ($value) => $value ? Money::of($value['amount'], $value['currency']) : Currency::zero(), set: fn ($value) => $value ? [ 'amount' => $value->getAmount(), 'currency' => $value->getCurrency()->getCode(), ] : null, ); }

    
    

7. Testing

use Mathiasverraes\Money\Money;
use PHPUnit\Framework\TestCase;

class MoneyTest extends TestCase
{
    public function testAddition()
    {
        $this->assertEquals(
            Money::EUR(120),
            Money::EUR(100)->add(Money::EUR(20))
        );
    }

    public function testZeroCurrency()
    {
        $zero = Currency::EUR()->zero();
        $this->assertEquals(0, $zero->getAmount());
        $this->assertEquals('EUR', $zero->getCurrency()->getCode());
    }
}

Gotchas and Tips

Pitfalls

  1. Floating-Point Precision

    • Avoid floating-point arithmetic. Use integers (e.g., Money::EUR(100) for €1.00, not Money::EUR(1.00)).
    • For cents, multiply by 100 (e.g., Money::EUR(100) = €1.00).
  2. Currency Code Case Sensitivity

    • Currency codes (e.g., EUR, USD) are case-sensitive. Always use uppercase.
  3. Exchange Rates

    • The package does not include exchange rates by default. You must implement ExchangeRateProvider:
      use Mathiasverraes\Money\Currency\ExchangeRateProvider;
      
      class ApiExchangeRateProvider implements ExchangeRateProvider
      {
          public function getRate(string $from, string $to): float
          {
              // Fetch from API (e.g., European Central Bank)
              return 1.1; // Example: 1 EUR = 1.1 USD
          }
      }
      
    • Register the provider in your app (e.g., Laravel service provider):
      $this->app->singleton(ExchangeRateProvider::class, function () {
          return new ApiExchangeRateProvider();
      });
      
  4. Negative Amounts

    • By default, Money objects cannot have negative amounts. Override if needed:
      $negative = Money::of(-100, 'EUR'); // Throws InvalidMoneyException
      $negative = Money::of(-100, 'EUR', true); // Allows negative (3rd param: $allowNegative)
      
  5. Serialization

    • Money objects are not JSON-serializable by default. Use:
      $json = json_encode([
          'amount' => $amount->getAmount(),
          'currency' => $amount->getCurrency()->getCode(),
      ]);
      
    • Or implement JsonSerializable:
      class SerializableMoney implements JsonSerializable
      {
          public function jsonSerialize(): array
          {
              return [
                  'amount' => $this->getAmount(),
                  'currency' => $this->getCurrency()->getCode(),
              ];
          }
      }
      
  6. Laravel Caching

    • If using Money::of() frequently, cache Currency objects:
      $eur = app(Currency::class)->get('EUR'); // Reuse instance
      $zeroEUR = $eur->zero(); // New zero shortcut
      

Tips

  1. Use Static Factory Methods Prefer Money::EUR(100) or Currency::EUR()->zero() (new) over new Money(100, Currency::EUR()) for readability.

  2. Custom Rounding Override rounding behavior for specific use cases:

    $amount = Money::of(1234, 'EUR', 0, RoundingMode::UP); // Rounds up
    
  3. Localization Use NumberFormatter for locale-aware formatting:

    use NumberFormatter;
    
    $formatter = new NumberFormatter('de_DE', NumberFormatter::CURRENCY);
    echo $amount->format($formatter); // "1.2
    
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor