Framework: [5.2][Question][Database] Using a "boolean" field in migration and eloquent does not play nice together

Created on 24 Feb 2016  路  8Comments  路  Source: laravel/framework

We have implemented a model which has a boolean property (called "public"). The migration we used to create the table adds the column using: $table->boolean('public')->default(false);. However, setting this property to any value and then saving it will still yield a "0" in that column in the database.

Am I missing something or might I have stumbled upon a bug? Note that the field is also present in the $casts attribute in the model.

Most helpful comment

Are you use mysql? In mysql, the true is stored as 1, the false is stored as 0, the type boolean is tinyint(1)

All 8 comments

Are you use mysql? In mysql, the true is stored as 1, the false is stored as 0, the type boolean is tinyint(1)

To be frank, the answer does not even come close to providing an answer to the issue. When trying to provide a full (code) example I figured out the reason I was getting the odd behaviour.

The header used to send the request was application/x-www-form-urlencoded which as we know does not play nicely with "true"-values. But the behaviour is still very strange. Because any value we set the public property in the model, will yield a returned value of "true" while it is actually stored as "0" in the database.

I'm not sure whether this is an intended feature or a side effect, but it might be good for other people who have the same issue to see what causes it.

Can you provide a short example how you store your model?

It's sounding a lot like the case where people override the constructor then forget to call the parent.

Below is a minimal example, @GrahamCampbell; I'm not overriding the constructor but still run into the issue.

The model:

class Test extends \Illuminate\Database\Eloquent\Model
{
    protected $casts = [
        "public" => "boolean",
    ];

    protected $fillable = [
        "public",
    ];
}

Minimal route to create the model:

Route::post('/', function (Illuminate\Http\Request $r) {
    return App\Test::create($r->all());
});

Migration to generate the table:

Schema::create('tests', function(Blueprint $table){
    $table->increments('id');
    $table->boolean('public')->default(false);
    $table->timestamps();
});

Now if you call the route using a properly structure json string (and headers) i.e.

{
    "public": true
}

It works fine. But when we use normal form data i.e.

public=true

Note that by default this is the behaviour of Swagger when using booleans in normal "form" calls instead of the (maybe) more applicable checkbox syntax; which is the original environment I encountered the issue in. It could therefor be that more people would run into this issue. But I can well imagine that this is a "won't fix".

Check public in your $r->all(), it seems string, "true".

I mean, you should make sure your model received boolean true, not string "true".

App\Test::create(["public": true]); should work

I'm having the same issue - application/x-www-form-urlencoded form being sent with true/false values. I perform a ternary $options->autoLix = ($request->get('autoLix') === 'true' ? 1 : 0) and none of it works. I replace 1,0 with true, false; "true", "false"; TRUE, FALSE etc. No difference.

I return the model like this:
$options->save();
return Response::json($user->liOptions);

and it shows as updated in the json response, with the correct true/false values (which shows that my ternary operator, above, is working as expected) but when I then call the model in artisan tinker or via an http route the value doesn't change. Most infuriating.

As an update, I've had success in giving an id field to the table, which I hadn't before.

Was this page helpful?
0 / 5 - 0 ratings