ご存知の 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 ' );
ポリモーフィックな関係 ( Post -> morphMany -> Image
) であるImage
モデルがあると想像してみましょう。通常の結合に加えて、 where imageable_type = Post::class
条件も適用する必要があります。そうしないと、乱雑な結果が得られる可能性があります。
多態性関係に参加すると、Eloquent Power Joins がこの条件を自動的に適用することがわかりました。同じメソッドを呼び出すだけです。
Post:: joinRelationship ( ' images ' );
MorphTo 関係に参加することもできます。
Image:: joinRelationship ( ' imageable ' , morphable: Post::class);
注: リレーションシップへのモーフのクエリでは、一度に 1 つのモーフィング タイプのみがサポートされます。
結合への条件とコールバックの適用
ここで、作成中の結合に条件を適用するとします。必要なのは、コールバックを 2 番目のパラメータとして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 ),
]
]);
これはこのパッケージの最も便利な機能の 1 つであると考えられます。 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
パラメーターにタイプ ヒントを入力することはできません。また、結合内にいるため、使用できる条件は結合でサポートされている条件のみに制限されることに注意してください。
同じテーブルを複数回結合するため、結合でテーブル エイリアスを使用する必要がある場合があります。これを実現する 1 つのオプションは、 joinRelationshipUsingAlias
メソッドを使用することです。
Post:: joinRelationshipUsingAlias ( ' category.parent ' )-> get ();
使用するエイリアスの名前を指定する必要がある場合は、次の 2 つの異なる方法で行うことができます。
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 ' );
複数のテーブル (1 対多、多対多など) が関係するリレーションシップでpowerJoinWhereHas
メソッドを使用する場合は、配列構文を使用してコールバックを渡します。
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 ライセンス (MIT)。詳細については、ライセンス ファイルを参照してください。