You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 19:40:36