Laravel/MySQL如何强制返回5位小数(无需逐个字段处理)
解决MySQL/Laravel返回double字段小数位数过多的问题
一、MySQL层面方案
1. 创建视图批量处理字段
要是不想在Laravel里写额外逻辑,可以在MySQL中创建视图,自动把所有double(20,5)类型的字段统一保留5位小数。
先查询表中所有double类型字段:
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '你的数据库名' AND TABLE_NAME = 'invoices' AND DATA_TYPE = 'double';
接着用这些字段生成视图SQL,每个数值字段用ROUND(字段名,5) AS 字段名,非数值字段直接保留:
CREATE VIEW invoices_view AS SELECT id, entity_id, ROUND(net_total,5) AS net_total, ROUND(total_vat,5) AS total_vat, ROUND(grand_total,5) AS grand_total, -- 其他字段依次列出,非数值字段直接写字段名 FROM invoices;
之后把Laravel的Invoice模型$table属性改成invoices_view,查询时就会自动返回处理后的小数结果。
2. 动态生成SELECT语句(适合临时场景)
如果不想创建视图,也可以在Laravel里通过查询信息表获取所有double字段,动态生成带ROUND的查询语句:
// 获取表中所有double类型字段 $decimalColumns = DB::select("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND DATA_TYPE = 'double'", [config('database.connections.mysql.database'), 'invoices']); $decimalColumnNames = collect($decimalColumns)->pluck('COLUMN_NAME')->toArray(); // 获取表中所有字段 $allColumns = DB::select("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?", [config('database.connections.mysql.database'), 'invoices']); // 构造查询字段,数值字段加ROUND处理 $selectFields = []; foreach ($allColumns as $col) { $field = $col->COLUMN_NAME; if (in_array($field, $decimalColumnNames)) { $selectFields[] = "ROUND({$field},5) AS {$field}"; } else { $selectFields[] = $field; } } // 执行查询 $invoices = DB::table('invoices')->selectRaw(implode(', ', $selectFields))->get();
二、Laravel层面方案
1. 模型动态注册访问器
在Invoice模型中自动为所有double字段注册访问器,统一处理小数位数:
class Invoice extends Model { protected static function booted() { // 获取表中所有double类型字段 $columns = DB::select("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND DATA_TYPE = 'double'", [config('database.connections.mysql.database'), 'invoices']); foreach ($columns as $col) { $column = $col->COLUMN_NAME; // 注册模型检索后的处理逻辑 static::retrieved(function ($model) use ($column) { if (isset($model->$column)) { $model->$column = round($model->$column, 5); } }); } } }
这样每次模型查询到数据后,会自动处理所有double字段,保留5位小数。
2. 资源集合统一处理
创建资源类,在返回数据时统一处理所有浮点型字段:
// app/Http/Resources/InvoiceResource.php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class InvoiceResource extends JsonResource { public function toArray($request) { $data = parent::toArray($request); // 遍历字段,处理浮点数值 foreach ($data as $key => $value) { if (is_numeric($value) && !is_int($value)) { $data[$key] = round($value, 5); } } return $data; } } // 控制器中返回集合 use App\Http\Resources\InvoiceCollection; public function index() { return new InvoiceCollection(Invoice::all()); }
这种方法不用关心具体字段,只要是浮点型数值都会自动保留5位小数。
3. 全局响应中间件处理
创建中间件,在响应返回前统一格式化JSON中的浮点数字段:
// app/Http/Middleware/FormatDecimal.php namespace App\Http\Middleware; use Closure; class FormatDecimal { public function handle($request, Closure $next) { $response = $next($request); // 处理JSON响应数据 if (isset($response->original) && is_array($response->original)) { $this->formatNumbers($response->original); } return $response; } private function formatNumbers(&$data) { foreach ($data as &$value) { if (is_array($value)) { $this->formatNumbers($value); } elseif (is_numeric($value) && !is_int($value)) { $value = round($value, 5); } } } }
然后在app/Http/Kernel.php的$routeMiddleware中注册这个中间件:
protected $routeMiddleware = [ // 其他中间件 'format.decimal' => \App\Http\Middleware\FormatDecimal::class, ];
最后给/api/invoices路由加上这个中间件:
Route::get('/api/invoices', [InvoiceController::class, 'index'])->middleware('format.decimal');
这样该路由返回的所有JSON数据里,浮点数字段都会被自动处理成5位小数。
内容的提问来源于stack exchange,提问作者Linesofcode
相关产品推荐
相关产品推荐

