新入职后组长安排了一个小的管理项目来检验能力,后发现自身对于 ThinkPHP 框架中的模型关联属于一窍不通,故被终止项目叫楼主去恶补 ThinkPHP6 框架知识。

对于多联表查询之前本人一直使用join方法,但是此方法对于代码效率和维护都有较大影响,故在此尝试使用 ThinkPHP 框架内置的模型关联来对多联表进行操作(本次只记录查询操作)

一、建立数据表

首先我们先创建三个简易的数据表 User、Article、Comment 并添加一些测试数据,结构如下:
User:
ThinkPHP6 多模型关联查询操作记录-小白菜博客
Article:
ThinkPHP6 多模型关联查询操作记录-小白菜博客
Comment:
ThinkPHP6 多模型关联查询操作记录-小白菜博客
其中 Article 表使用 user_id 与 User 表关联;Comment 表使用 article_id 与 Article 表进行关联。接下来我们来设置对应的数据库模型代码。

二、建立对应数据表模型,并添加关联

在 User 模型中,我们想实现根据提供的用户 ID 查询出该用户对应的所有文章以及这些文章对应的评论

class User extends Model
{
    //主键
    protected $pk = 'user_id';

    //关联文章
    public function articles(){
        return $this->hasMany(Article::class, 'user_id', 'user_id');
    }
    //根据文章关联评论
    public function comments(){
        return $this->hasManyThrough(Comment::class, Article::class, 'user_id', 'article_id', 'user_id', 'article_id');
    }
}

由于 User 表与 Comment 表没有直接的对应关系,所以我们使用 hasManyThrough() 方法来建立远程模型关联。其中 Comment 是我们要关联的目标模型;Article 是中间模型;第一个 user_id 是中间模型外键,第二个 article_id 是中间模型与目标模型关联键,第三个 user_id 是当前模型的主键,第四个 article_id 是中间模型的主键。

接下来我们来设置 Article 模型的关联关系,要求实现根据提供的 ID 查询出该文章与所对应的所有评论

class Article extends Model
{
    //主键
    protected $pk = 'article_id';

    //关联用户
    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'user_id');
    }

    //一对多关联评论
    public function comments()
    {
        return $this->hasMany(Comment::class, 'article_id', 'article_id');
    }
}

最后我们还需要设置 Comment 模型

class Comment extends Model
{
    //主键
    protected $pk = 'comment_id';

    //关联文章
    public function article()
    {
        return $this->belongsTo(Article::class, 'article_id', 'article_id');
    }
}

三、编写控制器关联查询代码

由于是演示代码,所以暂时都在 index 控制器下编写、同时我们默认所有的路由表已经配好

首先,我们来实现一个给出任意文章 ID 我们返回此 ID 的文章信息同时返回该文章的所有评论的功能

public function article($id)
{
    $article = Article::with(['comments'])->find($id);

    return json_encode($article);
}

其中 with(['comments']) 对应我们在 Article 模型中创建的关联方法,之后我们用 17 号文章请求这个接口,会得到如下数据:

{
  "article_id":17,
  "user_id":1,
  "title":"test2",
  "status":0,
  "comments":[
  {"comment_id":9,"article_id":17,"comment":"test comment 1","status":1},
  {"comment_id":10,"article_id":17,"comment":"test comment 2","status":1},
  {"comment_id":11,"article_id":17,"comment":"test comment 3","status":1}]
}

可以看到此时模型返回了 17 号文章信息与该文章下所有的评论信息。至此我们完成了一个一对多的双表关联查询

接下来我们尝试实现根据给出的用户 ID 返回该用户对应的所有文章以及这些文章所对应的所有评论

public function user($id)
{
    $user = User::with(['articles' =>function($query){
        $query->with(['comments']);
    }])->find($id);

    return json_encode($user);
}

此时我们先使用 with(['articles']) 关联 Article 表来查询文章,之后在 with() 方法内使用闭包来使 Comment 表与 Article 表进行关联,这样可以使查询到的评论与文章之间是父子结构;接下来我们使用 2 号用户请求这个接口,会得到如下数据:

