關聯模型

關係實際上就是關係模型在某一時刻的狀態或內容。也就是說,關係模式是型,關係是它的值。關係模式是靜態的、穩定的,而關係是動態的、隨時間不斷變化的,因為關係操作在不斷地更新著資料庫中的數據。

基本介紹

  • 中文名:關聯模型
  • 外文名:Association Model
  • 套用領域:計算機等
  • 涉及軟體:ThinkPHP
  • 關聯關係:一對一、一對多、多對多
  • 套用對象:數據表
簡介,關聯定義,關聯定義的格式,HAS_ONE,BELONGS_TO,HAS_MANY,MANY_TO_MANY,查詢,關聯操作,創建數據表,關聯定義,關聯寫入,更新關聯數據,關聯刪除,

簡介

通常我們所說的關聯關係包括下面三種:
  • 一對一關聯 :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_ONEBELONGS_TOHAS_MANYMANY_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

HAS_ONE關聯表示當前模型擁有一個子對象,例如,每個員工都有一個人事檔案。我們可以建立一個用戶模型UserModel,並且添加如下關聯定義:
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' => '[email protected]',
'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為例:
  • // 部門表
  • // 用戶表
  • // 用戶檔案表
  • // 銀行卡表
  • // 項目組表
  • // 用戶-項目組

關聯定義

分別來給數據表定義對應的模型,這裡關鍵是用戶模型的定義,因為我們以用戶表為核心來定義和使用關聯,所以其他模型中無需再定義關聯關係。
[php]view plaincopyprint?
classUserModelextendsModel
{
protected$_link=array(
‘Profile’=>HAS_ONE,
‘Dept’=>BELONGS_TO,
‘Card’=>HAS_MANY,
‘Group’=>MANY_TO_MANY,
);
}
上面的關聯定義,我們採用了最簡潔的定義方式,也就是所有規則都按照系統的默認規則進行。這些規則包括主鍵、外鍵、表名的規範。
完整的關聯定義可以寫成
[php]view plaincopyprint?
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操作方法裡面添加
[php]view plaincopyprint?
$User=D(”User”);
$User->name=‘thinkphp’;
$User->dept_id=1;
$User->Profile=array(
‘email’=>’[email protected]’,
‘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();
    能夠顯示出完整的含有關聯數據的數據集。

更新關聯數據

[php]view plaincopyprint?
$user[‘id’]=1;
$user['name']=‘tp’;
$user['Profile']['email']=‘[email protected]’;
$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);

    相關詞條

    熱門詞條

    聯絡我們