如何寫一個屬于自己的數(shù)據(jù)庫封裝(11) - 關(guān)聯(lián)關(guān)系篇

上一期 如何寫一個屬于自己的數(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ù)有什么用

例子

  1. 一對一

一部電影僅有一個語種(場景需要,勿糾結(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)用

  1. 一對多

一個演員可以接演多部電影, 所以演員和電影的關(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)
......
  1. 一對多間接關(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) - 分頁篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容