yii中需重写fields()和extrafields()控制序列化字段,日期用getter格式化防null异常,asarray()配合array_map批量处理,json响应用toarray指定extrafields,导出excel时用sprintf避免科学计数法。

在Yii项目中将数据库查出的模型数据直接返回给前端,常遇到时间戳变当前时间、数字被截断、null值显示为"(Not set)"等格式混乱问题,必须对查询结果做针对性格式化处理才能交付可用数据。
模型字段自动转数组并格式化
调用toArray()前先在模型类中重写fields()和extraFields()方法,控制哪些字段参与序列化。
在fields()里显式声明需要输出的字段名,避免敏感字段(如password_hash)意外暴露。
对日期字段单独定义getter,例如getCreatedAtFormatted(),内部用date('Y-m-d H:i', $this->created_at)处理,确保null值返回空字符串而非触发Formatter默认行为。
这一步操作起来很简单,直接把文件拖进去就行。
批量查询结果统一格式化
使用asArray()获取原始数组后,再用array_map()批量处理:
方法一:用Formatter组件逐字段格式化
$rows = User::find()->asArray()->all();
$formatted = array_map(function($row) {
return [
'id' => (int)$row['id'],
'name' => $row['name'] ?: '-',
'created_at' => Yii::$app->formatter->asDatetime($row['created_at']),
];
}, $rows);
方法二:直接在ActiveQuery中链式调用select()和asArray(),避免中间对象创建开销。
【注意:asArray()必须在all()之前调用,否则返回的是ActiveRecord对象数组而非纯数组】
单条记录格式化为JSON响应
第一步:在控制器动作中获取模型实例
$model = Post::findOne($id);
第二步:检查是否存在,避免后续调用getter时抛出异常
if (!$model) {
throw new NotFoundHttpException('Post not found.');
}
第三步:设置响应格式并赋值data
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
Yii::$app->response->data = $model->toArray([
'Post' => ['id', 'title', 'content', 'created_at_formatted'],
]);
第四步:在模型中定义created_at_formatted这个extraField,其getter内做判空+格式化,不依赖全局Formatter对null的默认处理逻辑。
防止null值被格式器转成当前时间
直接调用Yii::$app->formatter->asDate(null)会输出"(Not set)",但Yii::$app->dateformatter->format()会把null当当前时间——这是两个不同组件。
永远不要在视图中裸写Yii::$app->dateformatter->format(),它不是Formatter组件,也不走内容协商流程。
必须改用Yii::$app->formatter->asDate($model->date_field),它天然支持null安全。
如果业务要求null显示为空字符串,就别用Formatter,直接在模型getter里写return $this->date_field ? date('Y-m-d', $this->date_field) : '';
导出Excel时的数值格式化避坑
导出场景下,数字字段如price若为float类型,PHP可能输出科学计数法(如1.23456789E+7),Excel打开后显示异常。
解决方案:在toArray()前强制转字符串并保留两位小数
'price' => sprintf('%.2f', $model->price),
这比依赖Formatter的asDecimal()更可控,因为后者受decimalSeparator和thousandSeparator配置影响,在非中文环境可能输出逗号分隔符。
【关键前提:导出前必须确认模型字段类型是float或int,不能是字符串"123.45",否则sprintf会截断】











