您知道的 Laravel 魔法现在应用于连接。
连接在很多方面都非常有用。如果您在这里,您很可能了解并使用它们。 Eloquent 非常强大,但是在使用连接时它缺乏一点“Laravel 方式”。该包使您以更 Laravel 的方式进行连接,用更少的代码提高可读性,同时隐藏不需要公开的地方的实现细节。
在使用连接时,我们认为缺少一些非常强大的 Eloquent 功能:
您可以在这篇博客文章中阅读有关该包解决的问题的更详细说明。
您可以通过 Composer 安装该软件包:
composer require kirschbaum-development/eloquent-power-joins
对于 Laravel 版本 < 10,请使用 3.* 版本。对于 Laravel 版本 < 8,请使用 2.* 版本:
composer require kirschbaum-development/eloquent-power-joins:3. *
该软件包提供了一些功能。
假设您有一个与Post
模型具有hasMany
关系的User
模型。如果你想连接表,你通常会这样写:
User:: select ( ' users.* ' )-> join ( ' posts ' , ' posts.user_id ' , ' = ' , ' users.id ' );
这个包为您提供了一个新的joinRelationship()
方法,它执行完全相同的操作。
User:: joinRelationship ( ' posts ' );
两个选项都会产生相同的结果。就代码而言,您没有节省那么多,但您现在正在使用User
和Post
模型之间的关系来连接表。这意味着您现在隐藏了这种关系在幕后如何运作(实现细节)。如果关系类型发生变化,您也不需要更改代码。现在,您拥有了更具可读性且不那么繁重的代码。
但是,当您需要加入嵌套关系时,它会变得更好。假设您在Post
和Comment
模型之间也有一个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);
注意:查询变形关系一次仅支持一种变形类型。
对连接应用条件和回调
现在,假设您想对正在进行的连接应用一个条件。您只需将回调作为第二个参数传递给joinRelationship
方法即可。
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> where ( ' posts.approved ' , true ))-> toSql ();
您还可以在回调中指定要进行的连接类型:
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> left ());
对于嵌套调用,您只需传递引用关系名称的数组即可。
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 ();
});
在 join 子句中使用模型作用域时,您无法在作用域中键入提示$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 ()
对于属于多个或具有多个通过调用,您需要传递一个包含关系的数组,然后传递一个包含表名称的数组。
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
并且,如果您指定 select 语句:
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()
,它还会将附加的已发布范围应用于 join 子句。它会产生一个或多或少像这样的 SQL:
select users. * from users inner join posts on posts . user_id = posts . id and posts . published = 1
如果您的模型应用了全局作用域,您可以通过在 join 子句中调用withGlobalScopes
方法来启用全局作用域,如下所示:
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withGlobalScopes ());
不过,这里有一个问题。您的全局作用域不能在apply
方法的第一个参数中对EloquentBuilder
类进行类型提示,否则您将收到错误。
查询关系是否存在是 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 ' );
当使用powerJoinWhereHas
方法处理涉及 1 个以上表(一对多、多对多等)的关系时,请使用数组语法来传递回调:
User:: powerJoinWhereHas ( ' commentsThroughPosts ' , [
' comments ' => fn ( $ query ) => $ query -> where ( ' body ' , ' a ' )
])-> get ());
您还可以使用orderByPowerJoins
方法使用另一个表中的列对查询结果进行排序。
User:: orderByPowerJoins ( ' profile.city ' );
如果您需要为 order by 函数传递一些原始值,您可以这样做:
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 ' );
详细信息请参阅贡献。
如果您发现任何与安全相关的问题,请发送电子邮件至 [email protected],而不是使用问题跟踪器。
该软件包的开发由 Kirschbaum Development Group 赞助,这是一家专注于问题解决、团队建设和社区的开发人员驱动公司。了解更多关于我们的信息或加入我们!
麻省理工学院许可证 (MIT)。请参阅许可证文件以获取更多信息。