Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 13.07.2017 10:37:38

Курсы валют

Добрый день.
Я стараюсь спроектировать интернет магазин. Нужно что бы в нем можно было выбрать валюту (к примеру доллар) и все товары (цены) отображались бы в долларах. Но я не знаю как правильно спроектировать базу данных для этого.
Таблица с товарами
id
name
...
price // сюда пишется цена товара
Так же есть дефолтная валюта в которой отображаются цены, и надо как то вести курс валют относительно к дефолтной валюте.
Как можно спроектировать тут бд?
Спасибо!

Не в сети

#2 13.07.2017 10:43:41

Re: Курсы валют

В price хранишь цену как integer. Например, 20 долларов хранишь как 20000, перед сохранением или после извлечения данных из таблицы конвертируешь как нужно с помощью читателей и преобразователей (accessors & mutators или setters & getters).

https://laravel.ru/docs/v5/eloquent#читатели

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

Изменено AlexeyMezenin (13.07.2017 13:30:34)

Не в сети

#3 13.07.2017 12:03:44

Re: Курсы валют

всё-таки цены лучше хранить как decimal!

Не в сети

#4 13.07.2017 13:33:41

Re: Курсы валют

всё-таки цены лучше хранить как decimal!

Всё-таки лучше как integer, с decimal тебя ждёт очень весёлое будущее с округлением "копеек" вниз и вверх, т.к. это приближённые вычисления. Говорю по собственному опыту. В интернете много статей на эту тему.

Довольно коротко и понятно об этом написано в доке PHP:

Кроме того, рациональные числа, которые могут быть точно представлены в виде чисел с плавающей точкой с основанием 10, например, 0.1 или 0.7, не имеют точного внутреннего представления в качестве чисел с плавающей точкой с основанием 2, вне зависимости от размера мантиссы. Поэтому они и не могут быть преобразованы в их внутреннюю двоичную форму без небольшой потери точности. Это может привести к неожиданным результатам: например, floor((0.1+0.7)*10) скорее всего вернет 7 вместо ожидаемого 8, так как результат внутреннего представления будет чем-то вроде 7.9999999999999991118....

Не в сети

#5 13.07.2017 14:09:32

Re: Курсы валют

Всё-таки лучше как integer

Плюсую. Свое мнение могу продвигать как авторитетное, ибо последнее время занимаюсь биллинговой системой (большой по Российским меркам), храним деньги как bigint(20).
Гадание на картах точнее, чем операции с плавающей точкой  (в php) smile

Не в сети

#6 13.07.2017 16:02:20

Re: Курсы валют

Спасибо всем!

Не в сети

#7 13.07.2017 17:16:20

Re: Курсы валют

хм. интересно. надо проверить конечно, но по-моему у меня в проекте деньги на стороне пхп во float нигде не должны кастоваться, обрабатываются как строки, а вся «математика» над ними выполняется на стороне базы (в моём случае – постгрес). насколько я понимаю база с ними обращается аккуратно, возможно выполняет все операции как целочисленные и только форматирует их при выводе…

Не в сети

#8 13.07.2017 19:24:44

Re: Курсы валют

но по-моему у меня в проекте деньги на стороне пхп во float нигде не должны кастоваться, обрабатываются как строки

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

насколько я понимаю база с ними обращается аккуратно, возможно выполняет все операции как целочисленные и только форматирует их при выводе…

Не скажу за Postgres, но у меня из-за DECIMAL была куча проблем с MySQL. На стороне PHP обычно всё нормально, но внутри БД и между БД и PHP постоянно какие-то накладки.

Например, простая операция вроде "уменьшить баланс на цену заказа и вернуть новый баланс" превращается в магию с несколькими кастами и умножениями, потому что в MySQL единственный способ вернуть что-либо из UPDATE это LAST_INSERT_ID() (да, очень интуитивно), а он понимает только целые числа. Были ещё проблемы с числами, близкими к нулю - когда они воспринимаются как отрицательные и, соответственно, нельзя было оформить заказ, если на балансе равная с ним сумма и т.д. В итоге - костыли типа round(), "0.0009" и т.д.

