上一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(10) - 自動載入篇
下一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(12) - 分頁篇
開始之前
早在查詢 - JOIN篇內(nèi)我們就已經(jīng)基于數(shù)據(jù)庫 JOIN 命令 實(shí)現(xiàn)了相關(guān)的實(shí)用函數(shù), 但美中不足的是, 調(diào)回的數(shù)據(jù)僅限于讀 (Read Only), 無法對副表記錄進(jìn)行寫操作, 因此本期將會對此做出補(bǔ)充, 加強(qiáng)封裝包的可用性.
本期核心概念基于Laravel原代碼, 但僅實(shí)現(xiàn)最常用的3種函數(shù), 其余請自行實(shí)現(xiàn).
Builder.php
- hasOne - 一對一關(guān)聯(lián)
public function hasOne($model, $foregin, $primary) {
// 實(shí)例化 $model 中所代表的數(shù)據(jù)庫表, 但必須先在 model 文件夾內(nèi)設(shè)置該表的模型
$new = new $model();
// 獲取數(shù)據(jù)庫表的名字
$table = $new->getTable();
// 將當(dāng)前 model 轉(zhuǎn)為上方實(shí)例化的 Model
$this->model = new $model();
// join 函數(shù)連接副表返回數(shù)據(jù)
return $this->select(["$table.*"])->join($table, $foregin, $primary);
}
- hasMany - 一對多關(guān)聯(lián)
// 實(shí)現(xiàn)原理同 hasOne()
public function hasMany($model, $foregin, $primary) {
$new = new $model();
$table = $new->getTable();
$this->model = new $model();
return $this->select(["$table.*"])->join($table, $foregin, $primary);
}
- hasManyThrough - 一對多間接關(guān)聯(lián)
/**
* 兩個關(guān)聯(lián)的表之間相隔著一個收錄了它們的主鍵作為副鍵的表
* @param Model $model1 中間的表
* @param Model $model2 最終想查詢的表
* @param string $foregin1 當(dāng)前表的主鍵在中間表內(nèi)作為副鍵的字段名
* @param string $primary1 當(dāng)前表的主鍵
* @param string $foregin2 中間表的主鍵在最終表內(nèi)作為副鍵的字段名
* @param string $primary2 中間表的主鍵
* @return Builder Builder實(shí)例
*/
public function hasManyThrough($model1, $model2, $foregin1, $foregin2, $primary1, $primary2) {
// 帶入當(dāng)前實(shí)例的 model 待用
$model = $this->model;
// 實(shí)例化 model1
$model1 = new $model1();
// 實(shí)例化 model2
$model2 = new $model2();
// 獲取 model1 的表名
$table1 = $model1->getTable();
// 獲取 model2 的表名
$table2 = $model2->getTable();
// 由于最終想操作的實(shí)例是 model2, 設(shè)置 model2 為 實(shí)例的 model
$this->model = $model2;
// join 函數(shù)連接三個表
return $this->select(["$table2.*"])
->join($table1, $foregin1, $primary1)
->join($table2, $foregin2, "$table1.$primary2");
}
上方的函數(shù)如果看不懂沒關(guān)系, 下方的例子可以簡單直白的告訴你這些函數(shù)有什么用
例子
- 一對一
一部電影僅有一個語種(場景需要,勿糾結(jié)), 電影和語種的關(guān)系屬于一對一
首先打開 Film.php (數(shù)據(jù)庫表, Film 所代表的模型), 加入函數(shù)如下
// 函數(shù)名可以隨意取, 但為了方便辨識, 采用了副表的表名
public function language() {
// 調(diào)用 hasOne 函數(shù), 首參是副表的模型名稱, 2參是副鍵名稱, 3參是主鍵名稱
return $this->hasOne('Language', 'language_id', 'language_id');
}
接下來嘗試使用
$film = Film::find(1);
dd($film->language);
注意到了嗎? 調(diào)用函數(shù)的方式竟然是以變量的形式,這是為了與跟進(jìn)一步的篩選明顯地區(qū)分開
先看返回結(jié)果
object(Language)[30]
public 'language_id' => string '1' (length=1)
public 'name' => string 'English' (length=7)
public 'last_update' => string '2006-02-15 05:02:19' (length=19)
很多時候我們并不只是單純地連接副表查看結(jié)果, 打個比方, 我們想知道該電影是否中文, 如果是, 分享給朋友, 那應(yīng)該怎么做呢?
$film = Film::find(1);
if($film->language->name=='Chinese')
shareToFirends();
這段邏輯沒毛病, 但還有另一種實(shí)現(xiàn)方式
$film = Film::find(1);
$chinesFilm = $film->language()->where('name', 'Chinese')->first();
if($chinesFilm)
shareToFirends();
以上例子說明了關(guān)聯(lián)函數(shù)是可以再操作的, 只要用函數(shù)的方式來調(diào)用
- 一對多
一個演員可以接演多部電影, 所以演員和電影的關(guān)系屬于一對多
打開 Actor.php (數(shù)據(jù)庫表, Actor 所代表的模型), 加入函數(shù)如下
// 由于返回的是多條數(shù)據(jù), 建議函數(shù)名是副表模型的復(fù)數(shù)
public function filmActors() {
// 3個參數(shù)的作用參照 hasOne 函數(shù)
return $this->hasMany('FilmActor', 'actor_id', 'actor_id');
}
用法同 hasOne 函數(shù)
$actor = Actor::find(1);
dd($actor->filmActors);
返回結(jié)果
array (size=19)
0 =>
object(FilmActor)[48]
public 'actor_id' => string '1' (length=1)
public 'film_id' => string '1' (length=1)
public 'last_update' => string '2006-02-15 05:05:03' (length=19)
1 =>
object(FilmActor)[52]
public 'actor_id' => string '1' (length=1)
public 'film_id' => string '23' (length=2)
public 'last_update' => string '2006-02-15 05:05:03' (length=19)
2 =>
object(FilmActor)[56]
public 'actor_id' => string '1' (length=1)
public 'film_id' => string '25' (length=2)
public 'last_update' => string '2006-02-15 05:05:03' (length=19)
3 =>
object(FilmActor)[60]
public 'actor_id' => string '1' (length=1)
public 'film_id' => string '106' (length=3)
public 'last_update' => string '2006-02-15 05:05:03' (length=19)
......
- 一對多間接關(guān)聯(lián)
上一個例子的返回結(jié)果并沒有帶出什么實(shí)質(zhì)訊息, 這是一種常見的數(shù)據(jù)庫結(jié)構(gòu), 一對多的關(guān)系并不直接關(guān)聯(lián), 而是通過中間表來儲存
Actor -> FilmActor -> Film
打開 Actor.php, 加上函數(shù)如下
public function films() {
// 參數(shù)介紹請參照函數(shù)的附帶解釋
return $this->hasManyThrough('FilmActor', 'Film' , 'actor_id', 'film_id', 'actor_id', 'film_id');
}
調(diào)用方式
$actor = Actor::find(1);
dd($actor->films);
返回結(jié)果
array (size=19)
0 =>
object(Film)[49]
public 'film_id' => string '1' (length=1)
public 'title' => string 'ACADEMY DINOSAUR' (length=16)
public 'description' => string 'A Epic Drama of a Feminist And a Mad Scientist who must Battle a Teacher in The Canadian Rockies' (length=96)
public 'release_year' => string '2006' (length=4)
public 'language_id' => string '1' (length=1)
public 'original_language_id' => null
public 'rental_duration' => string '6' (length=1)
public 'rental_rate' => string '0.99' (length=4)
public 'length' => string '86' (length=2)
public 'replacement_cost' => string '20.99' (length=5)
public 'rating' => string 'PG' (length=2)
public 'special_features' => string 'Deleted Scenes,Behind the Scenes' (length=32)
public 'last_update' => string '2006-02-15 05:03:42' (length=19)
1 =>
object(Film)[53]
public 'film_id' => string '23' (length=2)
public 'title' => string 'ANACONDA CONFESSIONS' (length=20)
public 'description' => string 'A Lacklusture Display of a Dentist And a Dentist who must Fight a Girl in Australia' (length=83)
public 'release_year' => string '2006' (length=4)
public 'language_id' => string '1' (length=1)
public 'original_language_id' => null
public 'rental_duration' => string '3' (length=1)
public 'rental_rate' => string '0.99' (length=4)
public 'length' => string '92' (length=2)
public 'replacement_cost' => string '9.99' (length=4)
public 'rating' => string 'R' (length=1)
public 'special_features' => string 'Trailers,Deleted Scenes' (length=23)
public 'last_update' => string '2006-02-15 05:03:42' (length=19)
......
以上就是本期的所有內(nèi)容了, 實(shí)在無法評價自己的文筆, 如果看不懂可以在下方留言
完整代碼
源代碼放在coding.net里, 自己領(lǐng)
上一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(10) - 自動載入篇
下一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(12) - 分頁篇