singletonIf('currency.rates', function ($c) { $i = $c->make(static::class); $i->init(); return $i; }); return bff('currency.rates'); } /** * Register currency rates provider * @param CurrencyRatesProvider|Closure|string $provider * @param int|null $priority * @return mixed */ public static function registerProvider($provider, ?int $priority = null) { return Providers::register('currency.rates.providers', $provider, $priority); } /** * Get registered currency rates providers * @param string|null $key provider key * @return CurrencyRatesProvider | CurrencyRatesProvider[] */ public function getProvidersList(?string $key = null) { return Providers::get('currency.rates.providers', CurrencyRatesProvider::class, $key); } /** * Get registered provider * @param string $key registered currency rates provider key * @return CurrencyRatesProvider | null */ public function getProvider(string $key): ?CurrencyRatesProvider { return $this->getProvidersList($key) ?? null; } /** * Get update provider * @return CurrencyRatesProvider | null */ public function getUpdateProvider(): ?CurrencyRatesProvider { $providerKey = $this->config('currency.rates.update.provider', '', TYPE_STR); if (! empty($providerKey)) { return $this->getProvider($providerKey); } return $this->getProviderByCurrency(Currency::default('keyword')); } /** * Get provider by currency code * @param string $currencyCode * @return CurrencyRatesProvider | null */ public function getProviderByCurrency($currencyCode): ?CurrencyRatesProvider { $currencyCode = mb_strtoupper(strval($currencyCode)); $providers = $this->getProvidersList(); foreach ($providers as $provider) { if ($provider->providerCurrency() === $currencyCode) { return $provider; } } return null; } /** * Autoupdate rates * @return bool */ public function updateEnabled(): bool { return $this->config('currency.rates.update', false, TYPE_BOOL); } /** * Update rates * @param string|null $providerKey * @return bool */ public function update(?string $providerKey = null): ?bool { $currencies = $this->currencies(); if (empty($currencies)) { return false; } if ($providerKey) { $provider = $this->getProvider($providerKey); } else { $provider = $this->getUpdateProvider(); } if (! $provider) { return false; } $rates = $provider->getRates(); if ($rates === false || empty($rates)) { $this->log(_t('site', 'Error loading currency exchange rates by provider: [provider]', [ 'provider' => $provider->providerTitle(), ])); return false; } foreach ($rates as $code => $data) { # Skip currency if (! isset($currencies[$code])) { continue; } $currency = $currencies[$code]; # Rate changed if (round($currency['rate'], 8) === round($data['rate'], 8)) { continue; } $this->updateRate($currency['id'], $data['rate']); } return true; } /** * Update currency rates * @param int $id currency ID * @param float $rate * @return bool */ protected function updateRate(int $id, float $rate): bool { if (empty($id)) { return false; } $updated = $this->db->update(Site::TABLE_CURRENCIES, [ 'rate' => $rate, 'modified' => $this->db->now(), ], [ 'id' => $id, ]); if (! $updated) { return false; } $this->app->callModules('onCurrencyRateChange', [ $id, $rate, [ 'context' => 'site-currency-rate-autoupdate', ], ]); return true; } /** * Currency settings * @return array */ protected function currencies(): array { $list = Currency::list(); $default = Currency::id(); unset($list[$default]); $result = []; foreach ($list as $v) { $code = mb_strtoupper($v['keyword']); $result[$code] = [ 'id' => $v['id'], 'code' => $code, 'rate' => floatval($v['rate']), ]; } unset($v); return $result; } }