{
  "user_id":2,
  "username":"test",
  "password":"123456",
  "articles":[
  	{"article_id":14,"user_id":2,"title":"test2","status":1,"comments":[]},
	{"article_id":15,"user_id":2,"title":"test3","status":1,"comments":[]},
	{"article_id":16,"user_id":2,"title":"test1","status":1,"comments":[
	  {"comment_id":6,"article_id":16,"comment":"test comment 1","status":1},
	  {"comment_id":7,"article_id":16,"comment":"test comment 2","status":1},
	  {"comment_id":8,"article_id":16,"comment":"test comment 3","status":1}]},
	{"article_id":19,"user_id":2,"title":"test1","status":1,"comments":[
	  {"comment_id":17,"article_id":19,"comment":"test comment 1","status":1},
	  {"comment_id":18,"article_id":19,"comment":"test comment 2","status":1},
	  {"comment_id":19,"article_id":19,"comment":"test comment 3","status":1}]},
	{"article_id":20,"user_id":2,"title":"test2","status":1,"comments":[
	  {"comment_id":20,"article_id":20,"comment":"test comment 1","status":1},
	  {"comment_id":21,"article_id":20,"comment":"test comment 2","status":1},
	  {"comment_id":22,"article_id":20,"comment":"test comment 3","status":1}]},
	{"article_id":21,"user_id":2,"title":"test3","status":1,"comments":[
	  {"comment_id":23,"article_id":21,"comment":"test comment 1","status":1},
	  {"comment_id":24,"article_id":21,"comment":"test comment 2","status":1},
	  {"comment_id":25,"article_id":21,"comment":"test comment 3","status":1}]}]
}

可以看到这样直接查询出了所有与用户对应的文章以及这些文章所对应评论,同时表与表之间的数据是有父子关系的。
而当我们不使用闭包函数,而是直接在 with() 方法内直接调用我们在 User 模型中定义的远程关联 Comment 会返回什么样的数据呢?此时我们修改一下控制器中的代码

$user = User::with(['articles', 'comments'])->find($id);\\删除语句中的闭包

此时我们再次拿同样的 ID 请求这个接口,会得到如下数据:

{
  "user_id":2,
  "username":"test",
  "password":"123456",
  "articles":[
  	{"article_id":14,"user_id":2,"title":"test2","status":1},
	{"article_id":15,"user_id":2,"title":"test3","status":1},
	{"article_id":16,"user_id":2,"title":"test1","status":1},
	{"article_id":19,"user_id":2,"title":"test1","status":1},
	{"article_id":20,"user_id":2,"title":"test2","status":1},
	{"article_id":21,"user_id":2,"title":"test3","status":1}],
  "comments":[
	{"comment_id":6,"article_id":16,"comment":"test comment 1","status":1},
	{"comment_id":7,"article_id":16,"comment":"test comment 2","status":1},
	{"comment_id":8,"article_id":16,"comment":"test comment 3","status":1},
	{"comment_id":17,"article_id":19,"comment":"test comment 1","status":1},
	{"comment_id":18,"article_id":19,"comment":"test comment 2","status":1},
	{"comment_id":19,"article_id":19,"comment":"test comment 3","status":1},
	{"comment_id":20,"article_id":20,"comment":"test comment 1","status":1},
	{"comment_id":21,"article_id":20,"comment":"test comment 2","status":1},
	{"comment_id":22,"article_id":20,"comment":"test comment 3","status":1},
	{"comment_id":23,"article_id":21,"comment":"test comment 1","status":1},
	{"comment_id":24,"article_id":21,"comment":"test comment 2","status":1},
	{"comment_id":25,"article_id":21,"comment":"test comment 3","status":1}]
}

可以看到虽然也都查询出了所有数据,但是文章与评论之间并不是父子关系而是同级关系。

至此双表与三表的多表之间模型关联查询的代码已经介绍演示完毕,具体的应用场景需要读者们根据自身的业务场景来具体处理;本文章仅代表个人的学习经验,具体代码与参数设置请参考ThinkPHP官方开发文档。

第一次写博客记录学习过程,如有错误还请大佬们多多指出。