Как сделать гибкий конвертер валют в Laravel

Это мини-руководство покажет принцип создания самостоятельного сервиса Currency в рамках фреймворка Laravel. Основная цель сервиса — конвертация сумм из внутренней валюты сайта в валюту пользователя и обратно.

}

Автор материала

Артем Зернов. Веб-разработчик, создатель проекта Лектория, эксперт MODX Revolution, директор веб-студии OpenColour. Youtube-канал OpenModx.

7 минут на прочтение
Теги по теме:

Подготовка конфигурационного файла

В папке config создаем файл currency.php со следующим содержимым:

<?php
//валюты и курсы
return [
        'code' => 'usd', //Внутренняя валюта
        'precision' => 0, //Точность при конвертации сумм
        'default' => 'rub', //Валюта пользователя по-умолчанию, если не удалось определить иную
        'rates' => [
            'usd' => [
                'rub' => 75, //Покупка usd за рубли
            ],
            'rub' => [
                'usd' => 0.01333 //Покупка рублей за usd (продажа usd)
            ],
        ]
    ]
]

Готовим словарь с валютами

Для того, чтобы иметь гибкую возможность для разных языковых версий по-разному выдавать знак валюты или слово (например руб. или rub), создаем словарь, в котором определяем перевод для соответствующего кода валюты.

Создаем файл resources/lang/en/currency.php (можно создать также и для любого другого языка resources/lang/[код языка]/currency.php) со следующим содержимым:

<?php

return [
    'rub' => '₽',
    'usd' => '$'
];

Готовим главный класс сервиса Currency

В папке app/Services/Currency создаем класс CurrencyService.php со следующим содержимым:

<?php


namespace App\Services\Currency;

use App\Dictionary\BalanceTransactionOperationCode;
use App\Models\User;

/**
 * Класс для работы с конвертацией внутренней валюты
 * Class CurrencyService
 * @package App\Services\Currency
 */
class CurrencyService
{
    protected array $rates;
    protected string $defaultCurrencyCode;
    protected int $precision;
    protected string $defaultUserCurrencyCode;

    public function __construct(string $defaultCurrencyCode, array $rates, $defaultUserCurrencyCode, $precision = 2)
    {
        $this->rates = $rates;
        $this->defaultCurrencyCode = $defaultCurrencyCode;
        $this->precision = $precision;
        $this->defaultUserCurrencyCode = $defaultUserCurrencyCode;
    }

    /**
     * Получить валюту пользователя
     * @return string
     */
    public function getDefaultUserCurrency(){
        //TODO здесь вы можете определить произвольную логику, по которой мы определяем валюту по-умолчанию для текущего пользователя (не важно, авторизованного или нет)
        /** @var User $user */
        $user = \Auth::user();
        if($user) return $user->currency_code;
        return $this->defaultUserCurrencyCode;
    }

    /**
     * Конвертация из внутренней валюты во внешнюю
     * @param $amount
     * @param null $toCurrencyCode
     * @return float
     */
    public function convertFromDefault($amount, $toCurrencyCode = null){
        if(!$toCurrencyCode){
            $toCurrencyCode = $this->getDefaultUserCurrency();
        }
        $rate = $this->rates[$toCurrencyCode][$this->defaultCurrencyCode];
        return round($amount / $rate, $this->precision);
    }

    /**
     * Конвертация из внешней валюты во внутреннюю
     * @param $amount
     * @param null $fromCurrencyCode
     * @return float
     */
    public function convertToDefault($amount, $fromCurrencyCode = null){
        if(!$fromCurrencyCode){
            $fromCurrencyCode = $this->getDefaultUserCurrency();
        }
        $rate = $this->rates[$this->defaultCurrencyCode][$fromCurrencyCode];
        return round($amount * $rate, $this->precision);
    }

    /**
     * Конвертирует и возвращает отформатированную строку со знаком валюты
     * @param $amount
     * @return string
     */
    public function convertAndFormatFromDefault($amount, $toCurrency = null){
        if(!$toCurrency) {
            $toCurrency = $this->getDefaultUserCurrency();
        }
        return number_format($this->convertFromDefault($amount, $toCurrency), 0, ',', ' ').__('currency.'.$toCurrency);
    }

