RoboRobok's avatar

How are numeric columns casted automatically to integer?

I could bet $100 that it wasn't the case in the past. If you have a model with column of the type INT, FLOAT, DOUBLE (and any variations, like TINYINT etc.), it will automatically be casted to integer in Eloquent. But this is not the case for DECIMAL. Let's say I have an App\Dog model with legs_count column of type INT in the database.

If you do:

$dog = \App\Dog::firstOrFail();
var_dump($dog->legs_count);

It will give you int 4

If you change the type of column in the database directly from INT to VARCHAR, the result is immediately different: string '4'

How does it happen? I could swear that in the past everything was returned as a string by default. I was thinking that maybe a new PDO version now returns numbers somehow, but no - it just returns strings.

What is going on here?

0 likes
8 replies
Cronix's avatar

It depends on the php database driver. The pdo_mysql extension returns them as strings. mysqlnd (mysql native driver) returns them as they are (ints as ints, strings as strings, etc). It's not actually laravel or eloquent doing this.

2 likes
rooskie's avatar

@Cronix I think you've put me on to the trail of a problem that's been baffling me lately. In my local environment, numbers pulled from my MySQL database show up as numbers in Laravel (and when sent by Laravel in APIs). But it's all strings in the production environment. This has caused the most issues when foreign key id's show up as strings rather than integers.

Like the discussion that followed here, I can use the $casts property in every instance, but it seems overly explicit and tedious to have to cast every foreign key to an integer. I currently have both the pdo_mysql and mysqlnd extensions enabled. I tried disabling pdo_mysql to see if it would use mysqlnd instead, but I just get a driver error.

could not find driver (SQL: select * from ... )

Doing some research to try to change the driver didn't provide many results. I did find this article that suggests that these extensions are doing different things and that pdo_mysql is built on top of mysqlnd. The article could be totally wrong, but I'm just trying to understand.

Any thoughts on how to get my production environment driver to return the types that are stored rather than strings?

I'm running MySQL 8.0, Laravel 8.X, and PHP 8.0 (Locally and Production)

RoboRobok's avatar

@Cronix I thought so. I wonder how these drivers know the type, I thought SQL by standard just dumps everything as a regular string.

By the way, for consistency it would be better for Eloquent to return everything as a string IMHO. Someone could then assume everything is a string if he's not keen on using $casts.

Cronix's avatar

I'd guess that the mysqlnd driver looks at the schema for the tables and returns data based on the column type. Not sure, haven't dug into that c code lol.

By the way, for consistency it would be better for Eloquent to return everything as a string IMHO.

Personally, I'd give that a big, fat no. I want data returned how it's supposed to be, otherwise you'd have to typecast everything back into what it originally was if you want to use strict type checking like when using === and !==, etc.

Like if $a is supposed to be an int but it's being returned as a string, then this won't work.

$a = "1"; // let's say this is an int from db coming back as string
if ($a === 1) {
    // this will never pass
}
RoboRobok's avatar

And it shouldn't work that way, because you shouldn't rely on the engine. Database abstraction makes the engine transparent and that's its main goal. Your code with === and without $casts will not be portable, no matter if you compare to 1 or '1'.

On second thought, I'm a little more on the no side too, but for different reasons.

Cronix's avatar

Database abstraction makes the engine transparent and that's its main goal.

But that's not what QB/Eloquent actually does at that level...if some drivers return correct datatypes and others do not. All it does is normalize the sql across platforms. It returns data only as it receives it from the db driver. If laravels goal is to return all data consistently (for portabilities sake), then it itself should be doing the typecasting so it's the same no matter what driver/platform you were using. Thank goodness they're not!

We don't care if it's "portable to just any server". Our apps will only ever run on the servers we build, and they all use the better/newer pdo driver that returns data as it should. Lol WTF is the point of even having data types then if it all just gets converted to a string in the end? That's the part that's wrong, imo. It's not unreasonable to expect that a string in the db will be returned as a string, an int as an int, etc. I think it's kinda crazy to just expect strings back for everything.

1 like
RoboRobok's avatar

Most engines return string for everything, isn't that the case?

The real debate here is how low level should Eloquent be.

Your code is not always meant to be run on your server. Think about package development. Imagine you make a package to dump the models in some way. That would make sense to be sure they are strings no matter what, that would make for a more consistent behavior. This automatic typing doesn't have much value if you care for portability because like I said - most engines just don't support it.

Cronix's avatar

Ok, none of this has to do with your question though, which was answered.

Your code is not always meant to be run on your server.

I know where our code is supposed to run, but thanks. We're not developing open source packages to be used by the public, so it's kind of a non-issue for what we're doing. I understand your points, but they just are irrelevant for us. I'm glad eloquent doesn't force it on us and we can use whatever driver is appropriate for our use case.

Please or to participate in this conversation.