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 模型实例:

Laravel 通过子查询创建动态关联
这一次,我们可以通过 $user->lastPost 对象获取文章模型上指定的所有查询字段,不再是只能获取文章标题了

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 1

能说明一下,为什么查询结果会有relations字段吗?正常不是应该在模型中,先定义好public function lastPost()的关联关系,构建查询时使用with(["lastPost"])才会出现relations吗?

3年前 评论
sgm4231 (楼主) 3年前
sgm4231 (楼主) 3年前
LiamHao (作者) 3年前
sgm4231 (楼主) 3年前
LiamHao (作者) 3年前
sgm4231 (楼主) 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!