Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Mega_Aleksandar's avatar

Laravels default bigint and mariadb

Hello everyone,

I just had a huge issue because my local docker container was sending the bigint columns as normal numeric values, while on a server they were sent as strings. Whole bunch of inertia/vue3 filtering and mapping broke because well, "1" != 1

The current solution for me was to cast all of the relevant model fields to integers, but what I am asking here now is any kind of insights on why the local docker is managing it differently? And what is your way of handling that kind of issues?

I'll gladly share any and all information you need in order to come to a proper conclusion/solution to this.

Thank you, best regards.

0 likes
6 replies
LaryAI's avatar

This is a common issue when working with MySQL/MariaDB and Laravel, especially when using JSON APIs and JavaScript frontends like Vue or Inertia. The root cause is that PHP’s PDO (which Laravel uses under the hood) fetches numeric columns as strings by default, especially for large integers (like bigint), to avoid precision loss. However, the behavior can differ based on:

  • The database driver version (MySQL vs MariaDB, and their respective PDO drivers)
  • The PHP version
  • The platform (sometimes Docker images have different defaults)
  • The database connection options

Why does this happen?

  • On some systems, PDO returns bigint columns as strings to prevent integer overflow (since PHP integers are platform-dependent).
  • On other systems, especially if the value fits within PHP's integer range, it may be returned as an integer.
  • This inconsistency can cause issues when your frontend expects a consistent type.

How to handle this?

1. Always Cast in Your Laravel Models

You already found the most robust solution: use Laravel's $casts property to ensure the correct type is sent to your frontend.

class YourModel extends Model
{
    protected $casts = [
        'your_bigint_column' => 'integer',
        // other casts...
    ];
}

This guarantees that, regardless of how PDO fetches the value, your API responses will always have the correct type.

2. Consider JSON Encoding Options

If you use response()->json() or toJson(), you can pass the JSON_NUMERIC_CHECK option, but be careful: this will convert all numeric strings to numbers, which might have side effects (e.g., leading zeros lost).

return response()->json($data, 200, [], JSON_NUMERIC_CHECK);

3. Check Your Database Connection Options

You can try forcing PDO to return native types by setting the PDO::ATTR_EMULATE_PREPARES and PDO::ATTR_STRINGIFY_FETCHES options in your config/database.php:

'mysql' => [
    // ...
    'options' => [
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false,
    ],
],

But note: This may not always work for bigint columns, especially if the value exceeds PHP's integer size.

4. Be Consistent in Your Frontend

If possible, always use strict equality (===) in JavaScript, and be aware that JSON from APIs may have numbers as strings. Consider normalizing data in your frontend as a last resort.


Summary:

  • The difference is due to PDO, PHP, and database driver/platform differences.
  • Always cast your model attributes in Laravel for consistency.
  • Optionally, adjust PDO options or JSON encoding, but casting in models is the safest.
  • Be aware of this issue when working with JavaScript frontends.

Let me know if you want to dig into your specific Docker and server configs for more details!

vincent15000's avatar

I'm not sure that docker is responsible for your issue.

Docker is just a container and doesn't handle any datas of your application.

Mega_Aleksandar's avatar

@vincent15000 Mentioned docker as a "works on my machine".

// 'options' => extension_loaded('pdo_mysql') ? array_filter([
            //     PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            // ]) : [],
            'options' => extension_loaded('pdo_mysql') ? array_filter([
    PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    PDO::ATTR_STRINGIFY_FETCHES => true,  // Disable stringifying numeric values
    PDO::ATTR_EMULATE_PREPARES => true,   // Recommended for MariaDB/MySQL
]) : [],

this is what made my local environment simulate the same "bigint to string" conversions, but I do not think it is the ultimate, global "solution" I am looking for

1 like
vincent15000's avatar

@Mega_Aleksandar I didn't need to set all these parameters when I used Docker. Are you sure you don't have something else in your code that could be responsible for your problem ?

Mega_Aleksandar's avatar

@vincent15000 That is the issue, I do not know where to begin to look. All I know is that I made changes to the code base and database, pushed it to the staging and voila - filtered content is no longer presented because, well, comparing a string with an int is false.

All I can think of right now is installing the package to be able to change a column type without loosing data and the purifier

"mews/purifier": "^3.4",
"doctrine/dbal": "^4.2",

other than that, the issue came "overnight" since I know it worked (even with those packages) on local and server, then day after any sort of comparrison that involved "integers" became false, because the frontend got strings for those expected integers.

What I am also thinking is that, after certain number of rows in the whole database, the whole database switches to "stringify" mode, so as to not loose data due to the limitations of 2^64-1

1 like
vincent15000's avatar

@Mega_Aleksandar

the whole database switches to "stringify" mode

It seems impossible that the database does this on its own.

my local docker container was sending the bigint columns as normal numeric values, while on a server they were sent as strings

If it works locally, the problem is probably a server misconfiguration. Docker works as a container and if it works locally, it must work on the server.

I don't know how you have configured docker, but you should check the docker-compose.yml configuration.

Here are some insight on how it works and what you should check.

  • docker-compose.yml : base docker configuration file

  • docker-compose.override.yml : development configuration file

  • docker-compose.prod.yml : production configuration file

When you are using sail for local development, sail up automatically merge docker-compose.yml and docker-compose.override.yml.

When you are using docker-compose up on the server, only docker-compose.yml is loaded. If you want a different configuration with no change for your local environment, you need to add another configuration (prod) to be merged with the base one and force to use it : docker-compose -f docker-compose.yml -f docker-compose.prod.yml up.

Perhaps you are not using such configuration, but it's what I'm thinking about your problem.

Please or to participate in this conversation.