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

thinkerytim's avatar

Model json casting to array not working

I have a postgres table with a couple of jsonb columns defined. Obviously the data in those columns is json. I assumed all I'd need to do would be to add the $casts property in my model to ensure the json is cast to / from an array when it's read out or written to, as it's documented here: https://laravel.com/docs/5.3/eloquent-mutators#array-and-json-casting

Here's my model:

namespace App;
use Illuminate\Database\Eloquent\Model;
class Configuration extends Model
{
    protected $casts = [
        additional_options' => 'array',
    ];
// etc.

However, when I read out the first row with tinker ($r = App\Configuration::first();) the data is still in json format:

additional_options: "{"batch_size": 2000, "version": "1.7.2", "batch_process": true, "password": null}",

What am I doing wrong here?

0 likes
12 replies
thinkerytim's avatar
thinkerytim
OP
Best Answer
Level 2

Nevermind-- I spent way too much time messing with this and never noticed that you have to actually call the attribute for it to be cast as expected. So looking at the raw record, it's still in json format ($r = App\Configuration::first();), but if you then call $r->additional_options it shows the variable cast as an array, as expected.

6 likes
Mohannad's avatar

I'm having the same issue (using Laravel 5.3.26), even when I call the field attribute ($model->arrayField) I'm experiencing unexpected behaviour: sometimes it's working fine, sometimes it's not working.. (testing from tinker and dd) I thought the issue is related somehow to whether the field value has a special characters, new lines(\r\n) , ...etc. but it's not. I did this workaround because I couldn't follow along to see exactly where is the issue exactly, hope I'll be able to come back later with more details to report it.

My workaround basically is to attach/append additional field and manually encode/decode it, e.g:

class Order extends Model
{
    public $appends = ['addressParsed'];
    public function getAddressParsedAttribute()
    {
        return is_array($this->address) ? $this->address : (array) json_decode($this->address);
    }
}
3 likes
robbydooo's avatar

Having the same problem as @Mohannad , sometimes its returning as expected, sometimes its not. Mohannad workaround works for me also.

Laravel 5.4

papa's avatar

The same issue here with casts 5.4.23. Not working properly

awint's avatar

Had the same issue using 5.4.17, used an Accessor as a workaround. Data being retired from the database seem to be JSON encoded

class Item extends Model {

public function getattributesAttribute($value)
{
    return json_decode(json_decode($value));
}

This could because my data was double quoted and escaped.

mysql

attributes: "{\"payable_to\":\"Mike Smith\",\"address\":\"23 2nd Ave\",\"address_2\":\"Suite 212\",\"city\":\"New York\",\"state\":\"NY\",\"zip\":\"12322\",\"explanation\":null,\"due_date\":\"10\\/23\\/12\"}"

tinker

Getting the top level data

>>> $items = App\Item::find(8)
=> App\Models\Item {#879}
>>> $items
 fee_type_id: 3,
     attributes: ""{\"payable_to\":\"Mike Smith\",\"address\":\"23 2nd Ave\",\"address_2\":\"Suite 212\",\"city\":\"New York\",\"state\":\"NY\",\"zip\":\"12322\",\"explanation\":null,\"due_date\":\"10\\/23\\/12\"}"",
     quantity: 1,

tinker

Getting the JSON attributes

$items->attributes
=> {#892
     +"payable_to": "Mike Smith",
     +"address": "23 2nd Ave",
     +"address_2": "Suite 212",
     +"city": "New York",
     +"state": "NY",
     +"zip": "12322",
     +"explanation": null,
     +"due_date": "10/23/12",
   }
>>> $items->attributes->payable_to
=> "Mike Smith"

awint's avatar

After working on this issue ended up refactoring to just one json_decode because the original cast method mutated the data before saving. Which seems to have caused the issue. Still, need to dig deeper into the root cause. But the standard Laravel Accessor seemed to work consistently.

public function getattributesAttribute($value)
{
    return json_decode($value);
}

Mohannad's avatar

I was reviewing my replies and came here :D too late I think, but can we confirm that the issue somehow related to the double quoted json data being escaped before saving?

In my case, when I found that it's just about escaping, I remembered that the not-working data, was saved when the code was earlier like this:

 $model->attr = json_encode(['key'=>'value']);

and the working-fine data, started after modifying the code to be:

 $model->attr = ['key'=>'value'];

at this point, and hours of digging inside the framework, I was confident it was my mistake, not the framework.

4 likes
carestad's avatar

A bit late to it here, but I found that when the column has been created as a TEXT column, the array/json casting would not work. However, changing it to LONGTEXT made it work. Since I'm using MariaDB 10.1, the JSON alias isn't present quite yet, and it is anyway just an alias for LONGTEXT.

The documentation should reflect this I suppose, as now it just says:

For example, if your database has a JSON or TEXT field type that contains serialized JSON, adding the array cast to that attribute will automatically deserialize the attribute to a PHP array

jusep's avatar

A bit late to the party but nice and clean solution is to use $table->jsonb(..) field if you are using mysql. It seems that using $table->json is not working nicely with casts.

2 likes
JasonGuru's avatar

For those still facing this issue, store or update the column in array NOT json_encode format. Example,

protected $casts = [
        'options' => 'array'
  ];

$options = [
	'Size' => ['M', 'L']
];

instead of

$model->options = json_encode($options, true);

do

$model->options = $options;
$model->save;
18 likes

Please or to participate in this conversation.