kirchbaum-development / eloquent-power-joins
将 Laravel 的魔法应用于连接。
Requires
- php: ^8.0
- illuminate/database: ^8.0|^9.0|^10.0|^11.0
- illuminate/support: ^8.0|^9.0|^10.0|^11.0
Requires (Dev)
- laravel/legacy-factories: ^1.0@dev
- orchestra/testbench: ^4.0|^5.0|^6.0|^7.0|^8.0|^9.0
- phpunit/phpunit: ^8.0|^9.0|^10.0
- dev-master
- 3.5.8
- 3.5.7
- 3.5.6
- 3.5.5
- 3.5.4
- 3.5.3
- 3.5.2
- 3.5.1
- 3.5.0
- 3.4.0
- 3.3.4
- 3.3.3
- 3.3.2
- 3.3.1
- 3.3.0
- 3.2.4
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.1
- 3.1.0
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.7.3
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.5
- 2.6.4
- 2.6.3
- 2.6.2
- 2.6.1
- 2.6.0
- 2.5.2
- 2.5.1
- 2.5.0
- 2.4.1
- 2.4.0
- 2.3.3
- 2.3.2
- 2.3.1
- 2.3.0
- 2.2.3
- 2.2.2
- 2.2.1
- 2.2.0
- 2.1.0
- 2.0.2
- 2.0.1
- 2.0.0
- 1.1.1
- 1.1.0
- 1.0.x-dev
- 1.0.0
- 0.0.4
- 0.0.3
- 0.0.2
- 0.0.1
- dev-feature/diff-connections
- dev-issue-147
- dev-issue-134
- dev-php-81
- dev-php-8.0
- dev-fix-tests
This package is auto-updated.
Last update: 2024-09-10 10:28:27 UTC
README
您所熟知的 Laravel 魔法,现在应用于连接。
连接在许多方面非常有用。如果您在这里,您很可能会了解并使用它们。Eloquent 非常强大,但在使用连接时缺乏一点“Laravel 风格”。这个包使您的连接更加 Laravel 化,代码可读性更高,代码量更少,同时隐藏了不需要暴露的实现细节。
我们在使用连接时认为缺少了一些非常强大的 Eloquent 功能。
- 使用关系定义来创建连接的能力;
- 在不同的上下文中使用模型范围的能力;
- 使用连接而不是 WHERE EXISTS 来查询关系存在的能力;
- 根据相关表中的列或聚合轻松排序结果的能力;
您可以在这篇博客文章中阅读更多关于该包解决的问题的详细说明。
安装
您可以通过 composer 安装此包
composer require kirschbaum-development/eloquent-power-joins
对于 Laravel 版本 < 8,使用 2.* 版本
composer require kirschbaum-development/eloquent-power-joins:2.*
用法
此包提供了一些功能。
1 - 连接关系
假设您有一个具有 hasMany
关系到 Post
模型的 User
模型。如果您想连接表,您通常会写一些像这样的东西
User::select('users.*')->join('posts', 'posts.user_id', '=', 'users.id');
此包为您提供了一个新的 joinRelationship()
方法,它做的是完全相同的事情。
User::joinRelationship('posts');
两种选择产生相同的结果。在代码方面,您并没有节省那么多,但现在您正在使用 User
和 Post
模型之间的关系来连接表。这意味着现在您正在隐藏在幕后(实现细节)如何处理这种关系。如果关系类型发生变化,您也不需要更改代码。您现在拥有更易于阅读且更具可读性的代码。
但是,当您需要连接嵌套关系时,情况会更好。假设您还在这两个模型之间有一个 hasMany
关系,并且您需要连接这些表,您可以简单地写
User::joinRelationship('posts.comments');
好多了,不是吗?!您还可以根据需要将 left
或 right
连接到关系。
User::leftJoinRelationship('posts.comments'); User::rightJoinRelationship('posts.comments');
连接多态关系
让我们想象,您有一个 Image
模型,它是一个多态关系(Post -> morphMany -> Image
)。除了常规连接之外,您还需要应用 where imageable_type = Post::class
条件,否则您可能会得到混乱的结果。
结果发现,如果您连接一个多态关系,Eloquent Power Joins 会自动为您应用此条件。您只需要调用相同的方法。
Post::joinRelationship('images');
您也可以连接 MorphTo 关系。
Image::joinRelationship('imageable', morphable: Post::class);
注意:查询 morph to 关系一次只支持一种可变类型。
在连接中应用条件 & 回调
现在,假设您想要对您正在创建的连接应用一个条件。您只需要将回调作为 joinRelationship
方法的第二个参数传递。
User::joinRelationship('posts', fn ($join) => $join->where('posts.approved', true))->toSql();
对于 嵌套调用,您只需要传递一个引用关系名称的数组。
User::joinRelationship('posts.comments', [ 'posts' => fn ($join) => $join->where('posts.published', true), 'comments' => fn ($join) => $join->where('comments.approved', true), ]);
对于 属于多个 调用,您需要传递一个包含关系的数组,然后是一个包含表名称的数组。
User::joinRelationship('groups', [ 'groups' => [ 'groups' => function ($join) { // ... }, // group_members is the intermediary table here 'group_members' => fn ($join) => $join->where('group_members.active', true), ] ]);
在连接回调中使用模型范围 🤯
我们认为这是这个包最有用的功能之一。比如说,你在《Post》模型上有一个《published》作用域。
public function scopePublished($query) { $query->where('published', true); }
当建立关联关系时,你可以使用被连接的模型中定义的作用域。这有多酷?
User::joinRelationship('posts', function ($join) { // the $join instance here can access any of the scopes defined in Post 🤯 $join->published(); });
当在连接子句中使用模型作用域时,你不能在作用域中对《$query》参数进行类型提示。同时,请记住你在一个连接中,所以你只能使用连接支持的条件。
使用别名
有时候,你可能需要在连接中为同一个表使用表别名,因为你需要多次连接同一个表。实现这一点的其中一个选项是使用《joinRelationshipUsingAlias》方法。
Post::joinRelationshipUsingAlias('category.parent')->get();
如果你需要指定将要使用的别名名称,你可以有两种不同的方式
- 将字符串作为第二个参数传递(这不能用于嵌套连接)
Post::joinRelationshipUsingAlias('category', 'category_alias')->get();
- 在连接回调中调用《as》函数。
Post::joinRelationship('category.parent', [ 'category' => fn ($join) => $join->as('category_alias'), 'parent' => fn ($join) => $join->as('category_parent'), ])->get()
对于《belongs to many》或《has many through》调用,你需要传递一个包含关系的数组,然后是一个包含表名称的数组。
Group::joinRelationship('posts.user', [ 'posts' => [ 'posts' => fn ($join) => $join->as('posts_alias'), 'post_groups' => fn ($join) => $join->as('post_groups_alias'), ], ])->toSql();
选择表中的所有字段
当你进行连接时,使用《select * from ...》可能是危险的,因为父表和连接表之间可能存在同名字段,导致冲突。考虑到这一点,如果你在没有先选择任何特定列的情况下调用《joinRelationship》方法,Eloquent Power Joins 将自动为你包括这些列。例如,看看以下示例
User::joinRelationship('posts')->toSql(); // select users.* from users inner join posts on posts.user_id = users.id
并且,如果你指定了选择语句
User::select('users.id')->joinRelationship('posts')->toSql(); // select users.id from users inner join posts on posts.user_id = users.id
软删除
当连接任何使用《SoftDeletes》特质的模型时,以下条件将自动应用于所有你的连接。
and "users"."deleted_at" is null
如果你想包含已删除的模型,你可以在连接回调中调用《->withTrashed()》方法。
UserProfile::joinRelationship('users', fn ($join) => $join->withTrashed());
你还可以调用《onlyTrashed》模型。
UserProfile::joinRelationship('users', ($join) => $join->onlyTrashed());
在关系定义中定义的额外条件
如果你在关系定义中有额外的条件,它们将自动为你应用。
class User extends Model { public function publishedPosts() { return $this->hasMany(Post::class)->published(); } }
如果你调用《User::joinRelationship('publishedPosts')->get()`》,它也将应用额外的已发布作用域到连接子句。它将生成类似于以下SQL的SQL语句
select users.* from users inner join posts on posts.user_id = posts.id and posts.published = 1
全局作用域
如果你的模型有全局作用域被应用,你可以在连接子句中通过调用《withGlobalScopes》方法来启用全局作用域,如下所示
UserProfile::joinRelationship('users', fn ($join) => $join->withGlobalScopes());
不过,这里有一个问题。你的全局作用域在《apply》方法的第一个参数中不能对《Eloquent.Builder》类进行类型提示,否则你将得到错误。
2 - 查询关系存在性(使用连接)
查询关系存在性是Eloquent非常强大且方便的功能。然而,它使用《where exists》语法,这并不总是最佳选择,可能也不是性能更好的选择,这取决于你有多少记录或你表的结构。
这个包实现了相同的功能,但它不是使用《where exists》语法,而是使用《joins》。以下是你可以在包中看到的方法,以及Laravel的等效方法。
请注意,尽管方法相似,但使用连接时,根据你的查询上下文,你不会总是得到相同的结果。你应该意识到使用《where exists》与使用《joins》查询数据的差异。
Laravel原生方法
User::has('posts'); User::has('posts.comments'); User::has('posts', '>', 3); User::whereHas('posts', fn ($query) => $query->where('posts.published', true)); User::whereHas('posts.comments', ['posts' => fn ($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 ($join) { $join->where('posts.published', true); }); User::powerJoinDoesntHave('posts');
当使用涉及1个以上表(一对一、多对多等)关系的powerJoinWhereHas
方法时,请使用数组语法传递回调。
User::powerJoinWhereHas('commentsThroughPosts', [ 'comments' => fn ($query) => $query->where('body', 'a') ])->get());
3 - 排序
您也可以使用orderByPowerJoins
方法通过另一张表的列对查询结果进行排序。
User::orderByPowerJoins('profile.city');
如果您需要为排序函数传递一些原始值,可以这样做
User::orderByPowerJoins(['profile', DB::raw('concat(city, ", ", state)']);
此查询将根据user_profiles
表上的city
列对结果进行排序。您还可以根据聚合函数(COUNT
、SUM
、AVG
、MIN
或MAX
)对结果进行排序。
例如,要按发帖数最多的用户排序,您可以这样做
$users = User::orderByPowerJoinsCount('posts.id', 'desc')->get();
或者,要获取评论中平均得票数最高的帖子列表。
$posts = Post::orderByPowerJoinsAvg('comments.votes', 'desc')->get();
您还有用于SUM
、MIN
和MAX
的方法
Post::orderByPowerJoinsSum('comments.votes'); Post::orderByPowerJoinsMin('comments.votes'); Post::orderByPowerJoinsMax('comments.votes');
如果您想在排序中使用左连接,也可以
Post::orderByLeftPowerJoinsCount('comments.votes'); Post::orderByLeftPowerJoinsAvg('comments.votes'); Post::orderByLeftPowerJoinsSum('comments.votes'); Post::orderByLeftPowerJoinsMin('comments.votes'); Post::orderByLeftPowerJoinsMax('comments.votes');
贡献
有关详细信息,请参阅CONTRIBUTING。
安全
如果您发现任何与安全相关的问题,请通过电子邮件security@kirschbaumdevelopment.com联系,而不是使用问题跟踪器。
鸣谢
赞助
本软件包的开发由Kirschbaum Development Group赞助,这是一家以解决问题、团队建设和社区为中心的开发者驱动型公司。了解更多关于我们或加入我们!
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。
Laravel软件包模板
此软件包是使用Laravel软件包模板生成的。