上一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(10) - 自動載入篇
下一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(12) - 分頁篇
開始之前
早在查詢 - JOIN篇內我們就已經基于數(shù)據(jù)庫 JOIN 命令 實現(xiàn)了相關的實用函數(shù), 但美中不足的是, 調回的數(shù)據(jù)僅限于讀 (Read Only), 無法對副表記錄進行寫操作, 因此本期將會對此做出補充, 加強封裝包的可用性.
本期核心概念基于Laravel原代碼, 但僅實現(xiàn)最常用的3種函數(shù), 其余請自行實現(xiàn).
Builder.php
- hasOne - 一對一關聯(lián)
public function hasOne($model, $foregin, $primary) {
// 實例化 $model 中所代表的數(shù)據(jù)庫表, 但必須先在 model 文件夾內設置該表的模型
$new = new $model();
// 獲取數(shù)據(jù)庫表的名字
$table = $new->getTable();
// 將當前 model 轉為上方實例化的 Model
$this->model = new $model();
// join 函數(shù)連接副表返回數(shù)據(jù)
return $this->select(["$table.*"])->join($table, $foregin, $primary);
}
- hasMany - 一對多關聯(lián)
// 實現(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 - 一對多間接關聯(lián)
/**
* 兩個關聯(lián)的表之間相隔著一個收錄了它們的主鍵作為副鍵的表
* @param Model $model1 中間的表
* @param Model $model2 最終想查詢的表
* @param string $foregin1 當前表的主鍵在中間表內作為副鍵的字段名
* @param string $primary1 當前表的主鍵
* @param string $foregin2 中間表的主鍵在最終表內作為副鍵的字段名
* @param string $primary2 中間表的主鍵
* @return Builder Builder實例
*/
public function hasManyThrough($model1, $model2, $foregin1, $foregin2, $primary1, $primary2) {
// 帶入當前實例的 model 待用
$model = $this->model;
// 實例化 model1
$model1 = new $model1();
// 實例化 model2
$model2 = new $model2();
// 獲取 model1 的表名
$table1 = $model1->getTable();
// 獲取 model2 的表名
$table2 = $model2->getTable();
// 由于最終想操作的實例是 model2, 設置 model2 為 實例的 model
$this->model = $model2;
// join 函數(shù)連接三個表
return $this->select(["$table2.*"])
->join($table1, $foregin1, $primary1)
->join($table2, $foregin2, "$table1.$primary2");
}
上方的函數(shù)如果看不懂沒關系, 下方的例子可以簡單直白的告訴你這些函數(shù)有什么用
例子
- 一對一
一部電影僅有一個語種(場景需要,勿糾結), 電影和語種的關系屬于一對一
首先打開 Film.php (數(shù)據(jù)庫表, Film 所代表的模型), 加入函數(shù)如下
// 函數(shù)名可以隨意取, 但為了方便辨識, 采用了副表的表名
public function language() {
// 調用 hasOne 函數(shù), 首參是副表的模型名稱, 2參是副鍵名稱, 3參是主鍵名稱
return $this->hasOne('Language', 'language_id', 'language_id');
}
接下來嘗試使用
$film = Film::find(1);
dd($film->language);
注意到了嗎? 調用函數(shù)的方式竟然是以變量的形式,這是為了與跟進一步的篩選明顯地區(qū)分開
先看返回結果
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)
很多時候我們并不只是單純地連接副表查看結果, 打個比方, 我們想知道該電影是否中文, 如果是, 分享給朋友, 那應該怎么做呢?
$film = Film::find(1);
if($film->language->name=='Chinese')
shareToFirends();
這段邏輯沒毛病, 但還有另一種實現(xiàn)方式
$film = Film::find(1);
$chinesFilm = $film->language()->where('name', 'Chinese')->first();
if($chinesFilm)
shareToFirends();
以上例子說明了關聯(lián)函數(shù)是可以再操作的, 只要用函數(shù)的方式來調用
- 一對多
一個演員可以接演多部電影, 所以演員和電影的關系屬于一對多
打開 Actor.php (數(shù)據(jù)庫表, Actor 所代表的模型), 加入函數(shù)如下
// 由于返回的是多條數(shù)據(jù), 建議函數(shù)名是副表模型的復數(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);
返回結果
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)
......
- 一對多間接關聯(lián)
上一個例子的返回結果并沒有帶出什么實質訊息, 這是一種常見的數(shù)據(jù)庫結構, 一對多的關系并不直接關聯(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');
}
調用方式
$actor = Actor::find(1);
dd($actor->films);
返回結果
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)
......
以上就是本期的所有內容了, 實在無法評價自己的文筆, 如果看不懂可以在下方留言
完整代碼
源代碼放在coding.net里, 自己領
上一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(10) - 自動載入篇
下一期 如何寫一個屬于自己的數(shù)據(jù)庫封裝(12) - 分頁篇