在關系式數據庫中,要定義一個符合范式的多對多表關系需要一個中間表作為兩個表的關系。在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
方法來加載關聯對象)。