Polygon cast returns bare geometry instead of SpatialExpression — spatial writes fail with MySQL error 1416

Open Beginner friendly
#334 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
88/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
laravel, mysql, php
Domain
backend, databases

Research direction

Start with Fleetbase\FleetOps\Casts\Polygon::set() and compare its typed-geometry branches with Casts\MultiPolygon. Update the related return behavior, then change server/tests/Unit/Casts/SpatialCastBranchesTest.php to expect SpatialExpression for Polygon. Run that test and the TestingSeeder; done means the seeders complete and zones write correctly.

Written by the indexing model from the issue text.

Description

Bug

Fleetbase\FleetOps\Casts\Polygon::set() returns the bare Polygon object for typed geometries, while Casts\Point and Casts\MultiPolygon wrap theirs in SpatialExpression.

Laravel re-runs class casts during getAttributes() (mergeAttributesFromClassCasts), so the second set() call overwrites the SpatialExpression that SpatialTrait::performInsert() prepared. The raw Polygon object ends up bound to the query, PDO stringifies it to (lng lat, ...), and MySQL rejects the write:

SQLSTATE[22003]: Numeric value out of range: 1416 Cannot get geometry
object from data you send to the GEOMETRY field

Reproduction

On a fresh Docker install (fleetbase/fleetbase-api:latest, bundled mysql:8.0-oracle), create a company then run:

php artisan db:seed --class="Fleetbase\FleetOps\Seeders\Testing\TestingSeeder"

NetworkSeeder fails inserting zones.border. The same path breaks Zone creation through the API — any border write is affected.

Fix

Mirror Casts\MultiPolygon — wrap typed geometries in SpatialExpression:

         // 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);
         }

The GeoJSON and SpatialExpression branches are fine as-is — non-object inputs never enter the cast cache, so they skip the re-serialisation path.

server/tests/Unit/Casts/SpatialCastBranchesTest.php currently asserts the old behavior (expect($polygonCast)->toBe($polygon)); it should expect SpatialExpression like the MultiPolygon assertion above it.

Verified: with the patch, the testing seeders complete and zones write correctly.

Dominant language
PHP
Stars
34
Forks
65
Avg merge
1d 17h
Merged PRs (30d)
31

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from fleetbase/fleetops

All issues in fleetbase/fleetops

Similar issues

More PHP issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.