Laravel 8+Voyager+Swagger中geometry字段API返回异常如何解决
问题背景
- 技术栈:Laravel 8 + Voyager + Swagger
- 参照Voyager坐标自定义文档开发,
offices表points字段采用geometry类型存储坐标,建表代码:
Schema::create('offices', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('address')->nullable(); $table->geometry('points')->nullable(); $table->timestamps(); });
- 故障现象:API返回
points字段时抛出序列化错误,尝试使用mb_convert_encoding(\DB::raw($this->attributes['points']), 'UTF-8')处理后,字段返回乱码:
"points": "\u0000\u0000\u0000\u0000\u0001\u0001\u0000\u0000\u0000??\u0010??bR??Ng???D@",
故障原因
MySQL的geometry类型存储的是WKB(Well-Known Binary)格式的二进制空间数据,不是普通字符串类型,直接读取会返回二进制内容,既无法直接被JSON序列化,强行转字符编码也只会输出乱码。
解决方法
方案1:查询时通过空间函数直接输出可读格式
查询时调用MySQL内置空间函数,将二进制geometry转为可序列化的文本/JSON格式,无需额外模型处理:
- 若需要前端地图组件直接识别的标准GeoJSON格式,使用
ST_AsGeoJSON函数:
$officeList = Office::select( 'id', 'name', 'address', \DB::raw('ST_AsGeoJSON(points) as points'), 'created_at', 'updated_at' )->get();
返回的points为标准GeoJSON字符串,可直接被Leaflet、Mapbox、高德/百度地图等前端组件解析使用。
- 若需要WKT(Well-Known Text)文本格式(如
POINT(116.397 39.908)),将ST_AsGeoJSON替换为ST_AsText即可。
方案2:模型层配置访问器+修改器,自动处理读写转换
如果不想每次查询都手写DB::raw,可在Office模型中添加访问器和修改器,自动完成字段格式转换:
// app/Models/Office.php /** * 读取points字段时自动从二进制WKB转为GeoJSON数组 */ public function getPointsAttribute($value) { if (is_null($value)) { return null; } $geoJson = \DB::selectOne( 'SELECT ST_AsGeoJSON(ST_GeomFromWKB(?)) as point', [$value] )->point; return json_decode($geoJson, true); } /** * 写入points字段时自动将坐标转为geometry格式存储 * 传入格式示例:['lng' => 116.397, 'lat' => 39.908] */ public function setPointsAttribute($value) { if (is_array($value) && isset($value['lng'], $value['lat'])) { $this->attributes['points'] = \DB::raw( sprintf("ST_GeomFromText('POINT(%f %f)')", $value['lng'], $value['lat']) ); } }
配置后所有查询、写入操作都会自动处理格式,Swagger接口文档中直接将points定义为GeoJSON对象类型即可,不会再出现序列化报错。
注意:
mb_convert_encoding是字符编码转换函数,无法处理二进制空间数据,该方案完全无效,不要使用。
内容的提问来源于stack exchange,提问作者Galina Bublik
相关产品推荐
相关产品推荐

