- PHP:
^8.1
- Laravel:
9
This version drops support of PHP < 8.1 and now supports Laravel 9. This was made in order to keep pace with new versions and use new features.
bcmath
Now used for all calculations to be as accurate as possible.
The main purpose of this update was to raise the precision of monetary objects.
It was decided to use only string
as a representation of money due to large numbers are being stored, therefore these amounts were raised up to 4 decimal places.
For example, the following 102500
stands for 10.25
.
doctrine/dbal
package.
That's good if you heeded Uncle Bob's advice: don't use floating point numbers to represent monetary amounts.
View a migration
// Adjust these constants for your needs.
// DECIMALS is the value that is set in the config file (the value that was before).
// TABLES is an array of tables you need to change in.
// A key represents a table name and a value (array) represents needed columns for the change.
private const DECIMALS = 1;
private const TABLES = [
'users' => ['balance'],
'products' => ['min_price', 'price'],
];
public function up(): void
{
foreach (self::TABLES as $tableName => $columns) {
Schema::table($tableName, function (Blueprint $table) use ($columns) {
foreach ($columns as $column) {
$table->bigInteger($column)->default(0)->change();
}
});
$lackingDecimalPlaces = 4 - self::DECIMALS;
if ($lackingDecimalPlaces > 0) {
$multiplier = 10 ** $lackingDecimalPlaces;
DB::table($tableName)
->update(
collect($columns)
->mapWithKeys(function (string $column) use ($multiplier) {
return [$column => DB::raw("$column * $multiplier")];
})
->toArray()
);
}
}
}
Unfortunately, there is a lot of work to do here. However, I've spent some time to write a migration for you, but you still need to do a manual stuff in the code.
View a migration
// Adjust this constant for your needs.
// TABLES is an array of tables you need to change in.
// A key represents a table name and a value (array) represents needed columns for the change.
private const TABLES = [
'users' => ['balance'],
'products' => ['min_price', 'price'],
];
public function up(): void
{
foreach (self::TABLES as $tableName => $columns) {
Schema::table($tableName, function (Blueprint $table) use ($columns) {
foreach ($columns as $column) {
// WARNING: make sure the largest amount (in one of the columns) is not greater than 16 digits,
// otherwise increase the decimal parameters (20 and 4)
$table->decimal($column, 20, 4)->default(0)->change();
}
});
$multiplier = 10 ** 4;
DB::table($tableName)
->update(
collect($columns)
->mapWithKeys(function (string $column) use ($multiplier) {
return [$column => DB::raw("$column * $multiplier")];
})
->toArray(),
);
Schema::table($tableName, function (Blueprint $table) use ($columns) {
foreach ($columns as $column) {
$table->bigInteger($column)->default(0)->change();
}
});
}
}
Please note that you are responsible for the correctness of the migration. Check several times yourself locally.
If you find any vulnerability or idea for improvement in any migration, please open a new issue.
Origins were completely removed from this library, so if you use them anywhere, you must clean up every single place where it's been used.
❌ The property origin
was removed from the config file.
❌ The following methods were removed from MoneySettings
:
setOrigin()
getOrigin()
❌ The ORIGIN constants were removed:
ORIGIN_INT
ORIGIN_FLOAT
❌ The exception UndefinedOriginException
was removed.
❌ The following method was removed from Money
:
upload()
🟧 The following methods no longer accept number and origin as their first and second arguments, respectively.
Now they have only one argument (another monetary object).
❌ NO LONGER:
use PostScripton\Money\MoneySettings;
$m1 = money('100000');
$m1->add(200, MoneySettings::ORIGIN_INT)
✅ ONLY WAY:
$m1 = money('1000000');
$m1->add(money('2000000'));
😔 Unfortunately, you have to check all usages manually and adjust them so that they can accept only a monetary object as their first argument, and don't use origin
word.
All money construct methods:
new Money()
Money::of()
money()
now accept only string
as their first argument.
When the package migrated to PHP 8.1, the enums became available.
- ❌
Currency::DISPLAY_SYMBOL
=> 🟩CurrencyDisplay::Symbol
- ❌
Currency::DISPLAY_CODE
=> 🟩CurrencyDisplay::Code
CurrencyList::All
CurrencyList::Popular
CurrencyList::Custom
- ❌
Currency::POSITION_START
=> 🟩CurrencyPosition::Start
- ❌
Currency::POSITION_END
=> 🟩CurrencyPosition::End
- In the
currency_list
setting change a string on its enum equivalentCurrencyList
- In the
custom_currencies
setting change a position-constant on its enum equivalentCurrencyPosition
Put your formatting settings inside of formatting
key.
❌ Before
'thousands_separator' => ' ',
'decimal_separator' => '.',
'decimals' => 1,
'ends_with_0' => false,
'space_between' => true,
config('money.thousands_separator');
✅ After
'formatting' => [
'thousands_separator' => ' ',
'decimal_separator' => '.',
'decimals' => 1,
'ends_with_0' => false,
'space_between' => true,
],
config('money.formatting.thousands_separator');
Replace service
and services
with rate_exchanger
and rate_exchangers
correspondingly.
exchangeratesapi
is replaced with fixer
.
❌ Before
'service' => 'exchangerate',
'services' => [
'exchangeratesapi' => [
// https://exchangeratesapi.io/
'class' => \PostScripton\Money\Services\ExchangeRatesAPIService::class,
'key' => env('EXCHANGERATESAPI_API_KEY'),
'secure' => env('EXCHANGERATESAPI_SECURE', false),
'base_restriction' => env('EXCHANGERATESAPI_BASE_RESTRICTION', true),
],
'openexchangerates' => [
// https://openexchangerates.org/
'class' => \PostScripton\Money\Services\OpenExchangeRatesService::class,
'key' => env('OPENEXCHANGERATES_API_KEY'),
'base_restriction' => env('OPENEXCHANGERATES_BASE_RESTRICTION', true),
],
'exchangerate' => [
// https://exchangerate.host/
'class' => \PostScripton\Money\Services\ExchangeRateService::class,
],
],
✅ After
'rate_exchanger' => 'exchangerate',
'rate_exchangers' => [
'fixer' => [
// https://fixer.io/
'class' => \PostScripton\Money\Clients\RateExchangers\Fixer::class,
'key' => env('FIXER_API_KEY'),
'free_plan' => env('FIXER_FREE_PLAN', true),
],
'openexchangerates' => [
// https://openexchangerates.org/
'class' => \PostScripton\Money\Clients\RateExchangers\OpenExchangeRates::class,
'key' => env('OPENEXCHANGERATES_API_KEY'),
],
'exchangerate' => [
// https://exchangerate.host/
'class' => \PostScripton\Money\Clients\RateExchangers\ExchangeRate::class,
],
],
There's new settings for caching:
'cache' => [
'enabled' => env('MONEY_CACHE_ENABLED', true),
'store' => 'default',
'rate_exchanger' => [
// Request to get all supported currencies by rate exchanger.
'supports' => [
'ttl' => \DateInterval::createFromDateString('7 days'), // or `null` to store forever
],
// Request to get a real-time exchange rates. Historical rates are stored forever.
'rate' => [
'ttl' => \DateInterval::createFromDateString('1 hour'), // or `null` to store forever
],
],
],
There are several breaking changes in the constructor behavior:
- No settings in the constructor as third argument
- Accepts only string-integers, otherwise throws exception
InvalidArgumentException
- Trims all decimals. Only integers (as strings) are used to represent amount
The getPureAmount()
method has been removed, and getAmount()
now works as the previous one worked.
// 3.x =========================
$money = money('12345000');
$money->getPureAmount(); // "12345000"
$money->getAmount(); // "1 234.5"
// 4.x =========================
$money = money('12345000');
$money->getAmount(); // "12345000"
$money->toAmountOnlyString(); // "1 234.5"
These methods were moved to the Currency
class.
use PostScripton\Money\Currency;
Currency::getDefault();
Currency::setDefault(currency('RUB'));
The method has been removed due to vote results on GitHub because no one voted.
This method has been renamed to multiply()
in order to be grammatically correct.
Whenever you use it, you can take advantage of your IDE to replace this everywhere.
Press Ctrl
+Shift
+R
, select Match case
and Words
options, and replace all multiple(
with multiply(
. But first, check all found occurrences out.
In order not to stray from the previous change,
this method now accepts string
instead of float
.
It's very important because unexpected behavior may occur.
$money->multiply('1.5'); // use this
$money->multiply(1.5); // instead of this
This method now accepts string
instead of float
.
It's very important because unexpected behavior may occur.
$money->divide('1.5'); // use this
$money->divide(1.5); // instead of this
This method has been renamed to isZero()
.
Now throws MoneyHasDifferentCurrenciesException
when $money
argument has a different currency.
Now throws MoneyHasDifferentCurrenciesException
when $money
argument has a different currency.
Now throws MoneyHasDifferentCurrenciesException
when $money
argument has a different currency.
Now throws MoneyHasDifferentCurrenciesException
when $money
argument has a different currency.
The method was reworked. It no longer compares objects via ===
and ==
operators.
Now, with strict
flag it compares not only amounts, but also currencies of monetary objects.
This method no longer returns string, instead it returns a new monetary object. The doesn't accept settings as its second parameter as well.
The important thing is, the difference()
method returns an absolute amount.
use PostScripton\Money\Money;
$m1 = Money::parse('$ 25');
$m2 = Money::parse('$ 100');
// Before
$m3 = $m1->difference($m2); // "$ -75" (string)
// After
$m3 = $m1->difference($m2); // $ 75 (Money)
The signature of this method changed as following:
convertTo(Currency|string $to, ?Carbon $date = null): Money
offlineConvertTo(Currency|string $currency, string $rate): Money
The method was renamed to Money::of()
, the rest is the same.
Now you need to help a parser to recognize a currency by providing a code (USD
/840
) as the second parameter.
However, if only first parameter is passed, it will take the default_currency
from the config file.
This method now always returns 10.000
(10 powered by 4 decimal places)
The method was extracted from settings and moved here.
One of the most important things of this update is replacement MoneySettings with MoneyFormatter.
Now formatters are used in order to format (display) monetary objects.
👀 See here for full details about new formatters.
The helper for creating new settings instance was removed, because of the reason that has been mentioned above.
These methods were removed completely because of Garbage Collection.
These methods were extracted directly from settings into Money.
Most exceptions were reworked completely and some of them were removed.
- ❌
CustomCurrencyWrongFieldTypeException
- ❌
CustomCurrencyDoesNotHaveFieldException
It was decided to get rid of methods for setting a currency list from the code. From now on, it is still available for setting only from the config file.
- ❌
Currency::currentList()
- ❌
Currency::setCurrencyList()
- ❌
Currency::isIncorrectList()
All constants were replaced with enum equivalents.
See above in the Enums
section.
Use Currency::getDefault()
instead only if you don't override it with Currency::setDefault()
.
If you need to use exactly default currency from the config file,
then use config('money.default_currency')
This method has been moved to Currencies
class:
Currencies::get()
Currencies::getCodesArray()
Services have been refactored to RateExchangers. Those are used inside of convertTo()
method.
👀 See here for full details.
Most exceptions were refactored, some united and some removed. Several ones adjusted messages a little.
- 🟩
RateExchangerException
- this one united several exceptions into itself:- ❌
ServiceException
- ❌
ServiceClassDoesNotExistException
- ❌
ServiceDoesNotExistException
- ❌
ServiceDoesNotHaveClassException
- ❌
ServiceDoesNotInheritServiceException
- ❌
ServiceDoesNotSupportCurrencyException
- ❌
- 🟧 These are replaced with the standard
Exception
:- ❌
ShouldPublishConfigFileException
- ❌
WrongParserStringException
- ❌
- 🟧 These are replaced with the standard
InvalidArgumentException
:- ❌
CustomCurrencyValidationException
- ❌