Laravel Eloquent Power Joins 包
发布日期:作者: Luis Dalmolin
如果您对数据库有一些经验,您很可能在您的职业生涯中至少使用过一次 joins
。Joins 可以用于多种不同的原因,从选择其他表中的数据到限制查询的匹配结果。
我将在本篇文章中给出一些示例,为了对这些示例进行说明,假设我们有以下数据库/模型结构。
User
-> hasMany
-> Post
Post
-> hasMany
-> Comment
Post
-> morphMany
-> Image
在 Laravel 中,使用 Eloquent,连接 posts
表将类似于这样
User::select('users.*')->join('posts', 'posts.user_id', '=', 'users.id');
如果您想连接 posts 表和 comments 表,您的查询将类似于这样
User::select('users.*') ->join('posts', 'posts.user_id', '=', 'users.id') ->join('comments', 'comments.post_id', '=', 'posts.id');
这很好,我们可以理解,但是我们可以做得更好。我们已经在我们的模型中定义了所有这些关系,但是我们在编写连接语句时重复了一些实现细节。因此,与其这样做,不如考虑一下,如果您可以执行以下操作,是否会更酷?
// example 1User::joinRelationship('posts'); // example 2User::joinRelationship('posts.comments');
这样代码更少,更重要的是,更容易阅读。它还隐藏了有关您的关系如何工作的任何实现细节。因此,如果您的关系发生变化,您的连接将自动更新。
介绍 Eloquent Power Joins 包
我们觉得我们在应用程序中连接的方式并不是真正的“Laravel 方式”,因此我们决定在我们的连接方式中加入一些 Laravel 的精妙之处。
joinRelationship
是由 Eloquent Power Joins 包引入的一种方法。它适用于任何类型的现有 Laravel 关系。
安装该包非常简单,只需运行以下 Composer 命令,您就应该可以访问本篇文章中提到的所有内容。
composer require kirschbaum-development/eloquent-power-joins
在您想要使用下面描述的方法的任何模型上,您应该使用以下特质
use Kirschbaum\PowerJoins\PowerJoins; class User extends Model{ use PowerJoins;}
连接多态关系
joinRelationship
方法也适用于多态关系。除了执行常规连接外,它还执行 {morph}_type == Model::class
检查,如下所示。
Post::joinRelationship('images')->toSql(); // select * from posts// inner join images on images.imageable_id = posts.id AND images.imageable_id = 'App\\Post'
连接嵌套关系
它也适用于嵌套关系。
User::joinRelationship('posts.images')->toSql(); // select * from users// inner join posts on posts.user_id = users.id// inner join images on images.imageable_id = posts.id AND images.imageable_id = 'App\\Post'
它适用于任何关系
该包将适用于 Laravel 提供的任何类型的原生关系类型。
BelongsToMany
将考虑到中间表,进行 2 次连接。HasManyThrough
也进行 2 次必要的连接。
Eloquent Power Joins 还会在相关模型使用 SoftDeletes
特质的情况下应用任何软删除子句。
但是,该包还为您提供了一些其他非常有用的功能,如下所示。
将额外的条件应用于连接
您也可以将任何所需的额外条件应用于连接。
User::joinRelationship('posts', function ($join) { $join->where('posts.published', true);});
对于嵌套调用,以及/或者 BelongsToMany
或 HasManyThrough
关系,您需要传递一个数组,关系作为键。
User::joinRelationship('posts.images', [ 'posts' => function ($join) { $join->where('posts.published', true); }, 'images' => function ($join) { $join->where('images.cover', true); },]);
在回调中使用模型作用域 🤯
我们认为这是该包最实用的功能之一。假设,您在 Post
模型中有一个 published
作用域
public function scopePublished($query){ $query->where('published', true);}
在连接关系时,您可以使用在被连接的模型中定义的作用域。这太酷了!
User::joinRelationshio('posts', function ($join) { // the $join instance here can access any of the scopes defined in the Post model 🤯 $join->published();});
查询关系是否存在
查询关系是否存在 是 Eloquent 的一项非常强大且便捷的功能。但是,它使用 where exists
语法,这并非总是最佳选择,也并非在所有情况下性能都最佳,具体取决于您有多少条记录以及表的结构。
该包还使用 joins
而不是 where exists
,实现了几乎所有 Laravel 方法来查询关系是否存在。
性能
首先要注意的是,以下示例是在使用连接而不是 where exists 性能要好得多的一个用例。您不应该假设这在所有查询中都适用,您应该使用 Laravel Debugbar 、Laravel Telescope 或您选择的任何工具来确定什么最适合您的用例。
也就是说,您可以看到下面的示例,在我们的一个客户的应用程序中,将更改部署到使用 powerJoinHas 而不是 has 后,MySQL 的 CPU 使用情况。MySQL 在 RDS 上运行,此图像是从 AWS CloudWatch 中获取的。
给我看代码
下面,您可以看到该包实现的方法以及 Laravel 等效方法。
Laravel 原生方法
User::has('posts');User::has('posts.comments');User::has('posts', '>', 3);User::whereHas('posts', function ($query) { $query->where('posts.published', true);});User::doesntHave('posts');
使用连接的包实现
User::powerJoinHas('posts');User::powerJoinHas('posts.comments');User::powerJoinHas('posts.comments', '>', 3);User::powerJoinWhereHas('posts', function ($query) { $query->where('posts.published', true);});User::powerJoinDoesntHave('posts');
对查询结果进行排序
另一个有用的功能是使用 orderByPowerJoins
方法,使用另一个表中的列对查询结果进行排序。
User::orderByPowerJoins('profile.city')->toSql();// select "users".* from "users"// inner join "user_profiles" on "user_profiles"."user_id" = "users"."id"// order by "user_profiles"."city" asc
您还可以按聚合(COUNT
、SUM
、AVG
、MIN
或 MAX
)对结果进行排序。
例如,要按发帖最多的用户进行排序,您将执行以下操作
$users = User::orderByPowerJoinsCount('posts.id', 'desc')->get();
或者,要获取按评论包含最高平均投票数进行排序的帖子列表。
$posts = Post::orderByPowerJoinsAvg('comments.votes', 'desc')->get();
您还有 SUM
、MIN
和 MAX
的方法
Post::orderByPowerJoinsSum('…');Post::orderByPowerJoinsMin('…');Post::orderByPowerJoinsMax('…');
Joins,Laravel 方式
IMO,该包的优点之一是能够以更“Laravel 的方式”编写代码。因此,您可以在下面看到一些示例,说明在使用它之后代码看起来好多了多少。这里描述的任何示例都会产生完全相同的结果。
示例 1
BuilderFile::select('builder_detail_builder_file.*') ->join('builder_detail_builder_file', 'builder_files.id', '=', 'builder_detail_builder_file.builder_file_id') ->join('builder_details', 'builder_details.id', '=', 'builder_detail_builder_file.builder_detail_id') ->join('documents', 'builder_details.document_id', '=', 'ces_documents.id');
使用 Eloquent Power Joins
BuilderFile::joinRelationship('details.document');
示例 2
CesDocument::query() ->join('term_relations', function ($join) { $join ->on('term_relations.relationable_id', '=', 'ces_documents.id') ->where('term_relations.relationable_type', '=', CesDocument::class); }) ->join('terms', 'term_relations.term_id', '=', 'terms.id') ->join('vocabularies', 'terms.vocabulary_id', '=', 'vocabularies.id') ->get();
使用 Eloquent Power Joins
CesDocument::query() ->joinRelationship('related.terms') ->joinRelationship('related.vocabulary') ->get();
就是这样。希望这个包对你来说和对我们一样有用。祝你连接愉快!
Luis 是 Kirschbaum 的高级开发人员,拥有超过 10 年的复杂应用程序架构经验,并且从 Laravel 4 的早期版本开始就一直在使用 Laravel。
除了 PHP 和 Laravel 之外,Luis 还擅长 VueJS/Javascript 和所有与 DevOps 相关的内容。他喜欢使用开源软件,并为社区贡献了多个开源项目。
Luis 在 Feevale 大学教授 AngularJS 课程,在那里他获得了互联网系统学位,他还将 Dayle Rees 的 Laravel 书籍“Code Bright”翻译成了葡萄牙语。