Yii2读取Firebird3.0方言1的numeric(15,2)字段返回异常字符串
问题描述
使用Yii2 PHP框架访问方言1的Firebird 3.0数据库,其中大量numeric(15,2)字段用于存储金额(不适合用double precision)。
表结构:
create table invoices( id integer not null, total_amount numeric(15,2), primary key(id));
Yii2控制器访问代码:
public function actionGetList() { $sql_text = " select d.id, d.total_amount from invoices "; $data = new \stdClass(); $data->invoices = Array(); $db_data = Yii::$app->db2->createCommand($sql_text, [])->queryAll(); //var_dump($db_data); foreach ($db_data as $rec) { $inv = new \stdClass(); $inv->id = $rec["id"]; $inv->total_amount = $rec["total_amount"]; $data->invoices[] = $inv; } return json_encode($data); }
数据库配置文件db2.php:
<?php if (!extension_loaded('interbase')) { echo "Firebird/InterBase extension not loaded"; exit; } return [ 'class' => 'edgardmessias\db\firebird\Connection', 'dsn' => 'firebird:dbname=192.168.1.1/3050:/database/INVOICES.FDB', 'username' => 'SYSDBA', 'password' => 'masterkey', 'charset' => 'cp1257', 'enableSchemaCache' => false, 'schemaCacheDuration' => 3600, 'schemaCache' => 'cache', ];
未使用Yii2 ORM(ORM不适用于方言1),仅用Yii2数据库访问功能。var_dump显示total_amount为带固定垃圾值的字符串,读取多条或单条记录时分别返回4039119896.80或24833794986.24,但integer和double类型字段读取正常。不想绕开Yii2直接使用PDO等底层驱动,求基于Yii2数据库工具的解决方案。
解决方案
1. 自定义字段类型处理器
在数据库配置中利用Yii2的事件机制,注册numeric字段的转换逻辑:
return [ // 原有配置... 'on afterOpen' => function($event) { $connection = $event->sender; // 关闭字符串化获取,确保数值类型处理基础 $connection->pdo->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false); // 注册numeric类型的自定义转换回调 $connection->typeMap['numeric'] = function($value) { if (is_string($value)) { // 用正则提取有效数字部分(适配垃圾值前缀/后缀的情况) preg_match('/(\d+\.\d{2})/', $value, $matches); return isset($matches[1]) ? (float)$matches[1] : $value; } return $value; }; }, ];
2. SQL查询中显式转换字段
在查询时使用Firebird方言1支持的cast函数,将numeric字段转为干净的字符串,再在PHP中处理:
$sql_text = " select d.id, cast(d.total_amount as varchar(20)) as total_amount from invoices ";
处理结果时清理并转换数值:
$inv->total_amount = (float)preg_replace('/[^0-9.]/', '', $rec["total_amount"]);
3. 扩展Firebird Connection类
继承官方Firebird连接类,重写结果集处理逻辑,全局处理numeric字段:
namespace app\components; use edgardmessias\db\firebird\Connection as BaseConnection; class FirebirdDialect1Connection extends BaseConnection { protected function createCommand($sql = null, $params = []) { $command = parent::createCommand($sql, $params); // 重写queryAll方法,批量处理金额字段 $originalQueryAll = $command->queryAll; $command->queryAll = function() use ($originalQueryAll) { $result = $originalQueryAll(); foreach ($result as &$row) { foreach ($row as $key => &$value) { // 可通过字段名规则或元数据判断是否为numeric金额字段 if (strpos($key, '_amount') !== false && is_string($value)) { $value = (float)preg_replace('/[^\d.]/', '', $value); } } } return $result; }; return $command; } }
修改db2.php中的连接类配置:
'class' => 'app\components\FirebirdDialect1Connection',
内容的提问来源于stack exchange,提问作者TomR
相关产品推荐
相关产品推荐

