官網原文:http://www.yiiframework.com/doc/guide/1.1/zh_cn/database.arr
官網中后半段為英文,而且中文的內容比英文少一些,先放到這里,之后有時間再翻譯。
我們已經了解了怎樣使用 Active Record (AR) 從單個數據表中獲取數據。 在本節中,我們講解怎樣使用 AR 連接多個相關數據表并取回關聯(join)后的數據集。
為了使用關系型 AR,我們建議在需要關聯的表中定義主鍵-外鍵約束。這些約束可以幫助保證相關數據的一致性和完整性。
為簡單起見,我們使用如下所示的實體-關系(ER)圖中的數據結構演示此節中的例子。

信息:對外鍵約束的支持在不同的 DBMS 中是不一樣的。 SQLite < 3.6.19 不支持外鍵約束,但你依然可以在建表時聲明約束。
1. 聲明關系在我們使用 AR 執行關聯查詢之前,我們需要讓 AR 知道一個 AR 類是怎樣關聯到另一個的。
兩個 AR 類之間的關系直接通過 AR 類所代表的數據表之間的關系相關聯。 從數據庫的角度來說,表 A 和 B 之間有三種關系:一對多(one-to-many,例如tbl_user和tbl_post),一對一( one-to-one 例如tbl_user和tbl_PRofile)和 多對多(many-to-many 例如tbl_category和tbl_post)。 在 AR 中,有四種關系:
BELONGS_TO(屬于): 如果表 A 和 B 之間的關系是一對多,則 表 B 屬于 表 A (例如Post屬于User);
HAS_MANY(有多個): 如果表 A 和 B 之間的關系是一對多,則 A 有多個 B (例如User有多個Post);
HAS_ONE(有一個): 這是HAS_MANY的一個特例,A 最多有一個 B (例如User最多有一個Profile);
MANY_MANY: 這個對應于數據庫中的 多對多 關系。 由于多數 DBMS 不直接支持 多對多 關系,因此需要有一個關聯表將 多對多 關系分割為 一對多 關系。 在我們的示例數據結構中,tbl_post_category就是用于此目的的。在 AR 術語中,我們可以解釋MANY_MANY為BELONGS_TO和HAS_MANY的組合。 例如,Post屬于多個(belongs to many)Category,Category有多個(has many)Post.
AR 中定義關系需要覆蓋CActiveRecord中的relations()方法。此方法返回一個關系配置數組。每個數組元素通過如下格式表示一個單一的關系。
'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...additional options)其中VarName是關系的名字;RelationType指定關系類型,可以是一下四個常量之一:self::BELONGS_TO,self::HAS_ONE,self::HAS_MANYandself::MANY_MANY;ClassName是此 AR 類所關聯的 AR 類的名字;ForeignKey指定關系中使用的外鍵(一個或多個)。額外的選項可以在每個關系的最后指定(稍后詳述)。
以下代碼演示了怎樣定義User和Post類的關系:
class Post extends CActiveRecord{ ...... public function relations() { return array( 'author'=>array(self::BELONGS_TO, 'User', 'author_id'), 'categories'=>array(self::MANY_MANY, 'Category', 'tbl_post_category(post_id, category_id)'), ); }} class User extends CActiveRecord{ ...... public function relations() { return array( 'posts'=>array(self::HAS_MANY, 'Post', 'author_id'), 'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'), ); }}信息:外鍵可能是復合的,包含兩個或更多個列。 這種情況下,我們應該將這些外鍵名字鏈接,中間用空格或逗號分割。對于MANY_MANY關系類型, 關聯表的名字必須也必須在外鍵中指定。例如,Post中的categories關系由外鍵tbl_post_category(post_id, category_id)指定。
AR 類中的關系定義為每個關系向類中隱式添加了一個屬性。在一個關聯查詢執行后,相應的屬性將將被以關聯的 AR 實例填充。 例如,如果$author代表一個UserAR 實例, 我們可以使用$author->posts訪問其關聯的Post實例。
執行關聯查詢最簡單的方法是讀取一個 AR 實例中的關聯屬性。如果此屬性以前沒有被訪問過,則一個關聯查詢將被初始化,它將兩個表關聯并使用當前 AR 實例的主鍵過濾。 查詢結果將以所關聯 AR 類的實例的方式保存到屬性中。這就是傳說中的懶惰式加載(lazy loading,也可譯為 遲加載)方式,例如,關聯查詢只在關聯的對象首次被訪問時執行。 下面的例子演示了怎樣使用這種方式:
// 獲取 ID 為 10 的帖子$post=Post::model()->findByPk(10);// 獲取帖子的作者(author): 此處將執行一個關聯查詢。$author=$post->author;
信息:如果關系中沒有相關的實例,則相應的屬性將為 null 或一個空數組。BELONGS_TO和HAS_ONE關系的結果是 null,HAS_MANY和MANY_MANY的結果是一個空數組。 注意,HAS_MANY和MANY_MANY關系返回對象數組,你需要在訪問任何屬性之前先遍歷這些結果。 否則,你可能會收到 "Trying to get property of non-object(嘗試訪問非對象的屬性)" 錯誤。
懶惰式加載用起來很方便,但在某些情況下并不高效。如果我們想獲取N個帖子的作者,使用這種懶惰式加載將會導致執行N個關聯查詢。 這種情況下,我們應該改為使用渴求式加載(eager loading)方式。
渴求式加載方式會在獲取主 AR 實例的同時獲取關聯的 AR 實例。 這是通過在使用 AR 中的find或findAll方法時配合使用 with 方法完成的。例如:
$posts=Post::model()->with('author')->findAll();上述代碼將返回一個Post實例的數組。與懶惰式加載方式不同,在我們訪問每個Post實例中的author屬性之前,它就已經被關聯的User實例填充了。 渴求式加載通過一個關聯查詢返回所有帖子及其作者,而不是對每個帖子執行一次關聯查詢。
我們可以在with()方法中指定多個關系名字,渴求式加載將一次性全部取回他們。例如,如下代碼會將帖子連同其作者和分類一并取回。
$posts=Post::model()->with('author','categories')->findAll();我們也可以實現嵌套的渴求式加載。像下面這樣, 我們傳遞一個分等級的關系名表達式到with()方法,而不是一個關系名列表:
$posts=Post::model()->with( 'author.profile', 'author.posts', 'categories')->findAll();
上述示例將取回所有帖子及其作者和所屬分類。它還同時取回每個作者的簡介(author.profile)和帖子(author.posts)。
從版本 1.1.0 開始,渴求式加載也可以通過指定CDbCriteria::with的屬性執行,就像下面這樣:
$criteria=new CDbCriteria;$criteria->with=array( 'author.profile', 'author.posts', 'categories',);$posts=Post::model()->findAll($criteria);
或者
$posts=Post::model()->findAll(array( 'with'=>array( 'author.profile', 'author.posts', 'categories', ));3. 關系型查詢選項
我們提到在關系聲明時可以指定附加的選項。這些 名-值 對形式的選項用于自定義關系型查詢。概括如下:
select: 關聯的 AR 類中要選擇(select)的列的列表。 默認為 '*',即選擇所有列。此選項中的列名應該是已經消除歧義的。
condition: 即WHERE條件。默認為空。此選項中的列名應該是已經消除歧義的。
params: 要綁定到所生成的 SQL 語句的參數。應該以 名-值 對數組的形式賦值。此選項從 1.0.3 版起有效。
on: 即ON語句。此處指定的條件將會通過AND操作符附加到 join 條件中。此選項中的列名應該是已經消除歧義的。 此選項不會應用到MANY_MANY關系中。此選項從 1.0.2 版起有效。
order: 即ORDER BY語句。默認為空。 此選項中的列名應該是已經消除歧義的。
with: a list of child related objects that should be loaded together with this object. Be aware that using this option inappropriately may form an infinite relation loop.
joinType: type of join for this relationship. It defaults toLEFT OUTER JOIN.
alias: the alias for the table associated with this relationship. It defaults to null, meaning the table alias is the same as the relation name.
together: whether the table associated with this relationship should be forced to join together with the primary table and other tables. This option is only meaningful forHAS_MANYandMANY_MANYrelations. If this option is set false, the table associated with theHAS_MANYorMANY_MANYrelation will be joined with the primary table in a separate SQL query, which may improve the overall query performance since less duplicated data is returned. If this option is set true, the associated table will always be joined with the primary table in a single SQL query, even if the primary table is paginated. If this option is not set, the associated table will be joined with the primary table in a single SQL query only when the primary table is not paginated. For more details, see the section "Relational Query Performance".
join: the extraJOINclause. It defaults to empty. This option has been available since version 1.1.3.
group: theGROUP BYclause. It defaults to empty. Column names referenced in this option should be disambiguated.
having: theHAVINGclause. It defaults to empty. Column names referenced in this option should be disambiguated.
index: the name of the column whose values should be used as keys of the array that stores related objects. Without setting this option, an related object array would use zero-based integer index. This option can only be set forHAS_MANYandMANY_MANYrelations.
scopes: scopes to apply. In case of a single scope can be used like'scopes'=>'scopeName', in case of multiple scopes can be used like'scopes'=>array('scopeName1','scopeName2'). This option has been available since version 1.1.9.
In addition, the following options are available for certain relationships during lazy loading:
新聞熱點
疑難解答