-
關聯模型
鎖定
關聯模型簡介
- 一對一關聯 :ONE_TO_ONE,包括HAS_ONE 和 BELONGS_TO
- 一對多關聯 :ONE_TO_MANY,包括HAS_MANY 和 BELONGS_TO
- 多對多關聯 :MANY_TO_MANY
關聯關係必然有一個參照表,例如:
- 有一個員工檔案管理系統項目,這個項目要包括下面的一些數據表:基本信息表、員工檔案表、部門表、項目組表、銀行卡表(用來記錄員工的銀行卡資料)。
- 這些數據表之間存在一定的關聯關係,我們以員工基本信息表為參照來分析和其他表之間的關聯:
- 每個員工必然有對應的員工檔案資料,所以屬於HAS_ONE關聯;
- 每個員工必須屬於某個部門,所以屬於BELONGS_TO關聯;
- 每個員工可以有多個銀行卡,但是每張銀行卡只可能屬於一個員工,因此屬於HAS_MANY關聯;
- 每個員工可以同時在多個項目組,每個項目組同時有多個員工,因此屬於MANY_TO_MANY關聯;
- 分析清楚數據表之前的關聯關係後,我們才可以進行關聯定義和關聯操作。
關聯模型關聯定義
ThinkPHP可以很輕鬆的完成數據表的關聯CURD操作,目前支持的關聯關係包括下面四種:
HAS_ONE、BELONGS_TO、HAS_MANY和MANY_TO_MANY。
一個模型根據業務模型的複雜程度可以同時定義多個關聯,不受限制,所有的關聯定義都統一在模型類的 $_link 成員變量裏面定義,並且可以支持動態定義。
要支持關聯操作,模型類必須繼承Think\Model\RelationModel類。
關聯模型關聯定義的格式
namespace Home\Model; use Think\Model\RelationModel; class UserModel extends RelationModel{ protected $_link = array( '關聯1' => array( '關聯屬性1' => '定義', '關聯屬性N' => '定義', ), '關聯2' => array( '關聯屬性1' => '定義', '關聯屬性N' => '定義', ), '關聯3' => HAS_ONE, // 快捷定義 ... ); } |
下面我們首先來分析下各個關聯方式的定義:
關聯模型HAS_ONE
namespace Home\Model; use Think\Model\RelationModel; class UserModel extends RelationModel{ protected $_link = array( 'Profile'=> self::HAS_ONE, ); } |
上面是最簡單的方式,表示其遵循了系統內置的數據庫規範,完整的定義方式是:
namespace Home\Model; use Think\Model\RelationModel; class UserModel extends RelationModel{ protected $_link = array( 'Profile'=>array( 'mapping_type' => self::HAS_ONE, 'class_name' => 'Profile', // 定義更多的關聯屬性 …… ), ); } |
關聯HAS_ONE支持的關聯屬性有:
- mapping_type :關聯類型這個在HAS_ONE 關聯裏面必須使用HAS_ONE 常量定義。
- class_name :要關聯的模型類名例如,class_name 定義為Profile的話則表示和另外的Profile模型類關聯,這個Profile模型類是無需定義的,系統會自動定位到相關的數據表進行關聯。
- mapping_name :關聯的映射名稱,用於獲取數據用該名稱不要和當前模型的字段有重複,否則會導致關聯數據獲取的衝突。如果mapping_name沒有定義的話,會取class_name的定義作為mapping_name。如果class_name也沒有定義,則以數組的索引作為mapping_name。
- foreign_key : 關聯的外鍵名稱外鍵的默認規則是當前數據對象名稱_id,例如: UserModel對應的可能是表think_user (注意:think只是一個表前綴,可以隨意配置) 那麼think_user表的外鍵默認為 user_id,如果不是,就必須在定義關聯的時候顯式定義 foreign_key 。
- condition : 關聯條件關聯查詢的時候會自動帶上外鍵的值,如果有額外的查詢條件,可以通過定義關聯的condition屬性。
- mapping_fields : 關聯要查詢的字段默認情況下,關聯查詢的關聯數據是關聯表的全部字段,如果只是需要查詢個別字段,可以定義關聯mapping_fields屬性。
- as_fields :直接把關聯的字段值映射成數據對象中的某個字段這個特性是ONE_TO_ONE 關聯特有的,可以直接把關聯數據映射到數據對象中,而不是作為一個關聯數據。當關聯數據的字段名和當前數據對象的字段名稱有衝突時,還可以使用映射定義。
關聯模型BELONGS_TO
Belongs_to 關聯表示當前模型從屬於另外一個父對象,例如每個用户都屬於一個部門。我們可以做如下關聯定義。
'Dept' => self::BELONGS_TO
完整方式定義為:
'Dept' => array( 'mapping_type' => self::BELONGS_TO, 'class_name' => 'Dept', 'foreign_key' => 'userId', 'mapping_name' => 'dept', // 定義更多的關聯屬性 …… ), |
支持的關聯屬性
屬性 | 描述 |
---|---|
class_name | 要關聯的模型類名 |
mapping_name | 關聯的映射名稱,用於獲取數據用 該名稱不要和當前模型的字段有重複,否則會導致關聯數據獲取的衝突。 |
foreign_key | 關聯的外鍵名稱 |
mapping_fields | 關聯要查詢的字段 |
condition | 關聯條件 |
parent_key | 自引用關聯的關聯字段 默認為parent_id 自引用關聯是一種比較特殊的關聯,也就是關聯表就是當前表。 |
as_fields | 直接把關聯的字段值映射成數據對象中的某個字段 |
關聯模型HAS_MANY
HAS_MANY 關聯表示當前模型擁有多個子對象,例如每個用户有多篇文章,我們可以這樣來定義:
'Article' => self::HAS_MANY
完整定義方式為:
'Article' => array( 'mapping_type' => self::HAS_MANY, 'class_name' => 'Article', 'foreign_key' => 'userId', 'mapping_name' => 'articles', 'mapping_order' => 'create_time desc', // 定義更多的關聯屬性 …… ), |
關聯屬性有:
屬性 | 描述 |
---|---|
class_name | 要關聯的模型類名 |
mapping_name | 關聯的映射名稱,用於獲取數據用 該名稱不要和當前模型的字段有重複,否則會導致關聯數據獲取的衝突。 |
foreign_key | 關聯的外鍵名稱 |
parent_key | 自引用關聯的關聯字段 默認為parent_id |
condition | 關聯條件 關聯查詢的時候會自動帶上外鍵的值,如果有額外的查詢條件,可以通過定義關聯的condition屬性。 |
mapping_fields | 關聯要查詢的字段 默認情況下,關聯查詢的關聯數據是關聯表的全部字段,如果只是需要查詢個別字段,可以定義關聯的mapping_fields屬性。 |
mapping_limit | 關聯要返回的記錄數目 |
mapping_order | 關聯查詢的排序 |
外鍵的默認規則是當前數據對象名稱_id,例如:UserModel對應的可能是表think_user (注意:think只是一個表前綴,可以隨意配置) 那麼think_user表的外鍵默認為 user_id,如果不是,就必須在定義關聯的時候定義 foreign_key 。
關聯模型MANY_TO_MANY
MANY_TO_MANY 關聯表示當前模型可以屬於多個對象,而父對象則可能包含有多個子對象,通常兩者之間需要一箇中間表類約束和關聯。例如每個用户可以屬於多個組,每個組可以有多個用户:
'Group' => self::MANY_TO_MANY
完整定義方式為:
'Group' => array( 'mapping_type' => self::MANY_TO_MANY, 'class_name' => 'Group', 'mapping_name' => 'groups', 'foreign_key' => 'userId', 'relation_foreign_key' => 'groupId', 'relation_table' => 'think_group_user' //此處應顯式定義中間表名稱,且不能使用C函數讀取表前綴 ) |
關聯屬性定義有:
屬性 | 描述 |
---|---|
class_name | 要關聯的模型類名 |
mapping_name | 關聯的映射名稱,用於獲取數據用 該名稱不要和當前模型的字段有重複,否則會導致關聯數據獲取的衝突。 |
foreign_key | 關聯的外鍵名稱 外鍵的默認規則是當前數據對象名稱_id |
relation_foreign_key | 關聯表的外鍵名稱 默認的關聯表的外鍵名稱是表名_id |
mapping_limit | 關聯要返回的記錄數目 |
mapping_order | 關聯查詢的排序 |
relation_table | 多對多的中間關聯表名稱 |
多對多的中間表默認表規則是:數據表前綴_關聯操作的主表名_關聯表名
關聯模型查詢
由於性能問題,新版取消了自動關聯查詢機制,而統一使用relation方法進行關聯操作,relation方法不但可以啓用關聯還可以控制局部關聯操作,實現了關聯操作一切盡在掌握之中。
$User = D("User");$user = $User->relation(true)->find(1);
輸出$user結果可能是類似於下面的數據:
array( 'id' => 1, 'account' => 'ThinkPHP', 'password' => '123456', 'Profile' => array( 'email' => 'liu21st@gmail.com', 'nickname' => '流年', ), ) |
我們可以看到,用户的關聯數據已經被映射到數據對象的屬性裏面了。其中Profile就是關聯定義的mapping_name屬性。
定義:
- 'as_fields' => 'email,nickname:username',表示關聯表的nickname字段映射成當前數據對象的username字段。默認會把所有定義的關聯數據都查詢出來,有時候我們並不希望這樣,就可以給relation方法傳入參數來控制要關聯查詢的。
- $User = D("User");$user = $User->relation('Profile')->find(1);關聯查詢一樣可以支持select方法,如果要查詢多個數據,並同時獲取相應的關聯數據,可以改成:
- $User = D("User");$list = $User->relation(true)->Select();如果希望在完成的查詢基礎之上 再進行關聯數據的查詢,可以使用
- $User = D("User");$user = $User->find(1);// 表示對當前查詢的數據對象進行關聯數據獲取$profile = $User->relationGet("Profile");事實上,除了當前的參考模型User外,其他的關聯模型是不需要創建的。
關聯模型關聯操作
下面我們以一個實例來講述關聯操作的簡單用法,由於關聯操作定義複雜,這裏只是講述一般的情況。我們以用户表為核心,來描述如何使用表的關聯操作。假設存在如下的關聯情況:
- 每個用户有一個檔案表是HAS_ONE關聯;
- 每個用户屬於一個部門是BELONGS_TO關聯;
- 每個用户有多張銀行卡是HAS_MANY關聯;
- 每個用户可能屬於多個項目組,每個項目組也有多個用户是MANY_TO_MANY關聯。
關聯模型創建數據表
我們首先來創建數據表,以MySQL為例:
- // 部門表
- // 用户表
- // 用户檔案表
- // 銀行卡表
- // 項目組表
- // 用户-項目組
- 表
關聯模型關聯定義
分別來給數據表定義對應的模型,這裏關鍵是用户模型的定義,因為我們以用户表為核心來定義和使用關聯,所以其他模型中無需再定義關聯關係。
classUserModelextendsModel { protected$_link=array( ‘Profile’=>HAS_ONE, ‘Dept’=>BELONGS_TO, ‘Card’=>HAS_MANY, ‘Group’=>MANY_TO_MANY, ); } |
上面的關聯定義,我們採用了最簡潔的定義方式,也就是所有規則都按照系統的默認規則進行。這些規則包括主鍵、外鍵、表名的規範。
完整的關聯定義可以寫成:
classUserModelextendsModel { protected$_link=array( ‘Profile’=>array( ‘mapping_type’=>HAS_ONE, ‘mapping_name’=>’Profile’, ‘class_name’=>’Profile’, ‘foreign_key’=>’user_id’, ), ‘Dept’=>array( ‘mapping_type’=>BELONGS_TO, ‘mapping_name’=>’Dept’, ‘class_name’=>’Dept’, ‘foreign_key’=>’dept_id’, ), ‘Card’=>array( ‘mapping_type’=>HAS_MANY, ‘mapping_name’=>’Card’, ‘class_name’=>’Card’, ‘foreign_key’=>’user_id’, ), ‘Group’=>array( ‘mapping_type’=>MANY_TO_MANY, ‘mapping_name’=>’Group’, ‘class_name’=>’Group’, ‘foreign_key’=>’user_id’, ‘relation_foreign_key’=>’group_id’, ‘relation_table’=>’think_user_group’, ), ); } |
如果要給關聯定義增加可選的屬性,則必須採用完整定義的方式。
其中Profile Dept Card Group 分別是其他幾個模型的名稱,定義如下:
- class ProfileModel extends Model {}
- class DeptModel extends Model {}
- class CardModel extends Model {}
- class GroupModel extends Model {}
為了演示的方便,我們首先給部門表和項目組表增加一些數據:
INSERT INTO `think_dept` (`id`, `name`) VALUES (1, ‘開發部’),(2, ‘銷售部’) ,(3, ‘財務部’);
INSERT INTO `think_group` (`id`, `name`) VALUES (1, ‘項目組1′),(2, ‘項目組2′) ,(3, ‘項目組3′);
關聯模型關聯寫入
首先演示關聯寫入,我們創建一個IndexAction用於演示操作,記得在項目配置文件裏面定義好數據庫的連接信息。
在IndexAction的index操作方法裏面添加
$User=D(”User”); $User->name=‘thinkphp’; $User->dept_id=1; $User->Profile=array( ‘email’=>’liu21st@gmail.com’, ‘nickname’=>’流年’, ); $User->Card=array( array(‘id’=>1.’card’=>’12345678′), array(‘id’=>2,’card’=>’88888888′), ); $User->Group=array( array(’id’=>1), array(’id’=>2), ); $User->add(”,true); |
在執行User模型的add方法的同時,我們已經寫入了think_profile、think_card和think_user_group三個表的數據,BELONGS_TO關聯關係是不會自動寫入的。
如果我們在模型裏面設置了autoAddRelations屬性為True的話,使用
- $User->add();方法即可同時進行關聯寫入。為了驗證關聯數據是否已經寫入,我們現在來使用關聯查詢把相關的數據查出來。
- $user = $User->relation(true)->find(1);Dump($user);可以看到輸出的結果,把User模型關聯的數據都顯示出來了。如果我們只希望獲取某個關聯數據,可以使用
- $user = $User->relation(‘Profile’)->find(1);表示只是獲取關聯的用户檔案數據。數據集的查詢也可以支持關聯查詢,使用
- $user = $User->relation(true)->findAll();能夠顯示出完整的含有關聯數據的數據集。
關聯模型更新關聯數據
$user[‘id’]=1; $user['name']=‘tp’; $user['Profile']['email']=‘thinkphp@qq.com’; $user['Card']=array( array(’id’=>1,’card’=>’66666666′), array(’id’=>2,’card’=>’77777777′), ); $user[‘Group’]=array( array(’id’=>1), array(’id’=>3), ); $User->save($user,’id=1′,true); |
注意關聯更新的時候一定要包含主鍵數據。
關聯模型關聯刪除
$User->deleteById(2,true);
- 參考資料
-
- 1. ThinkPHP3.2完全開發手冊 .ThinkPHP3.2完全開發手冊[引用日期2017-08-10]
- 詞條統計
-
- 瀏覽次數:次
- 編輯次數:2次歷史版本
- 最近更新: 孔呃呃呃