Laravel 技巧之 Pivot

在關系式數據庫中,要定義一個符合范式的多對多表關系需要一個中間表作為兩個表的關系。在Laravel中這個表稱為pivot,在查詢出關聯的記錄之后,可以通過pivot屬性來訪問關聯表的字段:

$user = App\User::find(1);
foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

在實際應用中,這個中間表可能不僅僅包含兩個表的外鍵,還有一些附加的字段,舉個例子:

一個用戶可以屬于多個部門,即用戶和部門是多對多關系,一個用戶在不同部門里角色可能不一樣,即用戶和角色也是多對多。這個中間表的結構如下:

+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| id            | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id       | int(10) unsigned | NO   |     | NULL    |                |
| role_id       | int(10) unsigned | NO   |     | NULL    |                |
| department_id | int(10) unsigned | NO   |     | NULL    |                |
| created_at    | timestamp        | YES  |     | NULL    |                |
| updated_at    | timestamp        | YES  |     | NULL    |                |
+---------------+------------------+------+-----+---------+----------------+

獲取一個用戶在所有部門所對應的角色時:

foreach($user->departments as $department) {
    $role = Role::find($department->privot->role_id);
}

可以看到步驟還是比較繁瑣,如果這個pivot能像別的Model那樣直接通過$department->privot->role來拿到角色信息就會方便很多。

研究了一下Laravel的代碼,發現是可以實現的,首先新建一個類

namespace App\PivotModels;

use Illuminate\Database\Eloquent\Relations\Pivot;
use App\Models\Role;
use App\Models\Department;

class UserRole extends Pivot
{
    public function role()
    {
        return $this->belongsTo(Role::class);
    }
    public function department()
    {
        return $this->belongsTo(Department::class);
    }
}

然后在App\Models\Department類中重寫newPivot方法:

public function newPivot(Model $parent, array $attributes, $table, $exists)
{
    if ($parent instanceof User) {
        return new UserRole($parent, $attributes, $table, $exists);
    }
    return parent::newPivot($parent, $attributes, $table, $exists);
}

修改App\Models\User類中的departments方法:

public function departments()
{
    return $this->belongsToMany(Department::class, 'user_role', 'department_id', 'user_id')
        ->withPivot(['department_id', 'user_id', 'role_id']) // 這行要把中間表的字段都加上
        ->withTimestamps();
}

這個時候在tinker中可以測試一下

$pivot = $user->departments()->first()->pivot; //輸出一個App\PivotModels\UserRole對象
$pivot->role; // 輸出對應的角色對象
$pivot->department; // 輸出對應的部門

更進一步,Illuminate\Database\Eloquent\Relations\Pivot這個類實際上是繼承于Illuminate\Database\Eloquent\Model類的,也就是說可以通過mutators功能來自定義getter/setter。(經測試pivot不支持model中的$appends/$with等屬性,定義了也不會有相應的行為,但仍可以通過load方法來加載關聯對象)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文鏈接 必備品 文檔:Documentation API:API Reference 視頻:Laracasts ...
    layjoy閱讀 8,626評論 0 121
  • 過去做事情急,什么東西拿起來就用,不喜歡進行系統性的學習,造成在使用過程中的錯誤和低效,現在感覺自己耐心多了,用之...
    馬文Marvin閱讀 2,022評論 0 10
  • 配置 修改config/database.php在connection數組中添加mongodb的配置信息,如下 '...
    jooohnny閱讀 8,468評論 3 8
  • 校園失物招領平臺開發 ——基于laravel框架構建最小內容管理系統 摘要 ? 針對目前大學校園人口密度大、人群活...
    藍蓮花xzsky閱讀 6,248評論 8 54
  • 先說幾句廢話,調和氣氛。事情的起由來自客戶需求頻繁變更,偉大的師傅決定橫刀立馬的改革使用新的框架(created ...
    wsdadan閱讀 3,091評論 0 12