1. แนวคิดพื้นฐานของ Entity และ Association

ในเฟรมเวิร์ก ent องค์กรหมายถึงหน่วยข้อมูลพื้นฐานที่จัดการในฐานข้อมูล ซึ่งโดยทั่วไปมักจะสอดคล้องกับตารางในฐานข้อมูล ฟิลด์ในองค์กรสอดคล้องกับคอลัมน์ในตาราง ในขณะเดียวกันการมีการเชื่อมโยง (เชื่อมต่อ) ระหว่างองค์กรถูกใช้เพื่ออธิบายความสัมพันธ์และความขึ้นอยู่ระหว่างองค์กร การเชื่อมโยงขององค์กรเป็นพื้นฐานสำหรับการสร้างโมเดลข้อมูลที่ซับซ้อน ทำให้เป็นไปได้ที่จะแทนสถานการณ์ที่มีความสัมพันธ์แบบชั้นย่อย เช่น ความสัมพันธ์ระหว่างพ่อแม่และลูก และความสัมพันธ์เจ้าของสิทธิ์

เฟรมเวิร์ก ent มี APIs ที่หลากหลายที่ให้นักพัฒนาสามารถนิยามและจัดการเชื่อมโยงเหล่านี้ในโครงสร้างขององค์กร ผ่านเชื่อมโยงเหล่านี้ เรารสามารถแสดงออกและดำเนินการกับตรรกะธุรกิจที่ซับซ้อนได้อย่างง่ายดาย

2. ประเภทของการเชื่อมโยงของ Entity ใน ent

2.1 การเชื่อมโยงแบบหนึ่งต่อหนึ่ง (O2O) Association

การเชื่อมโยงแบบหนึ่งต่อหนึ่งหมายถึงความสอดคล้องแบบหนึ่งต่อหนึ่งระหว่างองค์กรสององค์กร ตัวอย่างเช่น ในกรณีของผู้ใช้และบัญชีธนาคาร แต่ละผู้ใช้สามารถมีเพียงหนึ่งบัญชีธนาคาร และแต่ละบัญชีธนาคารก็เป็นของผู้ใช้โดยเดียว ฟังก์ชัน edge.To และ edge.From ในเฟรมเวิร์ก ent ใช้ในการนิยามการเชื่อมโยงเช่นนี้

ก่อนอื่นเราสามารถนิยามการเชื่อมโยงแบบหนึ่งต่อหนึ่งที่ชี้ไปยัง Card ภายในโครงสร้าง User ได้เป็นวิธีนี้:

// เชื่อมโยงของ User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("card", Card.Type). // ชี้ไปที่องค์กร Card และกำหนดชื่อเชื่อมโยงเป็น "card"
            Unique(),               // ฟังก์ชัน Unique รับประกันว่านี่คือการเชื่อมโยงแบบหนึ่งต่อหนึ่ง
    }
}

ต่อมา เราจะนิยามการเชื่อมโยงตรงกลับไปที่ User ภายในโครงสร้าง Card ต่อไปนี้:

// เชื่อมโยงของ Card.
func (Card) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("owner", User.Type). // ชี้กลับไปยัง User จาก Card และกำหนดชื่อเชื่อมโยงเป็น "owner"
            Ref("card").              // ฟังก์ชัน Ref ระบุชื่อการเชื่อมโยงตรงกลับที่เหมือนกัน
            Unique(),                 // แสดงถึงผู้ที่ถูกสั่งให้เพื่อให้แน่ใจว่าบัตรหนึ่งสัมพัสวัตถุี
    }
}

2.2 การเชื่อมโยงแบบหนึ่งต่อมากกว่าหนึ่ง (O2M) Association

การเชื่อมโยงแบบหนึ่งต่อมากกว่าหนึ่งระบุว่าองค์กรหนึ่งสามารถมีการเชื่อมโยงกับองค์กรอื่นหลายๆ องค์กร แต่องค์กรเหล่านี้สามารถชี้กลับไปที่องคกรที่เดียวกันเท่านั้น ตัวอย่างเช่น ผู้ใช้อาจมีสัตว์เลี้ยงหลายตัว แต่แต่ละสัตว์เลี้ยงก็มีเพียงผู้เจ้าของตัวเดียว

ใน ent เรายังคงใช้ edge.To และ edge.From เพื่อนิยามประเภทการเชื่อมโยงเช่นนี้ ตัวอย่างต่อไปนี้นำเสนอการเชื่อมโยงแบบหนึ่งต่อมากกว่าหนึ่งระหว่างผู้ใช้และสัตว์เลี้ยง:

// เชื่อมโยงของ User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("pets", Pet.Type), // การเชื่อมโยงแบบหนึ่งต่อมากกว่าหนึ่งจากองค์กร User ไปยังองค์กร Pet
    }
}

ในองค์กร Pet เรานิยามการเชื่อมโยงแบบมากต่อหนึ่งกลับไปที่ User ต่อไปนี้:

