Динамическое обновление цены товара miniShop2 по курсу доллара, через Cron.
Всем привет, друзья. Возникла у меня задача, сделать так, чтобы цены у товаров менялись в соответствии с курсом доллара, раз в сутки. Оговорюсь, что это мой способ, если вы знаете лучше, то буду рад открыть для себя еще варианты… И так, приступим.
Создаем системную опцию MODX с ключём currency — в нее будет приходить наш курс доллара.
Устанавливаем дополнение CronManager, с помощью которого мы будем получать курс и обновлять цену раз в день.
Создаем TV поле с названием usdprice, в котором мы будем писать цену в долларах, в идеале конечно добавить поле в сам товар, но и так почему бы и нет :) И не забываем указать ему шаблон товара.
Создаем сниппет с любым названием, у меня это будет getCurrency и помещаем в него следующий код, этот сниппет будет получать курс доллара на сегодняшний день:

Далее создаем еще сниппет, например с названием updatePrice, он будет обновлять цену товаров на основе существующего курса.
Вставляем в него этот код:
Когда мы все подготовили, нам нужно это дело заставить запускаться раз в сутки, ведь ЦБ обновляет курс раз в день, так, что я не вижу смысла это делать чаще.
Чтобы выполнять наши сниппеты по расписанию, нам понадобится Cron, а именно, дополнение CronManager, настраивается он очень легко, вот документация.

Теперь дело осталось за малым. Открываем в админке CronManager, и нажимаем кнопку Create new cronjob и выбираем наши два плагина и выставляем время запуска в минутах.

ВАЖНО, сначала должен запускаться сниппет получения курса доллара, а потом сниппет обновления товаров иначе товары будут обновляться раньше, чем получат новый курс.
У товаров мы заранее проставляем цену в долларах.
Далее сниппет getCurrency получает из ЦБ курс на сегодняшний день и помещает его в системную настройку currency.
Затем сниппет updatePrice проходится по всем товарам и ищет цену в долларах, если она стоит, то пересчитывает цену в рублях по сегодняшнему курсу и подставляет уже новую цену в товар, в поле price.
Вот такой функционал у меня получился, я уверен, что есть масса вариантов как это сделать и мой один из них.
Так как я новичок в сфере программирования и хоть как-то пытаюсь принести пользу, Я надеюсь, что найдутся люди, которым моя статья поможет, всем спасибо за внимание :)
Подготовка
Создаем системную опцию MODX с ключём currency — в нее будет приходить наш курс доллара.
Устанавливаем дополнение CronManager, с помощью которого мы будем получать курс и обновлять цену раз в день.
Создаем TV поле с названием usdprice, в котором мы будем писать цену в долларах, в идеале конечно добавить поле в сам товар, но и так почему бы и нет :) И не забываем указать ему шаблон товара.
Создаем сниппет с любым названием, у меня это будет getCurrency и помещаем в него следующий код, этот сниппет будет получать курс доллара на сегодняшний день:
<?php
//Получаем нашу системную опцию с курсом доллара
$settings = $modx->getObject('modSystemSetting', 'currency');
//Подключаемся к ЦБРФ и тянем у них файл с ежедневными курсами валют
function getValutes($url) {
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FAILONERROR,1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$retValue = curl_exec($ch);
curl_close($ch);
return $retValue;
}
$get = getValutes('http://www.cbr.ru/scripts/XML_daily.asp');
if ($get){
$xml = new SimpleXMLElement($get);
$currency = 0;
// Парсим приходящий XML и вытаскиваем курс той валюты, которая нам нужна
foreach ($xml->Valute as $val) {
if ($val->attributes()['ID'] == 'R01235'){
$currency = $val->Value;
};
}
// Помещаем наш курс в системную опцию 'currency' и сохраняем
$set = $settings->set('value', $currency);
$settings->save();
$modx->cacheManager->refresh(array('system_settings' => array()));
$get = $settings->get('value');
}
Далее создаем еще сниппет, например с названием updatePrice, он будет обновлять цену товаров на основе существующего курса.
Вставляем в него этот код:
<?php
// Получаем курс из системной опции
$currency = $modx->getOption('currency');
$currency = str_replace(',','.',$currency);
// Получаем все товары
$col = $modx->getCollection('msProduct');
foreach ($col as $value){
// Если есть цена в долларах, то рассчитываем цену по курсу и помещаем новую цену в поле price
if ($value->getTVValue('usdprice')) {
$price = $value->get('price');
$usd = $value->getTVValue('usdprice');
$sum = $currency * $usd;
$sum = floor($sum);
$set = $value->set('price', $sum);
$value->save();
}
}Запуск по расписанию
Когда мы все подготовили, нам нужно это дело заставить запускаться раз в сутки, ведь ЦБ обновляет курс раз в день, так, что я не вижу смысла это делать чаще.
Чтобы выполнять наши сниппеты по расписанию, нам понадобится Cron, а именно, дополнение CronManager, настраивается он очень легко, вот документация.

