DEV Community

Tyler Smith
Tyler Smith

Posted on • Updated on

Laravel not running accessors with numbers in attribute name when serializing to JSON

I recently ran into an issue where Laravel wasn't running an accessor when serializing my Eloquent model to JSON. I had one property called is_drop_off_location and another called is_24_hour_drop_off_location. These values were stored in an SQLite database that doesn't support booleans, and when serializing the model to JSON it would return "0" or "1" as a string.

I added the following accessors to my model to convert these strings to proper booleans:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Location extends Model
{
    public function getIsDropOffLocationAttribute($value)
    {
        return $value == "1" ? true : false;
    }

    public function getIs24HourDropOffLocationAttribute($value)
    {
        return $value == "1" ? true : false;
    }
}
Enter fullscreen mode Exit fullscreen mode

However, when I serialized to JSON, I'd get the following:

{
  "is_drop_off_location": true,
  "is_24_hour_drop_off_location": "0"
}
Enter fullscreen mode Exit fullscreen mode

As I looked around, I wasn't the only person having problems with numbers and serialization.

As far as I can tell, this bug occurs because Laravel is trying to convert the StudlyCase attribute name to snake_case, and the conversion function has no way of knowing that there's an underscore before the number.

The good news is this is a solvable problem. Laravel allows you to explicitly append values to JSON with the protected $appends property on the Eloquent model. This feature exists for adding dynamic attributes that don't correspond to database columns, but we can use it to ensure that Laravel serializes our accessor for an attribute with a number in it.

Here is what our model looks like afterwards:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Location extends Model
{
    // This will make Laravel serialize our accessor.
    protected $appends = ['is_24_hour_drop_off_location'];

    public function getIsDropOffLocationAttribute($value)
    {
        return $value == "1" ? true : false;
    }

    public function getIs24HourDropOffLocationAttribute($value)
    {
        return $value == "1" ? true : false;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now when I serialize this to JSON, Laravel knows to look at the accessor.

Here is the JSON serialized output from the code above:

{
  "is_drop_off_location": true,
  "is_24_hour_drop_off_location": false
}
Enter fullscreen mode Exit fullscreen mode

Happy coding.

Top comments (0)