So I am in the process of upgrading an old Laravel project from 5.3 to 5.4 (plan on going to 5.8), but I've run into a problem with my unit tests in which doubles are losing their decimal places.
Here's an example of one such test (yes my tests interact with a database):
$this->user->message_score = 3.42;
$this->user->save();
echo $this->user->fresh()->message_score; // 3
The migration for the table:
$table->double('message_score')->nullable();
Whatever number I set, the decimal places are lost. It isn't round up or down, 3.42 becomes 3, as does 3.99.
Tinker does not have the same issue.
I assume I am missing something simple.
Any help is appreciated.
EDIT
So I ran a migration to update the message_score
precision and I am running into the same problem. The length displayed in the database before running the migration was (8,2)
and the migration I ran pushed it up to (10,8)
. The migration is below:
DB::statement('ALTER TABLE `users` MODIFY `message_score` DOUBLE(10,8) NOT NULL DEFAULT 0.0;');
The problem is related to the MYSQL connection, specifically setting PDO::ATTR_EMULATE_PREPARES
to true.
PDO::ATTR_EMULATE_PREPARES is what allows you to reuse parameters in a prepared statement, for example, you could just use :id
instead of :id1, :id2, :id3
.
When it is set to true
on PHP >= 7.2, it causes all floats to be cast to an integer, therefore losing the precision. This is due to Laravel not supporting this PDO flag.
There are essentially two options:
Set PDO::ATTR_EMULATE_PREPARES to false and then start uniquely naming parameters, like so :id1, :id2, :id3
.
Or
Set your floats to strings, at which point they will keep their decimals place as Laravel will no longer see it as a float and cast it to an integer.
Here is the issue on GitHub if you want to read more into it: https://github.com/laravel/framework/issues/23850