升级PHP、Yii、MariaDB后institutionId类型变更的原因及全局修复方法
技术栈升级后API字段类型变更问题分析与修复
版本变更详情
| Component | Old Version | New Version |
|---|---|---|
| PHP | 7.3.0 | 8.3.0 |
| Yii | 2.0.6 | 2.0.51 |
| MariaDB | 10.1.34 | 11.6.2 |
API响应变化
旧版本响应(institutionId为字符串类型)
{
"statusCode": 200,
"message": "",
"data": {
"institutionId": "66"
} }
新版本响应(institutionId为整数类型)
{
"statusCode": 200,
"message": "",
"data": {
"institutionId": 66
} }
相关代码片段
API控制器方法
public function actionGetInstitutionDetails() { $request = Yii::$app->request; $user = $request->get('userId'); $institutionId = $request->get('institutionId'); $userId = $user ? $user : yii::$app->user->identity->id; if ($userId && $institutionId) { $institutionDetails = ExtendedInstitution::getInstitutionData($institutionId); $resultSet = []; $result = new \stdClass(); if ($institutionDetails) { foreach ($institutionDetails as $key => $value) { $data = new \stdClass(); $data->institutionId = (!empty($value['id'])) ? $value['id'] : ''; $data->institutionName = (!empty($value['name'])) ? $value['name'] : ''; $data->phoneNumber2 = (!empty($value['phone2'])) ? $value['phone2'] : 'Not available'; $data->email = (!empty($value['email'])) ? $value['email'] : ''; $data->website = (!empty($value['url'])) ? $value['url'] : ''; } $this->statusCode = 200; $this->message = ''; $this->data = $data; return new ApiResponse($this->statusCode, $this->data,$this->message); } } }
数据获取方法与存储过程
public static function getInstitutionData($institutionId) { try { $institutionDetails = Yii::$app->db->createCommand( "CALL get_institution(:institutionid)") ->bindValue(':institutionid' , $institutionId ) ->queryAll(); return $institutionDetails; } catch (Exception $e) { return false; } }
CREATE DEFINER=`root`@`%` PROCEDURE `get_institution`(institutionid int) BEGIN SELECT i.id, i.`name`, i.pin, i.institutionlogo, i.email, i.url, i.moreurl, i.institutiontype AS 'institutiontypeid', FROM institution i WHERE i.id = institutionid; END
API响应类
class ApiResponse implements \JsonSerializable { public $statusCode; public $data; public $message = ""; public function __construct($status = null, $data = null, $message = "") { $this->statusCode = $status; $this->message = $message; $this->data = $data; } public static function __set_state($data) { $obj = new ApiResponse(); $obj->data = $data["data"]; $obj->message = $data["message"]; $obj->statusCode = $data["statusCode"]; return $obj; } /** * This is the abstract method which returns json encoded data */ function jsonSerialize() { if (is_null($this->data)) { $this->data = new \stdClass(); } return [ 'statusCode' => $this->statusCode, 'message' => $this->message, 'data' => $this->data ]; } }
问题原因
- PHP版本升级:PHP 8.x对JSON序列化的类型处理更严格,PDO扩展的类型映射也更精准,会保留数据库返回的整数类型为PHP的
int,而PHP 7.3中可能默认将数值隐式转为字符串。 - Yii框架升级:Yii 2.0.51优化了数据库组件的类型映射逻辑,不再自动将整数转为字符串,直接返回原始数值类型。
- MariaDB版本升级:新版本数据库驱动对存储过程返回的字段类型识别更准确,确保
i.id(整数类型)以数值形式传递给PHP,而非旧版本可能出现的字符串转换。
三者共同作用下,原本被隐式转为字符串的institutionId现在以整数类型输出到JSON响应中。
全局修复方案
方案1:PDO全局字符串化查询结果
在Yii数据库配置中添加PDO::ATTR_STRINGIFY_FETCHES属性,强制所有查询结果转为字符串:
// 数据库配置文件(如config/db.php) 'db' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=your_db', 'username' => 'root', 'password' => 'your_pwd', 'charset' => 'utf8mb4', 'attributes' => [ PDO::ATTR_STRINGIFY_FETCHES => true, // 全局将数值转为字符串 ], ],
优点:无需修改业务代码,一次性解决所有字段类型问题;注意:若业务中有依赖数值类型的计算逻辑,需手动将字符串转为整数后再处理。
方案2:修改API响应序列化逻辑
在ApiResponse类的jsonSerialize方法中,递归遍历响应数据,将所有以Id结尾的整数字段转为字符串:
class ApiResponse implements \JsonSerializable { // 原有属性和构造方法不变... function jsonSerialize() { if (is_null($this->data)) { $this->data = new \stdClass(); } // 处理所有Id结尾的整数字段 $processedData = $this->convertIdFieldsToString($this->data); return [ 'statusCode' => $this->statusCode, 'message' => $this->message, 'data' => $processedData ]; } /** * 递归转换所有Id结尾的整数字段为字符串 */ private function convertIdFieldsToString($data) { if (is_object($data)) { foreach ($data as $key => $value) { if (preg_match('/Id$/', $key) && is_int($value)) { $data->$key = (string)$value; } elseif (is_object($value) || is_array($value)) { $data->$key = $this->convertIdFieldsToString($value); } } return $data; } elseif (is_array($data)) { foreach ($data as $key => $value) { if (preg_match('/Id$/', $key) && is_int($value)) { $data[$key] = (string)$value; } elseif (is_object($value) || is_array($value)) { $data[$key] = $this->convertIdFieldsToString($value); } } return $data; } return $data; } }
优点:仅修改响应层,不影响数据库查询和业务逻辑;注意:需确保所有需要转换的ID字段命名符合xxxId格式。
方案3:自定义数据库结果处理器
创建自定义的DbCommand类,重写queryAll方法,统一转换整数类型字段为字符串:
namespace app\components; use yii\db\Command; class CustomDbCommand extends Command { public function queryAll($fetchMode = null) { $result = parent::queryAll($fetchMode); return $this->convertNumericFieldsToString($result); } private function convertNumericFieldsToString($data) { if (is_array($data)) { foreach ($data as &$row) { if (is_array($row)) { foreach ($row as &$value) { if (is_int($value)) { $value = (string)$value; } } } } } return $data; } }
然后在数据库配置中指定commandClass:
'db' => [ 'class' => 'yii\db\Connection', 'commandClass' => 'app\components\CustomDbCommand', // 其他配置... ],
优点:在数据库查询层统一处理,业务代码无需改动;注意:需考虑性能影响,尤其是大数据量查询时。
内容的提问来源于stack exchange,提问作者Meghana S Kumar
相关产品推荐
相关产品推荐

