Skip to content

Commit c1958ae

Browse files
authored
prevent json_encode error 5 (#100)
immediately apply casting to geometry objects to prevent errors while trying to json_encode binary data
1 parent 17f3c35 commit c1958ae

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

src/Traits/HasSpatial.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MatanYadaev\EloquentSpatial\Traits;
44

5+
use MatanYadaev\EloquentSpatial\Objects\Geometry;
56
use MatanYadaev\EloquentSpatial\SpatialBuilder;
67

78
trait HasSpatial
@@ -10,4 +11,28 @@ public function newEloquentBuilder($query): SpatialBuilder
1011
{
1112
return new SpatialBuilder($query);
1213
}
14+
15+
/**
16+
* @param array<string, mixed> $attributes
17+
*/
18+
public function setRawAttributes(array $attributes, $sync = false)
19+
{
20+
$result = parent::setRawAttributes($attributes, $sync);
21+
22+
foreach ($attributes as $attribute => $value) {
23+
if ($value && is_string($value) && ! preg_match('//u', $value)) { // the string is binary
24+
// access the attribute to force conversion via attribute cast
25+
$spatialAttribute = $this->$attribute;
26+
27+
// override attribute and original attribute to get rid of binary strings
28+
// (Those would lead to errors while JSON encoding a serialized version of the model.)
29+
if ($spatialAttribute instanceof Geometry) {
30+
$this->attributes[$attribute] = $spatialAttribute;
31+
$this->original[$attribute] = $spatialAttribute;
32+
}
33+
}
34+
}
35+
36+
return $result;
37+
}
1338
}

tests/GeometryCastTest.php

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@
109109
TestPlace::insert(array_merge(TestPlace::factory()->definition(), [
110110
'point_with_line_string_cast' => DB::raw('POINT(0, 180)'),
111111
]));
112-
/** @var TestPlace $testPlace */
113-
$testPlace = TestPlace::firstOrFail();
114112

115-
expect(function () use ($testPlace): void {
116-
$testPlace->getAttribute('point_with_line_string_cast');
113+
expect(function (): void {
114+
// cast deserialization happens in setRawAttributes
115+
// called immediately after retrieving db record
116+
TestPlace::firstOrFail();
117117
})->toThrow(InvalidArgumentException::class);
118118
});
119119

@@ -126,3 +126,41 @@
126126

127127
expect($testPlace->point)->toEqual($point);
128128
});
129+
130+
it('is serializeable and JSON encodeable by standard serialize / JSON encode functions', function (): void {
131+
$point = new Point(0, 180);
132+
133+
/** @var TestPlace $testPlace */
134+
$testPlace = TestPlace::factory()->make(['point' => $point]);
135+
136+
$serialized = serialize($testPlace);
137+
$json = json_encode($serialized);
138+
139+
expect($json)->toBeTruthy('JSON encoding failed.');
140+
141+
// @phpstan-ignore-next-line
142+
$recoveredTestPlace = unserialize(json_decode($json));
143+
144+
expect($recoveredTestPlace)->toEqual($testPlace);
145+
});
146+
147+
it('is serializeable and JSON encodeable by standard serialize / JSON encode functions when retrieved from db', function (): void {
148+
$point = new Point(0, 180);
149+
150+
/** @var TestPlace $testPlace */
151+
$testPlace = TestPlace::factory()->make(['point' => $point]);
152+
153+
$testPlace->save();
154+
155+
$testPlaceFromDb = TestPlace::find($testPlace->id);
156+
157+
$serialized = serialize($testPlaceFromDb);
158+
$json = json_encode($serialized);
159+
160+
expect($json)->toBeTruthy('JSON encoding failed.');
161+
162+
// @phpstan-ignore-next-line
163+
$recoveredTestPlace = unserialize(json_decode($json));
164+
165+
expect($recoveredTestPlace)->toEqual($testPlaceFromDb);
166+
});

0 commit comments

Comments
 (0)