diff --git a/src/Traits/HasSpatial.php b/src/Traits/HasSpatial.php index 478ac43..bc789a7 100644 --- a/src/Traits/HasSpatial.php +++ b/src/Traits/HasSpatial.php @@ -2,6 +2,7 @@ namespace MatanYadaev\EloquentSpatial\Traits; +use MatanYadaev\EloquentSpatial\Objects\Geometry; use MatanYadaev\EloquentSpatial\SpatialBuilder; trait HasSpatial @@ -10,4 +11,28 @@ public function newEloquentBuilder($query): SpatialBuilder { return new SpatialBuilder($query); } + + /** + * @param array $attributes + */ + public function setRawAttributes(array $attributes, $sync = false) + { + $result = parent::setRawAttributes($attributes, $sync); + + foreach ($attributes as $attribute => $value) { + if ($value && is_string($value) && ! preg_match('//u', $value)) { // the string is binary + // access the attribute to force conversion via attribute cast + $spatialAttribute = $this->$attribute; + + // override attribute and original attribute to get rid of binary strings + // (Those would lead to errors while JSON encoding a serialized version of the model.) + if ($spatialAttribute instanceof Geometry) { + $this->attributes[$attribute] = $spatialAttribute; + $this->original[$attribute] = $spatialAttribute; + } + } + } + + return $result; + } } diff --git a/tests/GeometryCastTest.php b/tests/GeometryCastTest.php index 4a6bb27..12fdb28 100644 --- a/tests/GeometryCastTest.php +++ b/tests/GeometryCastTest.php @@ -109,11 +109,11 @@ TestPlace::insert(array_merge(TestPlace::factory()->definition(), [ 'point_with_line_string_cast' => DB::raw('POINT(0, 180)'), ])); - /** @var TestPlace $testPlace */ - $testPlace = TestPlace::firstOrFail(); - expect(function () use ($testPlace): void { - $testPlace->getAttribute('point_with_line_string_cast'); + expect(function (): void { + // cast deserialization happens in setRawAttributes + // called immediately after retrieving db record + TestPlace::firstOrFail(); })->toThrow(InvalidArgumentException::class); }); @@ -126,3 +126,41 @@ expect($testPlace->point)->toEqual($point); }); + +it('is serializeable and JSON encodeable by standard serialize / JSON encode functions', function (): void { + $point = new Point(0, 180); + + /** @var TestPlace $testPlace */ + $testPlace = TestPlace::factory()->make(['point' => $point]); + + $serialized = serialize($testPlace); + $json = json_encode($serialized); + + expect($json)->toBeTruthy('JSON encoding failed.'); + + // @phpstan-ignore-next-line + $recoveredTestPlace = unserialize(json_decode($json)); + + expect($recoveredTestPlace)->toEqual($testPlace); +}); + +it('is serializeable and JSON encodeable by standard serialize / JSON encode functions when retrieved from db', function (): void { + $point = new Point(0, 180); + + /** @var TestPlace $testPlace */ + $testPlace = TestPlace::factory()->make(['point' => $point]); + + $testPlace->save(); + + $testPlaceFromDb = TestPlace::find($testPlace->id); + + $serialized = serialize($testPlaceFromDb); + $json = json_encode($serialized); + + expect($json)->toBeTruthy('JSON encoding failed.'); + + // @phpstan-ignore-next-line + $recoveredTestPlace = unserialize(json_decode($json)); + + expect($recoveredTestPlace)->toEqual($testPlaceFromDb); +});