Laravel pivot 添加 load

有这样的一个表结构

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\ModelsetRelation方法。

 $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);
    }
}

测试示例可以直观的看下

file
最后模型整理

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);
    }
}

本文章首发在 Laravel China 社区

NOT IS BECAUSE I WANT TO WRITE,
BUT I WANT TO INCREASE,
SO I GO TO WRITE~~