有這樣的一個表結構
tables
projects
- id
- name
roles
- id
- name
users
- id
- name
project_user
- project_id
- user_id
- role_id
Model
class Project extends BaseModel
{
public function users()
{
return $this->belongsToMany(User::class);
}
}
class Role extends Model
{
...
}
class User extends Model
{
public function projects()
{
return $this->belongsToMany(Project::class);
}
}
使用者可以參加多個專案,是多對多的關係。
使用者可以有多個角色,也是多對多的關係。
假如,要查詢,使用者在某個專案下的角色。
$user = user::find(1);
foreach ($user->projects as $project){
$role = Role::find($project->pivot->role_id);
};
有沒有可能直接這樣
$user = user::find(1);
foreach ($user->projects as $project){
$role = $project->pivot->role;
};
這需要自定義一箇中間表模型,與角色關聯
laravel5.5 已經支援自定義中間表模型 pivot。之前的版本也可以,只不過麻煩一點,可以參考Laravel 技巧之 Pivot。
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProjectUserPivot extends Pivot
{
public function role()
{
return $this->belongsTo(Role::class);
}
}
使用中間表
這裡要把role_id
帶上。
class User extends Model
{
public function projects()
{
return $this->belongsToMany(Project::class)->using(ProjectUser::class)->withPivot('role_id');;
}
}
這樣就可以優雅的透過中間表查詢使用者角色了。
這樣操作在後臺處理資料時沒有問題,假如要把透過中間表獲得的角色資料傳到檢視上進行展示或者透過api請求資料時,這樣就不行了。
想著在中間表新增個這樣的屬性
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProjectUserPivot extends Pivot
{
protected $with=['role'];
public function role()
{
return $this->belongsTo(Role::class);
}
}
測試的時候發現不支援with屬性,幸好load支援,不然就只能嘎嘎了。
這樣只能另想其它辦法了。其實我們只要找到laravel什麼時候把中間表的模型資料新增進去,然後在這時候為中間表模型新增一個load。
透過檢視 belongsToMany
的方法,最後定位到為Illuminate\Database\Eloquent\Relations\BelongsToMany
的
protected function hydratePivotRelation(array $models)
{
// To hydrate the pivot relationship, we will just gather the pivot attributes
// and create a new Pivot model, which is basically a dynamic model that we
// will set the attributes, table, and connections on it so it will work.
foreach ($models as $model) {
$model->setRelation($this->accessor, $this->newExistingPivot(
$this->migratePivotAttributes($model)
));
}
}
可以看到這裡呼叫了Illuminate\Database\Eloquent\Model
的setRelation
方法。
$model->setRelation($this->accessor, $this->newExistingPivot(
$this->migratePivotAttributes($model)
));
檢視 setRelation
方法.在這個trait裡面Illuminate\Database\Eloquent\Concerns\HasRelationships
/**
* Set the specific relationship in the model.
*
* @param string $relation
* @param mixed $value
* @return $this
*/
public function setRelation($relation, $value)
{
$this->relations[$relation] = $value;
return $this;
}
在 Project
模型裡新增新的方法覆蓋掉setRelation
class Project extends BaseModel
{
public function users()
{
return $this->belongsToMany(User::class);
}
public function setRelation($relation, $value)
{
if($value instanceof ProjectUserPivot){
$this->relations[$relation] =$value->load('role');
return $this;
}
return parent::setRelation($relation, $value);
}
}
測試示例可以直觀的看下
最後模型整理
class Project extends BaseModel
{
public function users()
{
return $this->belongsToMany(User::class);
}
public function setRelation($relation, $value)
{
if($value instanceof ProjectUserPivot){
$this->relations[$relation] =$value->load('role');
return $this;
}
return parent::setRelation($relation, $value);
}
}
class Role extends Model
{
...
}
class User extends Model
{
public function projects()
{
return $this->belongsToMany(Project::class)->using(ProjectUser::class)->withPivot('role_id');;
}
}
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProjectUserPivot extends Pivot
{
public function role()
{
return $this->belongsTo(Role::class);
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結