diff --git a/server/src/Casts/Polygon.php b/server/src/Casts/Polygon.php index 181fd6107..7c1cb482f 100644 --- a/server/src/Casts/Polygon.php +++ b/server/src/Casts/Polygon.php @@ -32,18 +32,20 @@ public function get($model, $key, $value, $attributes) public function set($model, $key, $value, $attributes) { // Checked before the broader GeometryInterface guard below, which a - // SpatialPolygon also satisfies — otherwise this arm never fires. Both - // arms behave identically, so ordering does not change what is stored. + // SpatialPolygon also satisfies — otherwise this arm never fires. + // It must wrap in a SpatialExpression exactly as that guard does: + // returning the bare geometry here would change what is bound on writes + // that skip SpatialTrait::performInsert(). if ($value instanceof SpatialPolygon) { $model->geometries[$key] = $value; - return $value; + return new SpatialExpression($value); } if ($value instanceof GeometryInterface) { $model->geometries[$key] = $value; - return $value; + return new SpatialExpression($value); } if (Utils::isGeoJson($value)) { diff --git a/server/tests/Unit/Casts/SpatialCastBranchesTest.php b/server/tests/Unit/Casts/SpatialCastBranchesTest.php index 4fb21d19e..7671a8f2e 100644 --- a/server/tests/Unit/Casts/SpatialCastBranchesTest.php +++ b/server/tests/Unit/Casts/SpatialCastBranchesTest.php @@ -38,12 +38,15 @@ function fleetopsSpatialCastSquare(): SpatialPolygon expect($returned)->toBeInstanceOf(SpatialExpression::class) ->and($model->geometries['border'])->toBe($multi); - // The polygon cast stashes the geometry but hands the value back directly + // The polygon cast wraps the geometry like every other spatial cast — + // returning the bare object lets class-cast re-serialisation overwrite the + // SpatialExpression that SpatialTrait::performInsert() prepared, so the + // raw geometry gets bound and MySQL rejects it with error 1416 $polygonModel = new FleetOpsSpatialCastModel(); $polygon = fleetopsSpatialCastSquare(); $polygonCast = (new Fleetbase\FleetOps\Casts\Polygon())->set($polygonModel, 'border', $polygon, []); - expect($polygonCast)->toBe($polygon) + expect($polygonCast)->toBeInstanceOf(SpatialExpression::class) ->and($polygonModel->geometries['border'])->toBe($polygon); // Expressions are already bind-ready, so the point cast returns them