Laravel 6.0 中 Eloquent 子查询的增强
发布于 作者: Jonathan Reinink
如果您已经关注我的工作一段时间,您就会知道我非常喜欢将 Laravel 应用程序中的更多工作推送到数据库层。通过在数据库中完成更多工作,我们通常可以减少数据库查询的数量,减少应用程序使用的内存量,并减少 Eloquent 处理模型所需的时间。这可能会带来一些相当大的性能提升。
使用子查询是将更多工作推送到数据库的一种极好的方法。子查询允许您在另一个数据库查询中运行嵌套查询。这是一种强大的方法,可以检索辅助模型数据,而无需进行任何额外的数据库查询,即使无法通过关系进行操作。您也可以在 `order by` 语句、`where` 语句和其他数据库子句中使用子查询。
在我的 Laracon US 2019 演讲 中,我提到了几个我一直使用的查询构建器宏,它们使在 Laravel 中使用子查询变得更容易。我已经向 Laravel 提交了三个拉取请求,将这些宏添加到核心框架中。
以下是每个宏的概述
“Select” 子查询
拉取请求 #29567 为 `select()` 和 `addSelect()` 查询构建器方法添加了对子查询的支持。
例如,假设我们有一个航班 `destinations` 表和一个航班 `flights` 表。`flights` 表包含一个 `arrived_at` 列,用于指示航班到达目的地的时刻。
使用 Laravel 6.0 中新的子查询选择功能,我们可以使用单个查询选择所有 `destinations` 以及最晚到达该目的地的航班的 `name`。
return Destination::addSelect(['last_flight' => Flight::select('name') ->whereColumn('destination_id', 'destinations.id') ->orderBy('arrived_at', 'desc') ->limit(1)])->get();
请注意,我们在这里使用 Eloquent 生成子查询。这提供了简洁且富有表现力的语法。也就是说,您也可以使用查询构建器来实现。
return Destination::addSelect(['last_flight' => function ($query) { $query->select('name') ->from('flights') ->whereColumn('destination_id', 'destinations.id') ->orderBy('arrived_at', 'desc') ->limit(1);}])->get();
“Order by” 子查询
此外,拉取请求 #29563 使在查询构建器的 `orderBy()` 方法中使用子查询成为可能。继续以上面的示例,我们可以使用它根据上次航班到达目的地的时刻对目的地进行排序。
return Destination::orderByDesc( Flight::select('arrived_at') ->whereColumn('destination_id', 'destinations.id') ->orderBy('arrived_at', 'desc') ->limit(1))->get();
与选择一样,您也可以直接使用查询构建器来创建子查询。例如,您可能希望根据用户的上次登录日期对用户进行排序。
return User::orderBy(function ($query) { $query->select('created_at') ->from('logins') ->whereColumn('user_id', 'users.id') ->latest() ->limit(1);})->get();
“From” 子查询
最后,拉取请求 #29602 使在查询构建器的 `from()` 方法中使用子查询成为可能。这些有时被称为派生表。
例如,您可能希望计算应用程序中用户进行的平均总捐赠额。但是,在 SQL 中,无法嵌套聚合函数。
AVG(SUM(amount))
相反,我们可以使用一个 `from` 子查询来计算这个值。
return DB::table(function ($query) { $query->selectRaw('sum(amount) as total') ->from('donations') ->groupBy('user_id');}, 'donations')->avg('total');
您可能不会每天都需要使用它,但当您需要它时,它必不可少。
如果您在 Laravel 之外使用 Eloquent,则需要注意的一项重大变更是在 `Illuminate/Database/Capsule/Manager` 对象上的 `table()` 方法的签名变更。它已从 `table($table, $connection = null)` 变更为 `table($table, $as = null, $connection = null)`。
了解更多
如果您有兴趣了解更多关于子查询和其他高级数据库技术的知识,请务必关注我的 博客,以及观看我的 Laracon US 2019 演讲。
在 Laracon 上,我还宣布了一个正在制作的新视频课程,名为 Eloquent 性能模式。我制作这门课程的目标是教 Laravel 开发人员如何通过将更多工作推送到数据库层来大幅提高 Laravel 应用程序的性能,同时仍然使用 Eloquent ORM。如果您感兴趣,请务必加入该课程的邮件列表!
网页设计师和开发人员。Laravel 贡献者。 Inertia.js 作者。 Tailwind CSS 联合作者。务必查看我即将推出的 Eloquent 性能模式 视频课程!