Upgrading
Changes that can affect existing code. The full list is in the CHANGELOG.
After an upgrade, compare your published config/currency.php with the package’s — new keys fall back to package defaults, but a published file never gets them written in:
php artisan vendor:publish --tag=currency-config --force # overwrites; back up your file firstconvertAt(), getRateAt() and getRatesAt() of Currency accept a string date. A subclass of Currency that overrides one of them must widen $date to \DateTimeInterface|string.
Providers outside the package (your own, or subclasses of built-in ones) are cached under their full class name: currency_rates_App_Currency_MyProvider instead of currency_rates_MyProvider. Their rates, fallback copy and historical rates are fetched again once after the upgrade; if the API is down right then, there is no fallback copy yet. Built-in providers keep their keys.
- Historical rates are cached under new keys (
currency_rates_{ProviderClass}_{generation}_{Y-m-d}), so every date is fetched once more after the upgrade. The oldcurrency_rates_{ProviderClass}_{Y-m-d}keys are no longer read; on a store without eviction remove them by hand. clearCache()andcurrency:rates --refreshnow drop historical rates too.- An empty
CURRENCY_CACHE_TTL_HISTORICAL=caches forever, asnulldoes. Before, it disabled the historical cache.
exchangeratesapi, currencyapi and fixer stored rates in the opposite direction (1 USD = 0.024 UAH), so convert(100, 'USD', 'UAH') returned 2.4 instead of 4150. They now store “UAH per 1 unit of the currency”, like the other providers.
- Inverted rates already in the cache are served until they expire — the current ones for
cache_ttl, the fallback copy forcache_ttl_fallback, and historical ones forever (defaultcache_ttl_historical = null). After deploying, remove the keys starting withcurrency_rates_ExchangeRatesApiProvider,currency_rates_CurrencyApiProvider,currency_rates_FixerProvider, or runphp artisan cache:clearif the store holds nothing else valuable.currency:rates --refreshis not enough: it does not touch per-date keys. Since 2.7.4 historical rates use new keys, so after upgrading to 2.7.4 or later the old inverted per-date keys are no longer read and need no cleanup. - If you worked around the bug with a subclass that inverts the rates, remove it, otherwise the rates get inverted twice.
- New historical rates API (
convertAt(),getRateAt(),getRatesAt(),supportsHistoricalRates(),currency_convert_at()),--date=on both commands,cache_ttl_historicalconfig key. Nothing to change unless you have custom providers:- to support historical rates, a provider must
implements HistoricalRateProviderand overridegetHistoricalApiUrl()— see Custom providers.
- to support historical rates, a provider must
CurrencyRateFetchFailedhas a new last constructor argument and property?DateTimeInterface $date. Code that constructs the event itself keeps working.
- An unknown provider — in
useProvider(),setRateProvider()ordefault_provider— throwsInvalidArgumentExceptioninstead of silently using Monobank. CheckCURRENCY_DEFAULT_PROVIDER. $rateTypeis validated: anything other thanbuy,sell,average(andallforgetRates()) throwsInvalidArgumentExceptioninstead of meaningaverage. This includes an invalidCURRENCY_DEFAULT_RATE_TYPE.getRate(),getRates()andcurrency_rate()take?string $rateType = null;nullnow meansdefault_rate_typefrom config (was a hard-coded default).getRates()throwsInvalidArgumentExceptionwhen the base currency set withsetBaseCurrency()has no rate in the provider (it used to return rates in the provider’s base silently).jsdelivrno longer returns hard-coded static rates when the CDN is down — you get the fallback-cached rates or nothing.- NBU no longer includes
UAHin its rates;getRate('UAH')still returns1.0. - The
currency.providerscontainer binding was removed — useuseProvider()/setRateProvider()orcurrency.manager. cache_ttlfrom config is now honoured (rates were always cached for 1 hour), and a newcache_ttl_empty(60 s) controls how often a failing API is retried.currency:rates --refreshclears the selected provider’s cache (respects--provider).
- Currencies with
'active' => falseare excluded fromgetActiveCurrencies()/getActiveCurrencyCodes(). jsdelivrusesUAHas base currency (wasEUR) — withdefault => 'UAH'rates no longer need recalculation; with anotherdefaultthey are recalculated through UAH.
clearCache()was added to theRateProviderinterface — custom providers that implement the interface directly (not viaAbstractRateProvider) must add it.
- PHP 8.1 is required.
1.x → 2.x
Section titled “1.x → 2.x”2.0 is a rewrite: exchange rates come from providers instead of the config file.
Requirements: PHP ^8.1, Laravel 9+ (1.x supported Laravel 5.8 – 7).
Config. Republish it — the format changed:
php artisan vendor:publish --tag=currency-config --force| 1.x | 2.x |
|---|---|
'default' => 'USD' | 'default' => 'UAH' — now the base currency of the rates |
divide_result | removed |
currencies.*.exchangeRate | removed — rates come from default_provider |
currencies.*.coin | removed |
currencies.*.format | removed |
currencies.*.active (default false) | optional; a listed currency is active unless 'active' => false |
| — | default_provider, providers, cache_ttl*, default_rate_type, default_precision, API keys |
The publish tag changed from config to currency-config.
Rate direction. 1.x exchangeRate meant “units of the currency per 1 default” and converted as amount × to ÷ from. 2.x rates are “units of the base per 1 unit of the currency” and convert as amount × from ÷ to. If you stored rates of your own, invert them.
API.
| 1.x | 2.x |
|---|---|
convert($amount, $from = null, $to = null, $format = true) — formatted string by default, null for a missing rate | convert(float $amount, string $from, string $to, ?string $rateType = null): float — always a float, throws for a missing rate. Format with format() |
format($value, $code = null, $symbol = null) — no space between symbol and number, 0 decimals by default | format(float $amount, string $currency, bool $includeSymbol = true) — symbol separated by a space, 2 decimals by default |
getCurrencies() | getAllCurrencies() |
getCurrency($code = null) | getCurrencyConfig(string $currency) (returns [] instead of null) |
issetCurrency($code) | getCurrencyConfig($code) !== [], or isSupported($code) for “the provider has a rate” |
isActive($code) | in_array($code, getActiveCurrencyCodes()) |
setUserCurrency() / getUserCurrency() | removed — keep the user’s currency in your app (session, user model) |
getActiveCurrencies() | same name; inactive now means 'active' => false |