您知道的 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)。請參閱許可證文件以獲取更多資訊。