Yii2 登錄後,Yii::$app->user->identity 默認只包含用戶表字段,需額外攜帶關聯表(如角色表)數據,避免每次業務查詢重複 JOIN。
// User.php (extends ActiveRecord implements IdentityInterface)
public function getRole() {
// 一對一關聯:用戶表 user_role_id -> 角色表 role_id
return $this->hasOne(Role::className(), ['role_id' => 'user_role_id']);
}
public static function findByUsername($username) {
return static::find()
->joinWith('role') // 關鍵:預加載角色關聯
->where(['username' => $username, 'status' => self::STATUS_ACTIVE])
->one(); // 返回 User 對象,role 已填充
}
原理:joinWith() 產生 LEFT JOIN SQL,將角色字段一併查出並自動填充到 _related['role'],後續訪問 $user->role 不再觸發額外查詢(即「急加載」/ Eager Loading)。
public function actionLogin() {
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
// 登錄成功後,identity 已包含 role
return $this->goBack();
}
return $this->render('login', ['model' => $model]);
}
<!-- 視圖中 -->
<?= Yii::$app->user->identity->role->role_name ?>
<!-- 或業務邏輯 -->
$user = Yii::$app->user->identity;
if ($user->role && $user->role->role_code === 'admin') {
// 管理員權限邏輯
}
| 錯誤做法 | 問題 | 正確做法 |
|---|---|---|
在 getRole() 用 inverseOf('user') | 反向關聯定義在 Role 模型,User 端不需聲明 | User 端僅 hasOne,Role 端可選 hasMany(User::class, ['user_role_id' => 'role_id']) |
登錄查詢不加 joinWith | 每次訪問 ->role 觸發 N+1 查詢 | findByUsername 中 joinWith('role') |
| 角色字段名與用戶表衝突 | 如雙方都有 name 導致覆蓋 | 角色表字段加前綴(role_name)或用 select() 別名 |
public static function findByUsername($username) {
return static::find()
->joinWith(['role', 'department', 'permissions'])
->where(['username' => $username, 'status' => self::STATUS_ACTIVE])
->one();
}
後續 $user->role、$user->department、$user->permissions 均無額外查詢開銷。
select(['user.*', 'role.role_name', 'role.role_code'])joinWith 避免 N+1Yii::$app->cache->set('user_roles_' . $uid, $roles)