laravel-eloquent-spatial: Unable to JSON encode payload. Error code: 5

Thank you for your work on this package! I’ve been fighting an issue for the past week that I can’t find a resolution to. I don’t know if this is an issue with this package or Laravel but I thought that I would start here.

I have a queued job that has an object (not a model) as constructor argument. The object has property that is a model with a coordinates (Point) attribute.

something like:

class Report
{
  $account = null;
  public function __construct(Model $account) {
    $this->account = $account
  }
}

$account = Account::first();
MakeReport::dispatch(new Report($account));

The coordinates attribute is properly casted on the Account model

protected $casts = [
    'coordinates' => Point::class
];

The problem is that the coordinates are not being serialized by laravel and I get the error: Illuminate\ Queue\ InvalidPayloadException Unable to JSON encode payload. Error code: 5

Any ideas? I’m starting to lose my mind on this.

PHP 8.1.10 Laravel 9.28.0

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Comments: 16 (12 by maintainers)

Most upvoted comments

I see, you can simplify the test to this code:

  $testPlace = TestPlace::factory()->create([
    'point' => new Point(0, 180),
  ])->fresh();

  $json = json_encode([
    'serialized_test_place' => serialize($testPlace),
  ]);

  
  expect($json)->toBeFalse();
  expect(json_last_error())->toBe(5);

As you already noticed, fixing this issue will require migrating from custom casts to setRawAttributes. I’m not sure I want to do it at the moment. I’m happy you found a solution for now. If I’ll see that this binary representation will produce more issues for this package users, I’ll re-consider doing this change.

Thanks for bringing this to my attention.

Hi @d0m4te, can you please submit a PR with tests?

Yes and no. Laravel jobs (that use SerializesModels) that have a model constructor argument work because SerializesModels extracts the model identifier and relationships. That data alone is passed through json_encode in \Illuminate\Queue\Queue@createPayload. If however the job has a constructor argument that is not a model (but has a model as it’s own property) then SerializesModels does not work and the entire object passes through json_encode. At no point is the geospatial binary data transformed into a MatanYadaev\EloquentSpatial object. json_encode does not trigger any magic methods to serialize or encode the binary geospatial data and therefore throws the Unable to JSON encode payload. Error code: 5 exception.

This issue was first described in 2015: https://github.com/laravel/framework/issues/11100 and various other issues, ex: https://github.com/michaeldyrynda/laravel-efficient-uuid/issues/22

This issue was solved in grimzy/laravel-mysql-spatial by overriding setRawAttributes https://github.com/grimzy/laravel-mysql-spatial/blob/2ca9f2f25cf10e3b4771881108ecc9c69317bf57/src/Eloquent/SpatialTrait.php#L102.

Just as a test, if I make the below change in my model then everything works. Model custom casts are a nice but also flawed.

/**
 * The list of attributes to cast.
 *
 * @var array
 */
protected $casts = [
    //'coordinates' => Point::class
];

public function setRawAttributes(array $attributes, $sync = false)
{
    $spatial_fields = ['coordinates'];

    foreach ($attributes as $attribute => &$value) {
        if (in_array($attribute, $spatial_fields) && is_string($value) && strlen($value) >= 13) {
            $value = \MatanYadaev\EloquentSpatial\Objects\Point::fromWkb($value);
        }
    }

    return parent::setRawAttributes($attributes, $sync);
}