Laravel 通过子查询创建动态关联
在 User
模型类中定义一个 lastPost
方法使其与该用户最新发布的文章记录建立一对一关联关系:
public function lastPost()
{
return $this->belongsTo(Post::class);
}
根据 Eloquent 默认使用的外键关联字段名称规则,上面的关联关系对应的外键字段应该是 last_post_id(即 users 表中的 last_post_id 映射到 posts 表的 id 字段建立关联),如果是常规的关联关系,需要在 users 表中包含 last_post_id,不过我们要建立的是运行时的动态关联关系,这个「动态」不依赖于具体的数据表字段,那要如何在运行时的 users 表中提供这个字段呢,结合上面的子查询实现,我们很容易想到,可以通过子查询动态提供这个字段。
我们继续在 User 模型类中新增一个本地查询作用域 scopeWithLastPost 方法来定义对应的子查询实现逻辑:
public function scopeWithLastPost($query)
{
return $query->addSelect([
'last_post_id' => Post::select(['id'])
->whereColumn('user_id', 'users.id')
->where('status', Post::STATUS_NORMAL)
->orderByDesc('created_at')
->limit(1)
])->with('lastPost:id,title,user_id,created_at');
}
这里我们将 posts 表子查询字段改为 id,将查询得到的字段别名设置为 users 表的 last_post_id,就可以在运行时动态创建 users 表的 last_post_id 字段了,我们接着在查询构建器上通过 with(‘lastPost’) 对 lastPost 关联进行渴求式加载(这里还指定了关联查询的查询字段),由于 users 表此时包含了 last_post_id 字段,也就可以顺理成章的进行对应的一对一关联查询了,这样一来,就可以在使用该查询作用域的 User 模型实例上获取到与之关联的 最新发布的一个 Post 模型实例了。
修改 UserController 关联查询代码如下:
class UserController extends Controller
{
public function index()
{
$columns = ['id', 'name', 'email'];
$users = User::select($columns)->withLastPost()->orderByDesc('id')->paginate(20);
return view('user.index', ['users' => $users]);
}
}
这一次,我们通过查询作用域获取动态的 lastPost 关联关系,打印获取到的 $users 变量,可以看到数据结构中包含了关联的 lastPost 关联对象,即 Post 模型实例:
这一次,我们可以通过 $user->lastPost
对象获取文章模型上指定的所有查询字段,不再是只能获取文章标题了
本作品采用《CC 协议》,转载必须注明作者和本文链接
能说明一下,为什么查询结果会有
relations
字段吗?正常不是应该在模型中,先定义好public function lastPost()
的关联关系,构建查询时使用with(["lastPost"])
才会出现relations
吗?