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);
}
}