Yii2 的 toArray() 仅返回 fields() 显式声明的字段,不自动包含所有 public 属性。fields() 是字段导出的唯一控制闸门,确保安全性、API 可控性与语义清晰。

这是设计,不是 bug。
Yii2 的 toArray() 行为严格依赖模型类中定义的 fields() 方法。它不会自动包含所有 public 属性,而是只按 fields() 显式声明的键来生成数组。如果你只在 fields() 里写了 'id' => 'id',那 toArray() 就真的只返回 ['id' => 123] —— 这是框架有意为之的显式控制机制,目的是防止敏感字段、冗余字段或未初始化属性意外暴露。
fields() 是字段出口的唯一闸门
fields() 返回的是一个键值映射数组:
- 键(如
'id')是最终 JSON 或 API 响应里的字段名; - 值(如
'id'或匿名函数)决定这个字段取什么数据。
示例:
public function fields()
{
return [
'id' => 'id',
'name' => 'name',
// 没写 'created_at',它就不会出现在 toArray() 结果里
];
}哪怕模型有 public $status; 或 public $updated_at;,只要没列在 fields() 中,toArray() 就不输出。
为什么这么设计?
- 安全性:避免密码、token、内部状态等字段被无意序列化;
-
API 可控性:REST 接口需要精确控制响应结构,
fields()提供声明式定义; - 性能与语义清晰:不靠“反射所有 public 属性”这种不可靠方式,而是由开发者明确契约。
常见误解场景
有人写:
$user = new User(); $user->id = 1; $user->name = 'Alice'; var_dump($user->toArray()); // 只有 ['id' => 1]
原因不是 $name 没赋值,而是 fields() 里根本没声明 'name' —— 即使赋了值也不会出现。
如果你想要“所有已赋值属性”?
Yii2 不提供默认行为,但你可以手动扩展:
public function fields()
{
$fields = parent::fields();
// 动态加入当前已设置且非 null 的属性(谨慎使用,仅调试或特殊场景)
foreach ($this->attributes() as $attr) {
if ($this->hasProperty($attr) && $this->$attr !== null && !isset($fields[$attr])) {
$fields[$attr] = $attr;
}
}
return $fields;
}不过生产环境不推荐这种方式,违背了字段显式声明的设计初衷。
本质上,toArray() 是一个受控导出过程,不是属性快照。它的精简,恰恰是 Yii2 面向 API 和企业级开发的严谨体现。


















