เวทมนตร์ Laravel ที่คุณรู้จัก ตอนนี้นำไปใช้กับการรวมแล้ว
การรวมมีประโยชน์มากในหลายๆ ด้าน หากคุณอยู่ที่นี่ คุณน่าจะรู้จักและใช้งานสิ่งเหล่านี้มากที่สุด Eloquent มีประสิทธิภาพมาก แต่ไม่มี "วิธี Laravel" เล็กน้อยเมื่อใช้การรวม แพ็คเกจนี้ทำให้การรวมของคุณเป็นแบบ Laravel มากขึ้น โดยอ่านง่ายขึ้นโดยใช้โค้ดน้อยลง ในขณะที่ซ่อนรายละเอียดการใช้งานจากที่ที่ไม่จำเป็นต้องเปิดเผย
บางสิ่งที่เราพิจารณาว่าขาดหายไปเมื่อใช้การรวมซึ่งเป็นฟีเจอร์ Eloquent ที่ทรงพลังมาก:
คุณสามารถอ่านคำอธิบายโดยละเอียดเพิ่มเติมเกี่ยวกับปัญหาที่แพ็คเกจนี้แก้ไขได้ในโพสต์บนบล็อกนี้
คุณสามารถติดตั้งแพ็คเกจผ่านทางผู้แต่ง:
composer require kirschbaum-development/eloquent-power-joins
สำหรับเวอร์ชัน Laravel < 10 ให้ใช้เวอร์ชัน 3.* สำหรับเวอร์ชัน Laravel < 8 ให้ใช้เวอร์ชัน 2.*:
composer require kirschbaum-development/eloquent-power-joins:3. *
แพ็คเกจนี้มีคุณสมบัติบางประการ
สมมติว่าคุณมีโมเดล User
ที่มีความสัมพันธ์ hasMany
กับโมเดล Post
หากคุณต้องการเข้าร่วมตาราง คุณมักจะเขียนดังนี้:
User:: select ( ' users.* ' )-> join ( ' posts ' , ' posts.user_id ' , ' = ' , ' users.id ' );
แพ็คเกจนี้ให้วิธี joinRelationship()
ใหม่แก่คุณ ซึ่งทำสิ่งเดียวกันทุกประการ
User:: joinRelationship ( ' posts ' );
ทั้งสองตัวเลือกให้ผลลัพธ์ที่เหมือนกัน ในแง่ของโค้ด คุณไม่ได้ประหยัดมากนัก แต่ตอนนี้คุณกำลังใช้ความสัมพันธ์ระหว่าง User
และโมเดล Post
เพื่อเข้าร่วมตาราง ซึ่งหมายความว่าขณะนี้คุณกำลังซ่อนความสัมพันธ์นี้ทำงานอยู่เบื้องหลัง (รายละเอียดการใช้งาน) คุณไม่จำเป็นต้องเปลี่ยนรหัสหากประเภทความสัมพันธ์เปลี่ยนแปลง ตอนนี้คุณมีโค้ดที่อ่านง่ายขึ้นและมีโค้ดที่ล้นหลามน้อยลง
แต่ จะดีขึ้น เมื่อคุณต้องการ เข้าร่วมความสัมพันธ์ที่ซ้อนกัน สมมติว่าคุณมีความสัมพันธ์ hasMany
ระหว่างโมเดล Post
และ Comment
และคุณจำเป็นต้องเข้าร่วมตารางเหล่านี้ คุณก็สามารถเขียน:
User:: joinRelationship ( ' posts.comments ' );
ดีกว่ามาก เห็นด้วยไหม! คุณยังสามารถเข้าร่วมความสัมพันธ์ left
หรือ right
ได้ตามต้องการ
User:: leftJoinRelationship ( ' posts.comments ' );
User:: rightJoinRelationship ( ' posts.comments ' );
ลองจินตนาการว่าคุณมีโมเดล Image
ที่มีความสัมพันธ์แบบ polymorphic ( Post -> morphMany -> Image
) นอกจากการเข้าร่วมปกติแล้ว คุณจะต้องใช้เงื่อนไข where imageable_type = Post::class
ไม่เช่นนั้นคุณอาจได้รับผลลัพธ์ที่ยุ่งเหยิง
ปรากฎว่า ถ้าคุณเข้าร่วมความสัมพันธ์แบบหลายรูปแบบ Eloquent Power Joins จะใช้เงื่อนไขนี้กับคุณโดยอัตโนมัติ คุณเพียงแค่ต้องเรียกวิธีเดียวกัน
Post:: joinRelationship ( ' images ' );
คุณยังสามารถเข้าร่วมความสัมพันธ์ MorphTo ได้อีกด้วย
Image:: joinRelationship ( ' imageable ' , morphable: Post::class);
หมายเหตุ: การสืบค้น morph ไปยังความสัมพันธ์รองรับประเภท morphable ได้ครั้งละหนึ่งประเภทเท่านั้น
การใช้เงื่อนไขและการเรียกกลับเพื่อเข้าร่วม
ตอนนี้ สมมติว่าคุณต้องการใช้เงื่อนไขกับการเข้าร่วมที่คุณกำลังทำ คุณเพียงแค่ต้องส่งการโทรกลับเป็นพารามิเตอร์ตัวที่สองไปยังเมธอด 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 ),
]
]);
เราถือว่านี่เป็นหนึ่งในคุณสมบัติที่มีประโยชน์ที่สุดของแพ็คเกจนี้ สมมติว่าคุณมีขอบเขต published
ในโมเดล Post
ของคุณ:
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 clause ด้วย มันจะสร้าง 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 ());
แม้ว่าจะมี gotcha ที่นี่ ขอบเขตส่วนกลางของคุณ ไม่สามารถ พิมพ์คำใบ้คลาส EloquentBuilder
ในพารามิเตอร์แรกของวิธี apply
มิฉะนั้นคุณจะได้รับข้อผิดพลาด
การสืบค้นการดำรงอยู่ของความสัมพันธ์เป็นคุณลักษณะที่มีประสิทธิภาพและสะดวกสบายมากของ Eloquent อย่างไรก็ตาม จะใช้ไวยากรณ์ where exists
ซึ่งไม่ได้ดีที่สุดเสมอไปและอาจไม่ใช่ตัวเลือกที่มีประสิทธิภาพมากกว่า ขึ้นอยู่กับจำนวนระเบียนที่คุณมีหรือโครงสร้างของตารางของคุณ
แพ็คเกจนี้ใช้ฟังก์ชันการทำงานเดียวกัน แต่แทนที่จะใช้ไวยากรณ์ where exists
ใช้ การรวม ด้านล่างนี้ คุณสามารถดูวิธีการที่แพ็คเกจนี้นำไปใช้และเทียบเท่ากับ 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 ' );
หากคุณต้องการส่งค่าดิบสำหรับการเรียงลำดับตามฟังก์ชัน คุณสามารถดำเนินการดังนี้:
User:: orderByPowerJoins ([ ' profile ' , DB :: raw ( ' concat(city, ", ", state) ' ]);
แบบสอบถามนี้จะเรียงลำดับผลลัพธ์ตามคอลัมน์ city
ในตาราง user_profiles
คุณยังสามารถจัดเรียงผลลัพธ์ตามการรวม ( 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) โปรดดูไฟล์ใบอนุญาตสำหรับข้อมูลเพิ่มเติม