Yii 的 Active Record (AR) 關聯查詢是框架最強大的特性之一,能將多表關係映射為物件屬性鏈式訪問。本文系統梳理四種關係類型、配置語法、加載策略、條件控制與靜態查詢,並附最佳實踐建議。
| 關係 | 常量 | 數據庫關係 | 典型場景 |
|---|---|---|---|
| 屬於 | self::BELONGS_TO | 多對一 | Post → User (author_id) |
| 有多個 | self::HAS_MANY | 一對多 | User → Post (author_id) |
| 有一個 | self::HAS_ONE | 一對一 | User → Profile (owner_id) |
| 多對多 | self::MANY_MANY | 多對多(需中間表) | Post ↔ Category (post_category) |
relations() 方法public function relations() {
return array(
'關係名' => array(關係常量, '關聯模型類', '外鍵', '額外選項...'),
);
}
// Post.php
'user' => array(self::BELONGS_TO, 'User', 'author_id'),
// User.php
'posts' => array(self::HAS_MANY, 'Post', 'author_id'),
'profile' => array(self::HAS_ONE, 'Profile', 'owner_id'),
// Post.php
'categories' => array(self::MANY_MANY, 'Category', 'post_category(post_id, category_id)'),
// Category.php
'posts' => array(self::MANY_MANY, 'Post', 'post_category(category_id, post_id)'),
關鍵:中間表名在前,外鍵順序與「本模型在前、關聯模型在後」對應。
$post = Post::model()->findByPk(10);
$author = $post->user; // 自動執行 SELECT * FROM user WHERE id=...
$posts = $user->posts; // SELECT * FROM post WHERE author_id=...
優點:代碼簡潔。缺點:N+1 問題,遍歷列表時產生大量 SQL。
with()$posts = Post::model()->with('user')->findAll(); // 1 條 JOIN 查詢
foreach ($posts as $post) {
echo $post->user->name; // 無額外查詢
}
支援巢狀:with('user.profile')、多關係:with('user', 'categories')。
'doingOutsources' => array(
self::MANY_MANY, 'Outsource', 'outsource_user(user_id, outsource_id)',
'condition' => "doingOutsources.status_id IN (" . Status::ASSIGNED . ", " . Status::STARTED . ")",
'order' => 'doingOutsources.created_at DESC',
'select' => 'id, title, status_id',
'limit' => 10, // 僅延遲加載生效
'offset' => 0,
'with' => 'categories', // 巢狀急切加載
'together' => true, // 強制單條 SQL(MANY_MANY/HAS_MANY)
'alias' => 'doingO', // 表別名
'on' => 'doingO.type=1', // JOIN ON 條件(覆蓋外鍵)
'index' => 'id', // 結果陣列鍵名
'params' => array(':uid'=>Yii::app()->user->id),
'group' => 'doingO.status_id',
'having' => 'COUNT(*) > 1',
),
self::STAT:聚合計數不載入模型'commentCount' => array(self::STAT, 'Comment', 'post_id'),
'categoryCount' => array(self::STAT, 'Category', 'post_category(post_id, category_id)'),
// 帶條件
'doingOutsourceCount' => array(
self::STAT, 'Outsource', 'outsource_user(user_id, outsource_id)',
'condition' => "outsource.status_id IN (" . Status::ASSIGNED . ", " . Status::STARTED . ")"
),
用法:
$post->commentCount; // 延遲
$posts = Post::model()->with('commentCount', 'categoryCount')->findAll(); // 急切
with() 急切加載,避免 N+1CDbCriteria + with(),而非在關係定義寫死Post::model()->with(array('user'=>false))->findAll()熟練掌握 Yii 關聯查詢,能讓數據庫操作代碼減少 80% 以上,同時保持極高的可讀性與性能。