Теперь дело осталось за малым. Открываем в админке CronManager, и нажимаем кнопку Create new cronjob и выбираем наши два плагина и выставляем время запуска в минутах.

ВАЖНО, сначала должен запускаться сниппет получения курса доллара, а потом сниппет обновления товаров иначе товары будут обновляться раньше, чем получат новый курс.
Как это работает
У товаров мы заранее проставляем цену в долларах.
Далее сниппет getCurrency получает из ЦБ курс на сегодняшний день и помещает его в системную настройку currency.
Затем сниппет updatePrice проходится по всем товарам и ищет цену в долларах, если она стоит, то пересчитывает цену в рублях по сегодняшнему курсу и подставляет уже новую цену в товар, в поле price.
Вот такой функционал у меня получился, я уверен, что есть масса вариантов как это сделать и мой один из них.
Так как я новичок в сфере программирования и хоть как-то пытаюсь принести пользу, Я надеюсь, что найдутся люди, которым моя статья поможет, всем спасибо за внимание :)
Комментарии: 27
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
Это не полноценный крон и если на сайт никто не заходит — событие никогда не будет выполненно, странное решение. Непонятно почему нельзя было использовать нормальный крон
В сниппете используется 2 раза, 1 лишний запрос в базу, непонятно почему нельзя было вынести в переменную и уже ее использовать, например
Также не вижу округления, а если результат умножения будет с 10ю тысячными?
В сниппете получения валюты получение происходит по ID, это не кошерно, не легче ли более традиционно проверять CharCode? Да и по CharCode будет быстрее, т.к. не нужно будет использовать функцию, достаточно А так в целом пойдет, новичкам думаю пригодится т.к. все максимально просто
Разделение на два сниппета тут логичное в плане «не клади все яйца в одну корзину». А если учесть что скорость тут не критична, ибо это делается в фоне а не в момент рендера страницы, то лишний вызов второго сниппета роли не играет
со остальными замечаниями согласен
Ну тут наверное вкусовщина больше, было бы там кода строк на 500 я бы скорее всего согласился, а разбивать 40 строк по 20 так себе, ИМХО
Есть маленький вопрос, что-то не так с математикой
в системной настройке появился курс доллара 66,74 (тот же, что и у ЦБ)
Сниппет, который умножает цену у меня выглядит так (убрал TV)
После умножения товара ценой 200$ на курс почему-то получилось 13 200р, хотя калькулятор утверждает, что будет 13348. Может есть какие-то мысли отчего так происходит?
Подскажете, как записывать в результат только целое число (убрать копейки)?
Очень часто сайт ЦБ отклоняет запросы сторонних серверов на получение курса. Получить курс иной раз удается только после пятой попытки в течении часа.
Не знаю как способ лучше. Кому какой способ больше нравиться? Пересчитывать при показе товара или пересчитывать кроном ночью все товары? И почему?