3

I am using Laravel 7.30.6 and have PHP 8.0.10 on my development machine; I am experiecing a weird issue where any model attributes that are booleans in my DB and have been correctly added to my $casts attribute in the Model still return 1 if the DB value is true.

I did see this SO post where they mentioned this is a bug, but it appears its been resolved in PHP8.0.6 which I'm already using a version higher than.

Can someone tell me how to resolve this? I have confirmed I am using PHP8.0.10 by typing php -v into my terminal.

Edit:
I have updated to Laravel v8.80 and this is still encountering the same issues. My PHP version is v8.0.10 which is beyond the v8.0.6 version claimed in the github issue page to have fixed it yet the problem still exists for me.

Second Edit:
Table Defintion using PostgreSQL v12.2

| table_name | column_name              | data_type                   |
|------------|--------------------------|-----------------------------|
| businesses | id                       | bigint                      |
| businesses | name                     | text                        |
| businesses | business_type_id         | bigint                      |
| businesses | is_active                | boolean                     |
| businesses | created_at               | timestamp without time zone |
| businesses | updated_at               | timestamp without time zone |
| businesses | enable_multi_queue       | boolean                     |
| businesses | multi_queue_active       | boolean                     |
| businesses | enable_shortest_option   | boolean                     |
| businesses | default_shortest         | boolean                     |

Model defintion:

class Business extends Model
{
    protected $casts = [
        'is_active'                 => 'boolean',
        'enable_multi_queue'        => 'boolean',
        'multi_queue_active'        => 'boolean',
        'enable_shortest_option'    => 'boolean',
        'default_shortest'          => 'boolean',
    ];

Link to Github Issue: https://github.com/laravel/framework/issues/37215

InvalidSyntax
  • 9,131
  • 20
  • 80
  • 127
  • So, are you using `postgresql` or `mysql`? It is a very weird bug/behavior... If you are using xdebug I would recommend to breakpoint `$model->boolean_attribute` and go inside it to see how it is resolved so you can understand what you are getting `1` instead of `true`. – matiaslauriti Jan 23 '22 at 06:50
  • Apologies I should have clarified this, I’m using Postgres. I haven’t installed xdebug but I’ll try installing this and testing soon. – InvalidSyntax Jan 23 '22 at 06:57
  • It seems it is a bug between PHP and PostrgreSql, but I am not 100% sure, so it is a really weird thing going on. – matiaslauriti Jan 23 '22 at 19:13
  • 1
    There is no context, without the actual table & model definition. Also no URL to said GitHub issue. Soon you could update Laravel furthermore to v9, but it may be the table definition. – Martin Zeitler Jan 27 '22 at 20:56
  • @MartinZeitler Please see updated post which includes table, model defintions and the Github issues mentioned. – InvalidSyntax Jan 27 '22 at 21:11
  • How are you determining the output is 1? What is the output from `dump(Business::find(1)->is_active);`? – miken32 Jan 27 '22 at 22:47

3 Answers3

2

I would recommend that you set the data type to TINYINT(1). TINYINT(1) is basically a Boolean. 1 means TRUE and 0 FALSE. MySQL, MariaDB, sqlite doesn't have a real Boolean type either. In MySQL, Boolean is just a synonym for a TINYINT(1).

If you have changed to TINYINT ($table->tinyInteger();), Laravel will do the cast correctly.

protected $casts = [
    'is_active' => 'boolean',
];

This means you have a value of type boolean and you are able to do strict (===) comparisons in your code. Like $business->is_active === TRUE ? 'is_true' : 'is_false';

Maik Lowrey
  • 15,957
  • 6
  • 40
  • 79
0

I don't know how PostgreSQL would actually store & return data-type boolean,

but Attribute Casting literally states, that:

... which is stored in our database as an integer (0 or 1) to a boolean value.

Therefore, change columns enable_multi_queue multi_queue_active enable_shortest_option default_shortest to the shortest integer type available, like INT or TINYINT, values 0 / 1.
The model may well return bool, if only the expected input type is suitable.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • Ok so I think I've misunderstood how the casting itself works, I am printing $model->enable_multi_queue in a Blade view which prints either 1 or empty, this confused me because if I returned the model to my view (JSON) then it would return either a true or false value. Wrapping my blade output in `json_encode($attribute)` shows the `true` or `false` value I was looking for. – InvalidSyntax Jan 27 '22 at 21:24
  • Just use `bool` getters, or even Blade directives (if these properties are essential for the template rendering), instead of direct property access. – Martin Zeitler Jan 27 '22 at 21:30
  • Can you include the stuff around bool in your answer and I'll mark this as the answer thanks. – InvalidSyntax Jan 27 '22 at 21:34
  • Well, the answer is to perform a cast - vs. `boolean` input and `boolean` output. What the model does with it or how the template may render, is secondary and had not been asked for. – Martin Zeitler Jan 27 '22 at 21:35
  • The input is boolean, and I was expecting a boolean when output but it wasn't. – InvalidSyntax Jan 27 '22 at 21:37
0

The answer to my issue raised for anyone experiencing the same issue:

Laravel by default when storing booleans (in Postgress) will return those values in boolean values when used in any logic and if you return your model in JSON. If you however try to print those boolean attributes in a Blade template, it will display either 1 or white space, it was this that was confusing me as the JSON output and the blade output were different.

I fixed this by pretending my blade output with json_encode($attribute), you could do this in a smarter way by using getters of course.

InvalidSyntax
  • 9,131
  • 20
  • 80
  • 127