When working with money in your Laravel projects, whether it's product prices or invoice total order amounts, we need to be extremely careful not to miscalculate something. Luckily, there are best practices and tools to help us with that, let's explore them in this article.
What we'll cover:
- Typical (and wrong) Money Behavior: Floats
- One Step Better: Integers
- Special PHP Packages For Money
- Laravel Way: Custom Casts
- Currency Conversion
- Specific Laravel Packages/Wrappers
Let's get into it!
Typical (and wrong) Money Behavior: Floats
How do you save data in the database when you need to work with things like product price, or order total?
Since the amount of money is a float - like $3.45, the logical way would be to store it the same way in the database.
1$table->decimal('price', 8, 2);2// 8 is total digits, 2 is decimal digits
And then, whenever you need to show the price in Blade, you do something like:
1Total price: ${{ number_format($product->price, 2) }}
In the code above, we need number_format()
so that 9.1 would be shown as 9.10, with two digits.
In most cases, this approach should work fine, as long as you have one currency in your project and you're not doing a lot of calculations with those fields.
But the way how programming languages and database engines work with calculating floats, you may encounter a rounding problem. Meaning, you may have incorrect calculations by 0.01 if the wrong roundings add up.
Here are a few articles to read more about this problem:
- Floating Point Rounding Errors in MySQL
- Why You Should Never Use Float and Double for Monetary Calculations
- Floating Point Numbers & Currency Rounding Errors
If you are not in the mood to read those articles, and if you want to just trust me, I will simplify it for you:
NEVER STORE MONEY VALUES AS FLOATS IN THE DATABASE.
There, I said it.
Here's another quote for you:
Yes, the possibility of that rounding error happening is very low, but still, it's better to be on the safe side, right? So here are the solutions below...