    /**
     * Конвертирует и возвращает отформатированную строку со знаком валюты
     * @param $amount
     * @return string
     */
    public function convertAndFormatToDefault($amount, $fromCurrency = null){
        if(!$fromCurrency) {
            $fromCurrency = $this->getDefaultUserCurrency();
        }
        return number_format($this->convertFromDefault($amount, $fromCurrency), 0, ',', ' ').__('currency.'.$fromCurrency);
    }
}

По комментариям и названию в doc-блоке функции можно понять, что делает та или иная функция.

Добавим сервис в контейнер

В файле app/Providers/AppServiceProvider.php в методе register() добавляем следующие строки инициализации нашего сервиса CurrencyService:

use App\Services\Currency\CurrencyService;

...
//В методе register()
$this->app->singleton(CurrencyService::class, function(){
            return new CurrencyService(
                config("currency.code"),
                config("currency.rates"),
                config("currency.default"),
                config("currency.precision")
            );
        });
...

Уже можно пользоваться, но это еще не все

Теперь мы можем обращаться к нашему сервису. Например так:

<?php
...
$currencyService = \App::make(\App\Services\Currency\CurrencyService::class);

//Выведет на экране что-то вроде 86 250₽
echo $currencyService->convertAndFormatToDefault(1150, 'usd');
...

Создадим фасад и псевдоним для удобного обращения к сервису

Для более удобного обращения, создадим фасад и короткий alias.

Сначала создадим фасад. Создаем файл app/Facades/Currency.php со следующим содержимым:

<?php
namespace App\Facades;

use App\Services\Currency\CurrencyService;
use Illuminate\Support\Facades\Facade;

class Currency extends Facade
{
    protected static function getFacadeAccessor()
    {
        return CurrencyService::class;
    }
}

Этот фасад позволит нам обращаться к сервису в статической форме. Например так:

<?php

echo App\Facades\Currency::convertAndFormatToDefault(1150);

И теперь чтобы сделать обращение еще короче, добавим в файле config/app.php в настройке 'providers' следующую связь:

<?php
...
'providers' => [
    
    ...
    
    'Currency' => App\Facades\Currency::class,
]

Теперь мы можем пользоваться в любом месте приложения такой записью:

<?php
...
echo Currency::convertAndFormatToDefault(1150);

В Blade-шаблонах:

...
{{ Currency::convertAndFormatToDefault(1150) }}
...

Создаем директиву Blade для отображения внутренней валюты в валюте пользователя

В файле app/Providers/AppServiceProvider.php в методе boot добавляем следующие строки кода:

<?php
use Illuminate\Support\Facades\Blade


...
public function boot()
{
...

//Директива позволяет выводить сумму во внутренней валюте, конвертированную в валюту пользователя
        Blade::directive('currency_money', function ($amount) {
            return "<?php echo Currency::convertAndFormatFromDefault($amount) ?>";
        });
...
}

Теперь мы можем в Blade-шаблонах использовать еще более лаконичную запись:

@currency_money(1150)

или с динамическим значением:

@currency_money($sum)

Итог

Мы создали сервис Currency, который полностью настраивается через конфигурационный файл, в котором мы можем задавать курс валют и отображать их в различных местах вашего веб-приложения или сайта. Гибкость данного подхода в том, что в AppServiceProvider вы можете задать другой источник курсов валют — например какой-то внешний сервис или база данных. Также в методе getDefaultUserCurrency класса CurrencyService вы можете определить свою логику получения валюты пользователя по-умолчанию.

Удачи в веб-разработке! Если хочешь получать больше полезных материалов по Laravel, подписывайся на мой канал в Youtube: Lectoria. Обучение веб-разработке

Комментарии

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Мы используем куки на нашем сайте. Продолжая просмотр, вы соглашаетесь с условиями пользовательского соглашения
    Пожалуйста, подождите. Процесс оформления заказа может занимать до 30 секунд.