Keajaiban Laravel lho, sekarang diterapkan pada join.
Bergabung sangat berguna dalam banyak hal. Jika Anda di sini, kemungkinan besar Anda mengetahui dan menggunakannya. Eloquent sangat kuat, namun kurang memiliki sedikit "cara Laravel" saat menggunakan gabungan. Paket ini membuat penggabungan Anda menjadi lebih Laravel, lebih mudah dibaca dengan lebih sedikit kode sambil menyembunyikan detail implementasi dari tempat yang tidak perlu diekspos.
Beberapa hal yang kami anggap hilang saat menggunakan gabungan yang merupakan fitur Eloquent yang sangat kuat:
Anda dapat membaca penjelasan lebih rinci tentang masalah yang dipecahkan oleh paket ini di postingan blog ini.
Anda dapat menginstal paket melalui composer:
composer require kirschbaum-development/eloquent-power-joins
Untuk versi Laravel <10, gunakan versi 3.*. Untuk versi Laravel <8, gunakan versi 2.*:
composer require kirschbaum-development/eloquent-power-joins:3. *
Paket ini menyediakan beberapa fitur.
Katakanlah Anda memiliki model User
dengan hubungan hasMany
dengan model Post
. Jika Anda ingin bergabung dengan tabel, biasanya Anda akan menulis sesuatu seperti:
User:: select ( ' users.* ' )-> join ( ' posts ' , ' posts.user_id ' , ' = ' , ' users.id ' );
Paket ini memberi Anda metode joinRelationship()
baru, yang melakukan hal yang persis sama.
User:: joinRelationship ( ' posts ' );
Kedua opsi menghasilkan hasil yang sama. Dalam hal kode, Anda tidak menghemat sebanyak itu, tetapi Anda sekarang menggunakan hubungan antara model User
dan Post
untuk menggabungkan tabel. Ini berarti Anda sekarang menyembunyikan cara kerja hubungan ini di balik layar (detail implementasi). Anda juga tidak perlu mengubah kode jika tipe relasi berubah. Anda sekarang memiliki kode yang lebih mudah dibaca dan tidak terlalu membebani.
Namun, akan lebih baik bila Anda perlu bergabung dalam hubungan yang bertumpuk . Anggaplah Anda juga memiliki hubungan hasMany
antara model Post
dan Comment
dan Anda perlu menggabungkan tabel-tabel ini, Anda cukup menulis:
User:: joinRelationship ( ' posts.comments ' );
Jauh lebih baik, setujukah Anda?! Anda juga dapat bergabung left
atau right
sesuai kebutuhan.
User:: leftJoinRelationship ( ' posts.comments ' );
User:: rightJoinRelationship ( ' posts.comments ' );
Bayangkan, Anda memiliki model Image
yang merupakan hubungan polimorfik ( Post -> morphMany -> Image
). Selain penggabungan reguler, Anda juga perlu menerapkan kondisi where imageable_type = Post::class
, jika tidak, Anda bisa mendapatkan hasil yang berantakan.
Ternyata, jika Anda bergabung dalam hubungan polimorfik, Eloquent Power Joins secara otomatis menerapkan ketentuan ini untuk Anda. Anda hanya perlu memanggil metode yang sama.
Post:: joinRelationship ( ' images ' );
Anda juga dapat bergabung dengan hubungan MorphTo.
Image:: joinRelationship ( ' imageable ' , morphable: Post::class);
Catatan: Mengkueri morf ke hubungan hanya mendukung satu tipe yang dapat diubah dalam satu waktu.
Menerapkan ketentuan & panggilan balik ke gabungan
Sekarang, katakanlah Anda ingin menerapkan suatu kondisi pada penggabungan yang Anda buat. Anda hanya perlu meneruskan panggilan balik sebagai parameter kedua ke metode joinRelationship
.
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> where ( ' posts.approved ' , true ))-> toSql ();
Anda juga dapat menentukan jenis gabungan yang ingin Anda buat dalam panggilan balik:
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> left ());
Untuk panggilan bersarang , Anda hanya perlu meneruskan array yang mereferensikan nama hubungan.
User:: joinRelationship ( ' posts.comments ' , [
' posts ' => fn ( $ join ) => $ join -> where ( ' posts.published ' , true ),
' comments ' => fn ( $ join ) => $ join -> where ( ' comments.approved ' , true ),
]);
Untuk memiliki banyak panggilan, Anda perlu meneruskan array dengan relasinya, dan kemudian array dengan nama tabel.
User:: joinRelationship ( ' groups ' , [
' groups ' => [
' groups ' => function ( $ join ) {
// ...
},
// group_members is the intermediary table here
' group_members ' => fn ( $ join ) => $ join -> where ( ' group_members.active ' , true ),
]
]);
Kami menganggap ini salah satu fitur paling berguna dari paket ini. Misalnya, Anda memiliki cakupan published
pada model Post
Anda:
public function scopePublished ( $ query )
{
$ query -> where ( ' published ' , true );
}
Saat menggabungkan hubungan, Anda dapat menggunakan cakupan yang ditentukan dalam model yang digabungkan. Seberapa keren ini?
User:: joinRelationship ( ' posts ' , function ( $ join ) {
// the $join instance here can access any of the scopes defined in Post
$ join -> published ();
});
Saat menggunakan cakupan model di dalam klausa gabungan, Anda tidak dapat mengetikkan petunjuk parameter $query
dalam cakupan Anda. Selain itu, perlu diingat bahwa Anda berada di dalam gabungan, jadi Anda dibatasi hanya untuk menggunakan kondisi yang didukung oleh gabungan.
Terkadang, Anda perlu menggunakan alias tabel pada gabungan Anda karena Anda bergabung dengan tabel yang sama lebih dari sekali. Salah satu opsi untuk mencapai hal ini adalah dengan menggunakan metode joinRelationshipUsingAlias
.
Post:: joinRelationshipUsingAlias ( ' category.parent ' )-> get ();
Jika Anda perlu menentukan nama alias yang akan digunakan, Anda dapat melakukannya dengan dua cara berbeda:
Post:: joinRelationshipUsingAlias ( ' category ' , ' category_alias ' )-> get ();
as
di dalam panggilan balik join. Post:: joinRelationship ( ' category.parent ' , [
' category ' => fn ( $ join ) => $ join -> as ( ' category_alias ' ),
' parent ' => fn ( $ join ) => $ join -> as ( ' category_parent ' ),
])-> get ()
Untuk milik banyak atau memiliki banyak panggilan melalui , Anda perlu meneruskan array dengan relasinya, dan kemudian array dengan nama tabel.
Group:: joinRelationship ( ' posts.user ' , [
' posts ' => [
' posts ' => fn ( $ join ) => $ join -> as ( ' posts_alias ' ),
' post_groups ' => fn ( $ join ) => $ join -> as ( ' post_groups_alias ' ),
],
])-> toSql ();
Saat membuat gabungan, penggunaan select * from ...
bisa berbahaya karena bidang dengan nama yang sama antara tabel induk dan tabel yang digabungkan dapat menimbulkan konflik. Mengingat hal itu, jika Anda memanggil metode joinRelationship
tanpa sebelumnya memilih kolom tertentu, Eloquent Power Joins akan secara otomatis menyertakannya untuk Anda. Misalnya, lihat contoh berikut:
User:: joinRelationship ( ' posts ' )-> toSql ();
// select users.* from users inner join posts on posts.user_id = users.id
Dan, jika Anda menentukan pernyataan pilih:
User:: select ( ' users.id ' )-> joinRelationship ( ' posts ' )-> toSql ();
// select users.id from users inner join posts on posts.user_id = users.id
Saat menggabungkan model apa pun yang menggunakan sifat SoftDeletes
, ketentuan berikut juga akan otomatis diterapkan ke semua gabungan Anda:
and " users " . " deleted_at " is null
Jika Anda ingin menyertakan model yang dibuang, Anda dapat memanggil metode ->withTrashed()
dalam panggilan balik gabungan.
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withTrashed ());
Anda juga dapat memanggil model onlyTrashed
:
UserProfile:: joinRelationship ( ' users ' , ( $ join ) => $ join -> onlyTrashed ());
Jika Anda memiliki ketentuan tambahan dalam definisi hubungan Anda, ketentuan tersebut akan diterapkan secara otomatis untuk Anda.
class User extends Model
{
public function publishedPosts ()
{
return $ this -> hasMany (Post::class)-> published ();
}
}
Jika Anda memanggil User::joinRelationship('publishedPosts')->get()
, itu juga akan menerapkan cakupan tambahan yang diterbitkan ke klausa join. Itu akan menghasilkan SQL kurang lebih seperti ini:
select users. * from users inner join posts on posts . user_id = posts . id and posts . published = 1
Jika model Anda menerapkan cakupan global, Anda dapat mengaktifkan cakupan global dengan memanggil metode withGlobalScopes
dalam klausa gabungan Anda, seperti ini:
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withGlobalScopes ());
Namun, ada masalah di sini. Cakupan global Anda tidak dapat mengetikkan petunjuk kelas EloquentBuilder
di parameter pertama metode apply
, jika tidak, Anda akan mendapatkan kesalahan.
Menanyakan keberadaan hubungan adalah fitur Eloquent yang sangat kuat dan nyaman. Namun, ia menggunakan sintaksis where exists
yang tidak selalu merupakan yang terbaik dan mungkin bukan pilihan yang berkinerja lebih baik, bergantung pada berapa banyak rekaman yang Anda miliki atau struktur tabel Anda.
Paket-paket ini mengimplementasikan fungsi yang sama, namun alih-alih menggunakan sintaksis where exists
, paket ini menggunakan joins . Di bawah, Anda dapat melihat metode yang diimplementasikan paket ini dan juga setara dengan Laravel.
Harap perhatikan bahwa meskipun metodenya serupa, Anda tidak akan selalu mendapatkan hasil yang sama saat menggunakan gabungan, bergantung pada konteks kueri Anda. Anda harus menyadari perbedaan antara menanyakan data dengan where exists
vs joins
.
Metode Asli 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 ' );
Setara dengan paket, tetapi menggunakan gabungan
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 ' );
Saat menggunakan metode powerJoinWhereHas
dengan hubungan yang melibatkan lebih dari 1 tabel (Satu ke Banyak, Banyak ke Banyak, dll.), gunakan sintaks array untuk meneruskan panggilan balik:
User:: powerJoinWhereHas ( ' commentsThroughPosts ' , [
' comments ' => fn ( $ query ) => $ query -> where ( ' body ' , ' a ' )
])-> get ());
Anda juga bisa mengurutkan hasil kueri menggunakan kolom dari tabel lain menggunakan metode orderByPowerJoins
.
User:: orderByPowerJoins ( ' profile.city ' );
Jika Anda perlu meneruskan beberapa nilai mentah untuk urutan berdasarkan fungsi, Anda dapat melakukan seperti ini:
User:: orderByPowerJoins ([ ' profile ' , DB :: raw ( ' concat(city, ", ", state) ' ]);
Query ini akan mengurutkan hasil berdasarkan kolom city
pada tabel user_profiles
. Anda juga dapat mengurutkan hasil berdasarkan agregasi ( COUNT
, SUM
, AVG
, MIN
atau MAX
).
Misalnya, untuk mengurutkan pengguna dengan jumlah postingan terbanyak, Anda dapat melakukan ini:
$ users = User:: orderByPowerJoinsCount ( ' posts.id ' , ' desc ' )-> get ();
Atau, untuk mendapatkan daftar postingan yang komentarnya mengandung rata-rata suara tertinggi.
$ posts = Post:: orderByPowerJoinsAvg ( ' comments.votes ' , ' desc ' )-> get ();
Anda juga memiliki metode untuk SUM
, MIN
dan MAX
:
Post:: orderByPowerJoinsSum ( ' comments.votes ' );
Post:: orderByPowerJoinsMin ( ' comments.votes ' );
Post:: orderByPowerJoinsMax ( ' comments.votes ' );
Jika Anda ingin menggunakan gabungan kiri dalam pengurutan, Anda juga dapat:
Post:: orderByLeftPowerJoinsCount ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsAvg ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsSum ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMin ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMax ( ' comments.votes ' );
Silakan lihat KONTRIBUSI untuk rinciannya.
Jika Anda menemukan masalah terkait keamanan, silakan kirim email ke [email protected] daripada menggunakan pelacak masalah.
Pengembangan paket ini disponsori oleh Kirschbaum Development Group, sebuah perusahaan berbasis pengembang yang berfokus pada pemecahan masalah, pembangunan tim, dan komunitas. Pelajari lebih lanjut tentang kami atau bergabunglah dengan kami!
Lisensi MIT (MIT). Silakan lihat File Lisensi untuk informasi lebih lanjut.