В новых проектах на MySQL, конечно, лучше использовать INT.

Очень похожая ситуация с временными зонами, но она, благо, решается просто переводом настроек зон (в MySQL и PHP) в UTC без изменения таблиц.

Не в сети

#9 21.07.2017 15:52:52

Re: Курсы валют

Здравствуйте еще ра!)
Тут возник вопрос, довольно таки базоввй, но я немогу что то сообразить.
Есть таблица с налогами (taxes). К примеру US-CA 9.75%. Учитывая что цены я теперь храню в integer с добавлением 3х нулей, то как правильно расчитывать налог теперь?
Извиняюсь если это тупой вопрос. Просто я реально немогу сообразить...

Не в сети

#10 21.07.2017 16:52:11

Re: Курсы валют

Учитывая что цены я теперь храню в integer с добавлением 3х нулей, то как правильно расчитывать налог теперь?

В чём трудность? Налог 9.75%, это значит что цены увеличиваются на 1.0975. Не имеет значения, цены это int или double, просто будет небольшое округление или потеря точности. Для процентов без разницы, сколько нулей ты хранишь после запятой при использовании int.

Например, можно проиндексировать товар, вне зависимости от типа price:

UPDATE goods SET price = ROUND(price * 1.0975)

Не в сети

#11 11.01.2018 14:14:41

Re: Курсы валют

  1. Например, можно проиндексировать товар, вне зависимости от типа price:

Спасибо. Сразу не подумал об этом решении.

Есть еще один вопрос.

  1. В price хранишь цену как integer. Например, 20 долларов хранишь как 20000, перед сохранением или после извлечения данных из таблицы конвертируешь как нужно с помощью читателей и преобразователей (accessors & mutators или setters & getters).

Я так и сделал. но сейчас выяснилось что, когда я заношу в базу к примеру 3.99, бд сама его к 4 приводит. Но я не добавляю 3 нолика. Вообще запутался. Можно примеры как заносить такую цену, и получать (преобразовывать) в мутаторе если не трудно.

Не в сети

#12 11.01.2018 16:31:53

Re: Курсы валют

В MySQL тип DECIMAL (он же NUMERIC) это тип с фиксированой точкой, как и INTEGER он содержит точное значение "exact value" smile Поэтому он подходит для хранения денежных значений. Никакой накапливающейся ошибки не будет при суммировании в SQL-запросах!

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

Ссылки:
https://dev.mysql.com/doc/refman/5.7/en … types.html
http://www.php.net/manual/en/ref.bc.php


There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.

Не в сети

#13 11.01.2018 18:08:29

Re: Курсы валют

Proger_XP пишет:

Например, простая операция вроде "уменьшить баланс на цену заказа и вернуть новый баланс" превращается в магию с несколькими кастами и умножениями, потому что в MySQL единственный способ вернуть что-либо из UPDATE это LAST_INSERT_ID()

https://stackoverflow.com/a/24799562/272885

UPDATE tbl_user SET
     amount = @amount := amount-'$amount'
 WHERE id='$id' LIMIT 1;

 SELECT @amount;

There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.

Не в сети

#14 11.01.2018 19:46:53

Re: Курсы валют

artoodetoo
При разрыве соединения между UPDATE и SELECT нет возможности узнать, было ли совершено изменение (разрыв произошел до UPDATE или до SELECT), или нет. Вру, при любой конфигурации запроса можно полагаться на число измененных строк, если проверять это после UPDATE. Решение годное, хотя использование переменных в таких запросах мне не очень нравится.

Тем не менее, я не рекомендую использовать DECIMAL для хранения денежных единиц, так же как работать с датой/временем не в UTC (кроме вывода). На первый взгляд это может казаться странным, поэтому призываю всех сомневающихся пробовать, и оценить мудрость данного правила через N лет.

Не в сети

Подвал раздела