app = $app; $this->defaultId = $this->app->config('currency.default', 1, TYPE_UINT); } /** * Currency model * @param int|null $id * @param array $columns * @param mixed $lang * @return array|\bff\modules\site\models\Currency */ public function model($id = null, array $columns = ['*'], $lang = null) { return Site::model()->currency($id, $columns, [], $lang); } /** * Currency data by id * @param int|null $id * @param string|null $key * @param mixed $lang * @return mixed */ public function data($id = null, ?string $key = null, $lang = null) { $id = $id ?? $this->defaultId; $index = ($lang === true ? $id . ':all' : $id); if (! array_key_exists($index, $this->data)) { $this->data[$index] = (!empty($id) ? $this->model($id, []/* as array */, $lang) : []); if (! empty($this->data[$index])) { # cache: code => id $this->idByCode($this->data[$index]['code'], $this->data[$index]); } } # particular data if (is_string($key)) { return $this->data[$index][$key] ?? null; } # all data return $this->data[$index] ?? []; } /** * Currency data by code ISO 4217: 'USD' or '980' * @param string $code * @param string|null $key * @param mixed $lang * @return mixed */ public function dataByCode(string $code, ?string $key = null, $lang = null) { return $this->data($this->idByCode($code), $key, $lang); } /** * Default currency data * @param string|null $key * @param mixed $lang * @return mixed */ public function default(?string $key = 'title_short', $lang = null) { return $this->data($this->defaultId, $key, $lang); } /** * Default currency id * @param string|null $code * @return int|null */ public function id(?string $code = null) { if ($code) { return $this->idByCode($code); } return $this->defaultId; } /** * Set/get currency id by code ISO 4217: 'USD' or '980' * @param string $code * @param array|null $data * @return int */ public function idByCode(string $code, ?array $data = null) { if (! array_key_exists($code, $this->codeIdMap)) { $data = $data ?? $this->model([ (is_numeric($code) ? 'code' : 'keyword') => $code, ], ['id','code','keyword']); if (! empty($data)) { $this->codeIdMap[$data['code']] = $data['id']; $this->codeIdMap[$data['keyword']] = $data['id']; } else { $this->codeIdMap[$code] = 0; } } return $this->codeIdMap[$code]; } /** * Currencies data list * @param bool $enabled * @param mixed $lang * @return array */ public function list($enabled = true, $lang = null) { # todo: cache return $this->model()->select() ->when($enabled, function ($query) { $query->enabled(); }) ->when($lang, function ($query, $lang) { if (is_string($lang)) { $query->lang($lang); } }) ->orderBy('num') ->get() ->keyBy('id') ->toArray(); } /** * Options list * @param mixed $selected * @param array $opts [empty, enabled, lang, currency_title] * @return string HTML */ public function options($selected, array $opts = []) { return HTML::selectOptions( $this->list($opts['enabled'] ?? true, $opts['lang'] ?? null), $selected, $opts['empty'] ?? null, 'id', $opts['currency_title'] ?? 'title_short' ); } /** * All known currencies list * @param mixed $lang * @return array */ public function knownList($lang = null) { $list = require_once modification($this->app->corePath('currency/currencies.php')); if (! is_array($list)) { $list = []; } if ($lang === 'ru') { foreach ($list as $key => $currency) { $list[$key]['title'] = $currency['title_' . $lang] ?? $currency['title']; } } return $list; } /** * Format price * @param int|string $number * @param mixed $lang * @return string */ public function formatPrice($number, ?string $lang = null) { if (! is_numeric($number)) { return $number; } $rtl = $this->app->locale()->isRTL($lang); if (fmod($number, 1) > 0) { return number_format($number, 2, '.', ($number >= 1000 && ! $rtl ? ' ' : '')); } return number_format($number, 0, '', ($number >= 1000 && ! $rtl ? ' ' : '')); } /** * Format price & currency * @param int|string $number * @param int|null $currency * @param array $opts [lang, currency_title] * @return string */ public function formatPriceAndCurrency($number, $currency = null, array $opts = []) { $lang = $opts['lang'] ?? $this->app->locale()->current(); $price = $this->formatPrice($number, $lang); $id = $currency ?? $this->id(); $currency = $this->data($id, $opts['currency_title'] ?? 'title_short', $lang) ?? ''; $extra = $this->data($id, 'extra') ?? []; $after = ($extra['view'] ?? 'after_price') === 'after_price'; $space = ' '; if (! empty($extra['without_space'])) { $space = ''; } return ($after ? $price . $space . $currency : $currency . $space . $price); } /** * Convert price to required currency (using rate) * @param int $price * @param int $from price currency * @param int $to currency to convert to * @return float */ public function convertPrice($price, $from, $to = 0) { if ( $from == $to # same price || $price <= 0 || !$this->defaultId # wrong default currency ) { return $price; } # correct currency ids if (! $from) { $from = $this->defaultId; } if (! $to) { $to = $this->defaultId; } if ($from == $to) { return $price; } # to default currency $priceDefault = ($from == $this->defaultId ? $price : round($price * floatval($this->data($from, 'rate'))) ); if ($to == $this->defaultId) { return $priceDefault; } # to particular currency $rate = floatval($this->data($to, 'rate')); return ($rate > 0 ? ($priceDefault / $rate) : $priceDefault); } /** * Convert price to default currency (using rate) * @param int $price * @param int $priceCurrency * @return float */ public function convertPriceToDefault($price, $priceCurrency) { return $this->convertPrice($price, $priceCurrency, 0); } }