// เชื่อมโยงของ Pet.
func (Pet) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("owner", User.Type). // การเชื่อมโยงแบบมากต่อหนึ่งจาก Pet ไปยัง User
            Ref("pets").              // ระบุชื่อการเชื่อมโยงตรงกลับจาก pet ไปยัง owner
            Unique(),                 // รับประกันว่าเจ้าของหนึ่งคนสามารถมีสัตว์เลี้ยงหลายตัว
    }
}

2.3 การเชื่อมโยงแบบมากต่อมาก (M2M) Association

การเชื่อมโยงแบบมากต่อมากช่วยให้องค์กรสองประเภทสามารถมีตัวอย่างหลายตัวขององค์กรตัวเอง ตัวอย่างเช่น นักศึกษาสามารถลงทะเบียนเรียนในหลายรายวิชา และรายวิชาก็สามารถมีนักศึกษาตัวอย่างหลายท่านที่ลงทะเบียนเรียนอยู่เช่นเดียวกัน ent มี APIs ที่เพื่อนุนการเชื่อมโยงแบบมากต่อมากได้:

ในองค์กร Student เราใช้ edge.To ในการนิยามการเชื่อมโยงแบบมากต่อมากกับ Course:

// เชื่อมโยงของ Student.
func (Student) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("courses", Course.Type), // นิยามการเชื่อมโยงแบบมากต่อมากจาก Student ไปยัง Course
    }
}

ในทำเดียวกันในองค์กร Course เรานิยามการเชื่อมโยงตรงกลับไปยัง Student สำหรับความสัมพันธ์แบบมากต่อมากต่อไปนี้:

// เชื่อมโยงของ Course.
func (Course) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("students", Student.Type). // นิยามการเชื่อมโยงแบบมากต่อมากจาก Course ไปยัง Student
            Ref("courses"),                  // ระบุชื่อการเชื่อมโยงตรงกลับจาก Course ไปยัง Student
    }
}

การเชื่อมโยงเหล่านี้เป็นฐานรากสำคัญของการสร้างโมเดลข้อมูลแอปพลิเคชั่นที่ซับซ้อน และความเข้าใจในวิธีการนิยามและใช้เชื่อมโยงใน ent เป็นสิ่งสำคัญสำหรับการขยายโมเดลข้อมูลและตรรกะธุรกิจข้อมูลเช่นใด

3. การดำเนินการพื้นฐานสำหรับการเชื่อมโยงของ Entity

ในส่วนนี้จะแสดงวิธีการดำเนินการพื้นฐานโดยใช้ ent กับความเกี่ยวข้องที่กำหนดไว้ ซึ่งรวมถึงการสร้าง การคิวรี่ และการเดินทางข้าม Entity ที่เกี่ยวข้อง

3.1 การสร้าง Associated Entities

ในขณะที่สร้าง entities คุณสามารถกำหนดความสัมพันธ์ระหว่าง entities พร้อมกัน สำหรับความสัมพันธ์ one-to-many (O2M) และ many-to-many (M2M) คุณสามารถใช้วิธี Add{Edge} เพื่อเพิ่ม associated entities

ตัวอย่างเช่น หากเรามี user entity และ pet entity ที่มีความสัมพันธ์บางอย่าง โดยที่ผู้ใช้สามารถมีสัตว์เลี้ยงหลายตัว ต่อไปนี้เป็นตัวอย่างของการสร้างผู้ใชมงใหม่และเพิ่มสัตว์เลี้ยงให้กับพวกเขา:

// สร้างผู้ใช้และเพิ่มสัตว์เลี้ยง
func CreateUserWithPets(ctx context.Context, client *ent.Client) (*ent.User, error) {
    // สร้างตัวอย่างของสัตว์เลี้ยง
    fido := client.Pet.
        Create().  
        SetName("Fido").
        SaveX(ctx)
    // สร้างตัวอย่างของผู้ใช้และเชื่อมโยงกับสัตว์เลี้ยง
    user := client.User.
        Create().
        SetName("Alice").
        AddPets(fido). // ใช้วิธี AddPets เพื่อเชื่อมโยงสัตว์เลี้ยง
        SaveX(ctx)

    return user, nil
}

ในตัวอย่างนี้เราก่อนที่จะสร้างตัวอย่างของสัตว์เลี้ยงที่ชื่อ Fido จากนั้นสร้างผู้ใช้ที่ชื่อ Alice และเชื่อมโยงตัวอย่างของสัตว์เลี้ยงกับผู้ใช้ โดยใช้วิธี AddPets

3.2 การคิวรี่ Associated Entities

การคิวรี่ associated entities เป็นการดำเนินการที่ธรรมดาใน ent เช่นเดียวกัน ตัวอย่างเช่น คุณสามารถใช้วิธี Query{Edge} เพื่อเรียกดู entities ที่เกี่ยวข้องกับ entity ที่กำหนด

