跳至正文

Laravel多态关联的应用案例

多对多 多态

需求如图用户关注不同的实体
用户表
专家表
主播表
作者表
关注中间表
//用户模型
class User extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens, DateFormat, Filterable;

    protected $table = 'users';
    
    //关注的用户
    public function followUser()
    {
        return $this->morphedByMany($this, 'followable', 'followable', 'user_id', 'followable_id')
            ->withTimestamps();
    }
    //关注的专家
    public function followExpert()
    {
        return $this->morphedByMany('App\Models\Expert', 'followable', 'followable', 'user_id', 'followable_id')
            ->withTimestamps();
    }
    //关注的主播
    public function followAnchors()
    {
        return $this->morphedByMany('App\Models\Anchors', 'followable', 'followable', 'user_id', 'followable_id')
            ->withTimestamps();
    }
    //关注的作者
    public function followAuthors()
    {
        return $this->morphedByMany('App\Models\Authors', 'followable', 'followable', 'user_id', 'followable_id')
            ->withTimestamps();
    }
    //关注者,即主动关注用户的人
    public function followerUser()
    {
        return $this->morphToMany($this, 'followable', 'followable', 'followable_id', 'user_id')
            ->withTimestamps();
    }
}
//专家模型
class Expert extends BaseModel
{
    protected $table = 'expert';
    //关注者,即主动关注专家的人
    public function followerUser()
    {
        return $this->morphToMany('App\Models\User', 'followable', 'followable', 'followable_id', 'user_id')
            ->withTimestamps();
    }
}
//主播模型
class Anchors extends BaseModel
{
    protected $table = 'anchors';
    //关注者,即主动关注主播的人
    public function followerUser()
    {
        return $this->morphToMany('App\Models\User', 'followable', 'followable', 'followable_id', 'user_id')
            ->withTimestamps();
    }
}
//作者模型
class Authors extends BaseModel
{
    protected $table = 'authors';
    //关注者,即主动关注作者的人
    public function followerUser()
    {
        return $this->morphToMany('App\Models\User', 'followable', 'followable', 'followable_id', 'user_id')
            ->withTimestamps();
    }
}
//控制器代码
class FollowController extends Controller
{
  //关注/取消
  public function toggle(Request $request)
 {
    $request->validate([
         'followable_id'=>'bail|required|integer',
        'followable_type'=>'bail|required|string|in:user,expert,authors,anchors'
    ]);
    //利用可变方法动态创建模型对象
    $type_model = "\App\Models\\".ucfirst($request->input('followable_type'));
    $follow = $type_model::findOrFail($request->input('followable_id'));
    //调用关注人关联和toggle方法动态切换关注和取消关注
    $result = $follow->followerUser()->toggle($request->user()->id);

    if ($result['attached']) {
        $status = 1;
    } else {
        $status = 0;
    }

    return response()->json(compact('status'));
 }

//关注列表
public function index(Request $request)
 {
    $request->validate([
      'followable_type'=>'bail|required|string|in:user,expert,authors,anchors'
    ]);
    //根据类型可变方法动态加载我的关注关联,实现4个类型列表
    $followable_funname = 'follow'.ucfirst($request->input('followable_type'));
    $builder = $request->user()->$followable_funname();

    if ($request->input('followable_type') == 'user') {
        $builder->select('users.id', 'name', 'avatar');
    } elseif ($request->input('followable_type') == 'expert') {
        $builder->select('expert.id', 'name', 'nickname', 'photo', 'tags')
                ->where('is_show', 1);
    } elseif ($request->input('followable_type') == 'authors') {
        $builder->select('authors.id', 'authors.user_id', 'introduction')
                ->with([
                    'user'=>function ($query) {
                        $query->select('id', 'name', 'avatar');
                    }
                ])
                ->where('status', 1);
    } elseif ($request->input('followable_type') == 'anchors') {
        $builder->select('anchors.id', 'anchors.user_id', 'games_type')
                ->with([
                    'user'=>function ($query) {
                        $query->select('id', 'name', 'avatar');
                    }
                ])
                ->where('status', 1);
   }
   //按关注时间排序
   $followed = $builder->orderBy('followable.created_at', 'desc')
           ->forPage($request->input('page', 1), $request->input('per_page', 1))
           ->get();

   return $followed;
}

}

这种抽象的写法可以减少接口数量,要看具体需求是否合适