The Exchange & Currencies facades
Money and Currency are plain value objects you create directly (see The Money object). The services around them sit behind two facades: Exchange for conversion and the stored rates table, Currencies for the currency registry.
Exchange
use RoundlyConsulting\Money\Currency;
use RoundlyConsulting\Money\Facades\Exchange;
Exchange::convert($money, 'USD'); // Money — default driver, latest rate
Exchange::convert($money, 'USD', now()->subDay()); // historical
Exchange::convertWithRate($money, 'CZK'); // Conversion: original, converted, rate
Exchange::rate(Currency::of('EUR'), Currency::of('USD')); // ExchangeRate
Exchange::driver('database')->rate(Currency::of('EUR'), Currency::of('USD'));
Exchange::getDefaultDriver(); // "ecb"
Exchange::extend('fixer', fn ($app) => new FixerProvider(/* ... */));
Exchange::source('ecb')->fetch($from, $to); // the raw, un-cached source| Exchange:: | Returns / does |
|---|---|
rate($from, $to, ?$on) | An ExchangeRate from the default driver (null = the latest). |
convert($money, $to, ?$on, ?$rounding) | The converted Money, rounded once (money.exchange.rounding). |
convertWithRate($money, $to, ?$on, ?$rounding) | A Conversion: original, converted and the rate used. |
driver(?$name) / provider(?$name) | A cached driver — config, database, ecb, chain or your own. |
getDefaultDriver() | money.exchange.default, else ecb. |
source($name) | The raw, un-cached driver the refresh pipeline fetches from. |
extend($driver, Closure $callback) | Register a host driver — cached automatically. |
forgetDrivers() | Drop the resolved (cached) driver instances. |
rates() | The RateStore that manages the stored rates table. |
fake($rates = [], $pivot = 'EUR') | Swap in static rates for tests — see Testing. |
Exchange::rates() — the stored rates table
rates() manages the money_exchange_rates table. Every write goes through one path, and a manual row is never overwritten by a refresh:
use RoundlyConsulting\Money\Enums\EcbFeed;
use RoundlyConsulting\Money\Facades\Exchange;
Exchange::rates()->refresh(); // RefreshResult: fetch the ECB daily feed now
Exchange::rates()->refresh(source: 'ecb', feed: EcbFeed::Recent, from: $from, to: $to);
Exchange::rates()->refreshLater(feed: EcbFeed::History); // queue it (unique per source + feed)
Exchange::rates()->manual('EUR', 'CZK', '25.10', $date); // a manual rate, today if undated
Exchange::rates()->store(...$rates); // ExchangeRate ...$rates
Exchange::rates()->prune(before: now()->subYear(), includeManual: false, pretend: true); // int| Exchange::rates()-> | Does | Returns |
|---|---|---|
refresh($source = 'ecb', $feed = EcbFeed::Daily, ?$from, ?$to) | Fetch a source into the table now; fires ExchangeRatesRefreshed, or ExchangeRatesRefreshFailed and rethrows. | RefreshResult |
refreshLater(same arguments) | Queue the same refresh — unique per source and feed for ten minutes. | void |
store(ExchangeRate ...$rates) | Upsert rates; a stored manual row is only ever overwritten by another manual rate. | RefreshResult |
manual($from, $to, $rate, ?$on) | One manual rate (1 from = rate × to), effective today in money.exchange.timezone unless dated. | RefreshResult |
prune($before, $includeManual = false, $pretend = false) | Delete — or with pretend, count — rows effective before a date; manual rows stay unless included. | int |
The money:rates:refresh and money:rates:prune commands call the same methods, so everything they do is recorded under Exchange::fake() too.
Currencies
Currencies proxies the currency registry singleton — lookups, registration and listings (details under Currencies):
| Currencies:: | Returns / does |
|---|---|
get($code) | The Currency — throws UnknownCurrency for an unknown code. |
find($code) | The Currency, or null. |
has($code) | Whether the code is registered. |
findByNumericCode($numeric) | Look up by ISO numeric code (978 or "978"), or null. |
register(Currency $currency, bool $replace = false) | Add a custom currency — in a service provider’s boot(). |
all() / iso() / custom() | list<Currency> — everything, the bundled ISO list, or your own. |
Currencies has no fake: the registry is in-memory and deterministic. Exchange::fake() is covered under Testing.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.