ต่อจากตัวอย่างของผู้ใช้และสัตว์เลี้ยงของเรา นี่คือวิธีการคิวรี่สัตว์เลี้ยงทั้งหมดที่เป็นเจ้าของโดยผู้ใช้:

// คิวรี่สัตว์เลี้ยงทั้งหมดของผู้ใช้
func QueryUserPets(ctx context.Context, client *ent.Client, userID int) ([]*ent.Pet, error) {
    pets, err := client.User.
        Get(ctx, userID). // รับ instance ของผู้ใช้ตาม user ID
        QueryPets().      // คิวรี่ entities ของสัตว์เลี้ยงที่เกี่ยวข้องกับผู้ใช้
        All(ctx)          // ส่งคืน entities ทั้งหมดที่ถูกคิวรี่
    if err != nil {
        return nil, err
    }

    return pets, nil
}

ในโค้ดด้านบน เราจะรับ instance ของผู้ใช้ตาม user ID จากนั้นเรียกใช้วิธี QueryPets เพื่อดึงข้อมูล entities ของสัตว์เลี้ยงที่เกี่ยวข้องกับผู้ใช้

หมายเหตุ: เครื่องมือสร้างโค้ดของ ent สร้าง API โดยอัตโนมัติสำหรับการคิวรี่ความสัมพันธ์จาก entities ที่กำหนดไว้ แนะนำให้ตรวจสอบโค้ดที่สร้างขึ้นมาด้วย

โหลดข้อมูลสัมพันธ์ของมิตร

ถ้าเราต้องการดึงข้อมูลของผู้ใช้ทั้งหมดจากฐานข้อมูลและโหลดข้อมูลสัมพันธ์ของสัตว์เลี้ยง เราสามารถทำได้โดยเขียนโค้ดต่อไปนี้:

users, err := client.User.
    Query().
    WithPets().
    All(ctx)
if err != nil {
    // จัดการข้อผิดพลาด
    return err
}
for _, u := range users {
    for _, p := range u.Edges.Pets {
        fmt.Printf("ผู้ใช้ (%v) เป็นเจ้าของสัตว์เลี้ยง (%v)\n", u.ID, p.ID)
    }
}

ในตัวอย่างนี้ เราใช้ WithPets method เพื่อขอ ent โหลดข้อมูลของสัตว์เลี้ยงที่เชื่อมโยงกับผู้ใช้ไป เราสามารถเข้าถึงข้อมูลสัมพันธ์นี้ผ่านฟิลด์ Edges.Pets ที่ถูกโหลดไว้

โหลดข้อมูลสัมพันธ์หลายอย่างพร้อมกัน

ent ช่วยให้เราโหลดข้อมูลสัมพันธ์หลายอย่างพร้อมกัน และยังระบุการโหลดข้อมูลสัมพันธ์ที่ซ้อนอยู่ด้วย สามารถกรอง จัดเรียง หรือ จำกัดจำนวนของผลลัพธ์ที่โหลดได้ด้วย ตัวอย่างด้านล่างเป็นการโหลดข้อมูลของสัตว์เลี้ยงของผู้ดูแลระบบ และทีมที่พวกเขาเป็นส่วนหนึ่ง พร้อมกับโหลดข้อมูลของผู้ใช้ที่เกี่ยวข้องกับทีมด้วย:

admins, err := client.User.
    Query().
    Where(user.Admin(true)).
    WithPets().
    WithGroups(func(q *ent.GroupQuery) {
        q.Limit(5)          // จำกัดไว้ที่ 5 ทีมแรก
        q.Order(ent.Asc(group.FieldName)) // เรียงลำดับตามชื่อทีมจากน้อยไปมาก
        q.WithUsers()       // โหลดข้อมูลของผู้ใช้ในทีมด้วย
    }).
    All(ctx)
if err != nil {
    // จัดการข้อผิดพลาด
    return err
}
for _, admin := range admins {
    for _, p := range admin.Edges.Pets {
        fmt.Printf("ผู้ดูแลระบบ (%v) เป็นเจ้าของสัตว์เลี้ยง (%v)\n", admin.ID, p.ID)
    }
    for _, g := range admin.Edges.Groups {
        fmt.Printf("ผู้ดูแลระบบ (%v) เป็นส่วนหนึ่งของทีม (%v)\n", admin.ID, g.ID)
        for _, u := range g.Edges.Users {
            fmt.Printf("ทีม (%v) มีสมาชิก (%v)\n", g.ID, u.ID)
        }
    }
}

ผ่านตัวอย่างนี้ คุณจะเห็นว่า ent เป็นเครื่องมือที่มีพลังและยืดหยุ่นมาก ด้วยเพียงไม่กี่บรรทัดโค้ดเราสามารถโหลดข้อมูลที่เกี่ยวข้องมาอย่างมีประสิทธิภาพอย่างมาก และจัดเรียงข้อมูลเหล่านั้นให้อยู่ในรูปแบบที่ชัดเจน ทำให้สะดวกสบายในการพัฒนาแอปพลิเคชันที่ใช้ข้อมูลอย่างมาก