Yii 1.x 的 ActiveRecord 通過 relations() 方法聲明模型間關聯,支持 BELONGS_TO、HAS_ONE、HAS_MANY、MANY_MANY 四種類型。本文深入講解統計查詢與命名範圍結合關聯查詢的高級用法,解決實際開發中「N+1 問題」與「複雜條件關聯」的痛點。
統計查詢適用於 HAS_MANY 或 MANY_MANY 關聯,用於獲取聚合信息(如評論數、平均分、標籤數),而非具體關聯對象列表。聲明方式:
class Post extends CActiveRecord {
public function relations() {
return array(
// 基礎統計:評論數
'commentCount' => array(self::STAT, 'Comment', 'post_id'),
// 多對多統計:分類數(經過中間表)
'categoryCount' => array(self::STAT, 'Category', 'post_category(post_id, category_id)'),
// 條件統計:已審核評論數
'approvedCommentCount' => array(
self::STAT, 'Comment', 'post_id',
'condition' => 'status=1'
),
// 自定義選擇字段:平均評分
'averageRating' => array(
self::STAT, 'Rating', 'post_id',
'select' => 'AVG(score)'
),
);
}
}
$post = Post::model()->findByPk(10);
echo $post->commentCount; // 自動執行 COUNT(*) 查詢
echo $post->averageRating; // 自動執行 AVG(score) 查詢
// 急切加載統計(避免 N+1)
$posts = Post::model()->with('commentCount, categoryCount')->findAll();
foreach ($posts as $p) {
echo $p->title . ': ' . $p->commentCount . ' 條評論';
}
在模型中定義 scopes(),將常用條件封裝為鏈式調用方法:
class Post extends CActiveRecord {
public function scopes() {
return array(
'published' => array('condition' => 'status=1'),
'recent' => array('order' => 'created_at DESC', 'limit' => 10),
'hot' => array('condition' => 'view_count > 1000', 'order' => 'view_count DESC'),
);
}
}
// 獲取最近發布的文章及其評論
$posts = Post::model()->published()->recent()->with('comments')->findAll();
// 等價 SQL: SELECT * FROM post WHERE status=1 ORDER BY created_at DESC LIMIT 10
// 及關聯查詢 comments
// 獲取所有文章,但只加載「已審核」且「最近」的評論
$posts = Post::model()->with('comments:approved:recent')->findAll();
// comments 為關聯名,approved/recent 為 Comment 模型中的命名範圍
class User extends CActiveRecord {
public function relations() {
return array(
'posts' => array(
self::HAS_MANY, 'Post', 'author_id',
'with' => 'comments:approved', // 默認只加載審核通過的評論
'order' => 'created_at DESC'
),
);
}
}
// 使用時
$user = User::model()->findByPk(1);
foreach ($user->posts as $post) {
// $post->comments 僅包含 approved 評論
}
| 場景 | 優化方案 | 代碼示例 |
|---|---|---|
| 大量數據統計 | 統計查詢 + 緩存 | $count = Yii::app()->cache->getOrSet('post_'.$id.'_comments', fn()=>$post->commentCount, 3600); |
| 複雜多表關聯 | 拆分為多次查詢 + PHP 組裝 | 避免超大 JOIN 導致臨時表/檔排序 |
| 動態關聯條件 | 使用 on 參數而非 condition | 'on' => 'comment.status=1 AND comment.user_id=:uid', 'params' => [':uid'=>Yii::app()->user->id] |
| 關聯計數顯示 | 列表頁使用統計查詢而非加載集合 | with('commentCount') 代替 with('comments') 然後 count() |
scopes 定義中無法直接接收動態參數,需在調用時用 applyScopes() 或手動 criteria 合併。Yii 的 relations() + scopes() + with() 三位一體,構建了強大的聲明式查詢體系。掌握統計查詢替代集合加載、命名範圍封裝業務邏輯、冒號語法穿透關聯模型這三大核心技巧,能讓你在處理複雜關聯數據時既保持代碼整潔,又避免性能陷阱。建議在項目中建立關聯查詢規範文檔,統一團隊使用模式。