1. ent کی تعارف

اینٹ فیس بک کا ایک انٹٹی فریم ورک ہے جو Go زبان کے لئے تیار کیا گیا ہے۔ یہ بڑے پیمانے پر ڈیٹا ماڈل ایپلیکیشنز کو بنانے اور ان کی نگرانی کو آسان بناتا ہے۔ اینٹ فریم ورک عام طور پر مندرجہ ذیل اصول کا مطابق چلتا ہے:

  • ڈیٹا بیس سکیما کو گراف سڑچر کے طور پر آسانی سے ماڈل کرنا۔
  • گو زبان کے کوڈ کی شکل میں سکیما کی تعریف کرنا۔
  • کوڈ جنریشن کے بنیادی پر اسٹیٹک ٹائپس کا اطلاق کرنا۔
  • ڈیٹا بیس کوئیریز اور گراف ٹری ورسل کو بہت آسان بنانا۔
  • گو ٹیمپلیٹس کا استعمال کرکے آسانی سے توسیع اور تخصیص کرنا۔

2. ماحول کی تشکیل

اینٹ فریم ورک کا استعمال شروع کرنے کے لئے یقینی بنائیں کہ آپ کے ترقیاتی ماحول میں گو زبان انسٹال ہے۔

اگر آپ کا پروجیکٹ ڈائریکٹری GOPATH کے باہر ہے یا آپ GOPATH کے ساتھ واقف نہیں ہیں، تو آپ مندرجہ ذیل کمانڈ کا استعمال کرکے ایک نیا گو ماڈیول پروجیکٹ بنانے کے لئے استعمال کرسکتے ہیں:

go mod init entdemo

یہ آپ کے entdemo پروجیکٹ کے لئے ایک نیا گو ماڈیول شروع کرے گا اور ایک نیا go.mod فائل بنائے گا۔

3. پہلا سکیما کی تعریف

3.1. ent CLI کا استعمال کرکے اسکیما کی تشکیل

پہلے، آپ کو اسکیما Nامَک یُزنگ ent CLI ٹول کا استعمال کرکے اپنے پروجیکٹ کی روٹ ڈائریکٹری میں مندرجہ ذیل کمانڈ کو چلانے کی ضرورت ہے تاکہ ایک اسکیما جس کا نام User ہے بنایں:

go run -mod=mod entgo.io/ent/cmd/ent new User

مندرجہ بالا کمانڈ entdemo/ent/schema/ ڈائرکٹری میں یوزَر سکیما بنائے گا:

فائل entdemo/ent/schema/user.go:

package schema

import "entgo.io/ent"

// User یوزَر entity کے لیے سکیما تعریف ہوتا ہے۔
type User struct {
    ent.Schema
}

// یوزَر کے فیلڈز۔
func (User) Fields() []ent.Field {
    return nil
}

// یوزَر کے ایجز۔
func (User) Edges() []ent.Edge {
    return nil
}

3.2. فیلڈز شامل کرنا

اگلے، ہمیں یوزَر اینٹٹی میں فیلڈ تعریفات شامل کرنے کی ضرورت ہوتی ہے۔ مندرجہ ذیل ایک مثال ہے یوزَر اینٹٹی کے دو فیلڈز شامل کرنے کی:

متن ترمیم شدہ فائل entdemo/ent/schema/user.go:

package schema

import (
    "entgo.io/ent"
    "entgo.io/ent/schema/field"
)

// یوزَر کے فیلڈز۔
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.Int("age").
            Positive(),
        field.String("name").
            Default("unknown"),
    }
}

یہ کوڈ یوزَر ماڈیل کے لئے دو فیلڈز کی تعریف کرتا ہے: age اور name، جہاں age مثبت عدد ہے اور name ایک مقامی چیز کی پورانی قیمت ہے۔

3.3. ڈیٹا بیس اینٹیٹیز جنریٹ کرنا

سکیما کی تعریف کے بعد، آپ کو مندرجہ ذیل کمانڈ کا استعمال کرکے اندراجی ڈیٹا بیس ایکسیس منصوبے کو جنریٹ کرنا ہوگا۔

آپ کے پروجیکٹ کی روٹ ڈائریکٹری میں مندرجہ ذیل کمانڈ کو چلائیں:

go generate ./ent

یہ کمانڈ پہلے سے تعریف شدہ سکیما کی بنیاد پر متعلقہ گو کوڈ کو جنریٹ کرے گا، اور مندرجہ ذیل فائل ساخت کرے گا:

ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── generate.go
├── mutation.go
... (brevity کیلئے کئی فائلز مختصر کردی گئی ہیں)
├── schema
│   └── user.go
├── tx.go
├── user
│   ├── user.go
│   └── where.go
├── user.go
├── user_create.go
├── user_delete.go
├── user_query.go
└── user_update.go

4.1. ڈیٹا بیس کنکشن کی شروعات

MySQL ڈیٹا بیس کے ساتھ کنکشن قائم کرنے کے لئے، ہم ent فریم ورک کی دی گئی Open فنکشن استعمال کر سکتے ہیں۔ پہلے، MySQL ڈرائیور کو امپورٹ کریں اور پھر درست کنکشن سٹرنگ فراہم کرکے ڈیٹا بیس کنکشن کو آغاز کریں۔

package main

import (
    "context"
    "log"

    "entdemo/ent"
    
    _ "github.com/go-sql-driver/mysql" // MySQL ڈرائیور کو امپورٹ کریں
)

func main() {
    // MySQL ڈیٹا بیس کے ساتھ کنکشن قائم کرنے کے لئے ent.Open کا استعمال کریں۔
    // یاد رکھیں کہ نیچے دی گئی خانوں "your_username"، "your_password"، اور "your_database" کو بدلیں۔
    client, err := ent.Open("mysql", "your_username:your_password@tcp(localhost:3306)/your_database?parseTime=True")
    if err != nil {
        log.Fatalf("mysql کنکشن کھولنے میں ناکامی: %v", err)
    }
    defer client.Close()

    // خودکار مائیگریشن ٹول کو چلایا جا سکتا ہے
    ctx := context.Background()
    if err := client.Schema.Create(ctx); err != nil {
        log.Fatalf("سکیما ریسورسز کی تخلیق میں ناکامی: %v", err)
    }
    
    // یہاں اضافی بزنس منطق لکھا جا سکتا ہے
}

4.2. انٹٹیٹیز تخلیق کرنا

صارف انٹٹیٹی کو تخلیق کرنا نئی انٹٹیٹی آبجیکٹ بنانا اور اسے Save یا SaveX میتھڈ کا استعمال کرکے ڈیٹا بیس میں ذخیرہ کرنا شامل ہوتا ہے۔ زیریں دی گئی کوڈ میں دکھایا گیا ہے کہ نئی یوزر انٹٹیٹی کو کیسے تخلیق کیا جاتا ہے اور دو فیلڈز age اور name کی ابتدائیت کیسے ہوتی ہے۔

// CreateUser فنکشن نئے یوزر انٹٹیٹی کو تخلیق کرنے کے لئے استعمال ہوتا ہے۔
func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
    // client.User.Create() کا استعمال کرکے یوزر کے لئے ریکویسٹ بنانے کے لئے،
    // پھر SetAge اور SetName میتھڈز کو زنجیر کی طرح جوڑیں تاکہ انٹٹیٹی فیلڈز کی قیمتیں مقرر کی جائیں۔
    u, err := client.User.
        Create().
        SetAge(30).    // یوزر عمر مقرر کریں
        SetName("a8m"). // یوزر نام مقرر کریں
        Save(ctx)     // انٹٹیٹی کو ڈیٹا بیس میں محفوظ کرنے کے لئے Save کو بلائیں
    if err != nil {
        return nil, fmt.Errorf("یوزر کی تخلیق میں ناکامی: %w", err)
    }
    log.Println("یوزر تخلیق ہوا: ", u)
    return u, nil
}

main فنکشن میں، آپ CreateUser فنکشن کو بلانے کے لئے نیا یوزر انٹٹیٹی تخلیق کرسکتے ہیں۔

func main() {
    // ... ڈیٹا بیس کنکشن قائم کرنے کے لئے دی گئی کوڈ کو چھوڑ دیا 

    // یوزر انٹٹیٹی تخلیق کریں
    u, err := CreateUser(ctx, client)
    if err != nil {
        log.Fatalf("یوزر تخلیق میں ناکامی: %v", err)
    }
    log.Printf("تخلیق شدہ یوزر: %#v\n", u)
}

4.3. انٹٹیٹیز کا سوال کرنا

انٹٹیٹیز کا سوال کرنے کے لئے، ہم ent کی طرف سے جنریٹ کردہ سوال بلڈر کا استعمال کرسکتے ہیں۔ زیریں دی گئی کوڈ میں دکھایا گیا ہے کہ کس طرح "a8m" نام کے یوزر کا سوال کیا جاتا ہے۔

// QueryUser فنکشن کو استعمال کرکے یوزر انٹٹیٹی کا مخصوص نام سوال کرنے کے لئے استعمال کیا جاتا ہے۔
func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
    // client.User.Query() کا استعمال کرکے یوزر کیلئے سوال بنانے کے لئے،
    // پھر Where میتھڈ کو زنجیر کی طرح جوڑیں تاکہ سوال کی شرائط شامل کی جائیں، جیسے نام کی تلاش کرنا
    u, err := client.User.
        Query().
        Where(user.NameEQ("a8m")).      // شرط شامل کریں، اس صورت میں، نام "a8m" ہے
        Only(ctx)                      // Only میتھڈ واحد نتیجہ متوقع کرتا ہے
    if err != nil {
        return nil, fmt.Errorf("یوزر کا سوال کرنے میں ناکامی: %w", err)
    }
    log.Println("واپس آیا یوزر: ", u)
    return u, nil
}

main فنکشن میں، آپ QueryUser فنکشن کو بلانے کے لئے یوزر انٹٹیٹی کا سوال کرسکتے ہیں۔

func main() {
    // ... ڈیٹا بیس کنکشن قائم کرنے، یوزر تخلیق کرنے والا کوڈ کو چھوڑ دیا 

    // یوزر انٹٹیٹی کا سوال کریں
    u, err := QueryUser(ctx, client)
    if err != nil {
        log.Fatalf("یوزر کا سوال کرنے میں ناکامی: %v", err)
    }
    log.Printf("سوال کردہ یوزر: %#v\n", u)
}

5.1. کناروں اور انکرس کناروں کا سمجھنا

ent فریم ورک میں، ڈیٹا ماڈل کو گراف ساخت میں تشاکلی کیا جاتا ہے، جہاں انٹٹیٹیز گراف میں نوڈز کی حیثیت اختیار کرتے ہیں، اور انٹٹیٹیز کے درمیان تعلقات کو کناروں سے ظاہر کیا جاتا ہے۔ ایک کنارا ایک انٹٹیٹی سے دوسری انٹٹیٹی تک کا رابطہ ہوتا ہے، مثال کے طور پر، ایک صارف کئی کاروں کو ملکری کرسکتا ہے۔

انکرس کنارے کنارے کنارے کو الٹے حوالے کو ظاہر کرتے ہیں، منطقی طور پر انٹٹیٹیز کے الٹ تعلق کو ظاہر کرتے ہیں، لیکن ڈیٹا بیس میں نیا تعلق نہیں بناتے۔ مثال کے طور پر، ایک کار کے انکرس کنارے کے ذریعے ہم اس کار کا مالک ڈھونڈ سکتے ہیں۔

کناروں اور انکرس کناروں کا کلیدی اہمیت اس میں ہے کہ جڑیں انٹٹیٹیز کے درمیان منسلک ہونے کا بہت ہی آسان اور سیدھا بناتے ہیں۔

مشورہ: ent میں، کنارے روایتی ڈیٹا بیس فارن کیز کے مترادف ہوتے ہیں اور خواص کے درمیان تعلقات کا تعین کرنے کے لئے استعمال ہوتے ہیں۔

5.2. سکیما میں کنارے تعریف کرنا

پہلے، ہم Car اور Group کے لئے ابتدائی سکیما بنانے کے لئے ent CLI استعمال کریں گے:

go run -mod=mod entgo.io/ent/cmd/ent new Car Group

پھر، ہم User سکیما میں، Car کے ساتھ کنارا تعریف کرتے ہیں۔ ہم cars نام کے ایک کنارا شامل کر سکتے ہیں جو صارف انٹٹیٹیپر ہو تاکہ دکھایا جا سکے کہ ایک صارف کے پاس متعدد کار ہو سکتی ہیں۔

// entdemo/ent/schema/user.go

// کنارے صارف کے.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("cars", Car.Type),
    }
}

کناروں کی تعریف کے بعد، ہمیں دوبارہ go generate ./ent کو چلانا ہوگا تاکہ موازی کوڈ جنریٹ ہوسکے۔

5.3. کنارے ڈیٹا پر عمل کرنا

صارف کے ساتھ جڑی ہوئی کاروں کو بنانا ایک آسان عمل ہے۔ دیے گئے صارف انٹٹیٹیپر کے بعد، ہم نیا کار انٹٹیٹیپر بناسکتے ہیں اور اس کو صارف سے منسلک کرسکتے ہیں۔

import (
    "context"
    "log"
    "entdemo/ent"
    // یقینی بنانے کے لئے کہ درستی کار کی تعریف شامل ہے
    _ "entdemo/ent/schema"
)

func CreateCarsForUser(ctx context.Context, client *ent.Client, userID int) error {
    user, err := client.User.Get(ctx, userID)
    if err != nil {
        log.Fatalf("صارف حاصل کرنے میں ناکام: %v", err)
        return err
    }

    // نیا کار بنانا اور اسے صارف کے ساتھ منسلک کرنا
    _, err = client.Car.
        Create().
        SetModel("ٹیسلا").
        SetRegisteredAt(time.Now()).
        SetOwner(user).
        Save(ctx)
    if err != nil {
        log.Fatalf("صارف کے لئے کار بنانے میں ناکام: %v", err)
        return err
    }

    log.Println("کار بنایا گیا اور صارف کے ساتھ منسلک کیا گیا")
    return nil
}

اسی طرح، صارف کی کاروں کا سوال کرنا بھی سیدھا ہے۔ اگر ہم ایک صارف کے ذریعے وہ ساری کاروں کو حاصل کرنا چاہتے ہیں، تو ہم نیچے دیا گیا کام کرسکتے ہیں:

func QueryUserCars(ctx context.Context, client *ent.Client, userID int) error {
    user, err := client.User.Get(ctx, userID)
    if err != nil {
        log.Fatalf("صارف حاصل کرنے میں ناکام: %v", err)
        return err
    }

    // صارف کی تمام کاروں کا سوال کرنا
    cars, err := user.QueryCars().All(ctx)
    if err != nil {
        log.Fatalf("کاروں کا سوال کرنا ناکام: %v", err)
        return err
    }

    for _, car := range cars {
        log.Printf("کار: %v, ماڈل: %v", car.ID, car.Model)
    }
    return nil
}

یہ تمام اقدامات کے ذریعے، ہم نے نہ صرف سکیما میں کناروں کی تعریف کرنے کا طریقہ سیکھا بلکہ دکھایا بھی کہ کناروں سے منسلک ڈیٹا کو کیسے بنایا اور سوال کیا جاتا ہے۔

6. گراف ٹریورسل اور کوئیری کرنا

6.1. گراف ساخت کو سمجھنا

ent میں، گراف ساخت انٹٹیٹیز اور ان کے درمیان کناروں کو ظاہر کرتے ہیں۔ ہر انٹٹیٹی ایک نوڈ کے مترادف ہے، اور انٹٹیز کے درمیان تعلقات کو کناروں سے ظاہر کیا جاتا ہے، جو ایک سے ایک، ایک سے متعدد، متعدد سے متعدد، وغیرہ ہوسکتے ہیں۔ یہ گراف ساخت، ریلیشنل ڈیٹا بیس پر پیچیدہ کوئیریز اور عملیات کو سادہ اور آسان بناتا ہے۔

6.2. گراف ساختاروں کاٹراورس

گراف ٹریورسل کوڈ لکھنا عموماً خود ہی قوائد کا تعین کرنے اور انفرادی جوڑوں کے درمیان کنکشن کے ذریعے ڈیٹا کی سروی کرنے پر مشتمل ہوتا ہے۔ نیچے ایک مثال دی گئی ہے جو دکھاتی ہے کہ ent میں گراف ساختار کو کیسے ٹریورس کیا جاتا ہے۔

import (
    "context"
    "log"

    "entdemo/ent"
)

// GraphTraversal is an example of traversing the graph structure
func GraphTraversal(ctx context.Context, client *ent.Client) error {
    // Query the user named "Ariel"
    a8m, err := client.User.Query().Where(user.NameEQ("Ariel")).Only(ctx)
    if err != nil {
        log.Fatalf("Failed querying user: %v", err)
        return err
    }

    // Traverse all the cars belonging to Ariel
    cars, err := a8m.QueryCars().All(ctx)
    if err != nil {
        log.Fatalf("Failed querying cars: %v", err)
        return err
    }
    for _, car := range cars {
        log.Printf("Ariel has a car with model: %s", car.Model)
    }

    // Traverse all the groups Ariel is a member of
    groups, err := a8m.QueryGroups().All(ctx)
    if err != nil {
        log.Fatalf("Failed querying groups: %v", err)
        return err
    }
    for _, g := range groups {
        log.Printf("Ariel is a member of group: %s", g.Name)
    }

    return nil
}

اوپر دیا گیا کوڈ گراف ٹریورسل کا ایک بنیادی مثال ہے، جو پہلے ایک صارف کی کوائیری کرتا ہے اور پھر صارف کی گاڑیوں اور گروپس کو ٹریورس کرتا ہے۔

7. ڈیٹا بیس سکیما کی تصوری تشریح

7.1. ایٹلس ٹول کی انسٹالیشن

ent کے ذریعے جنریٹ ہونے والے ڈیٹا بیس سکیما کو تصور کرنے کے لئے ہم ایٹلس ٹول کا استعمال کرسکتے ہیں۔ ایٹلس کی انسٹالیشن کے لئے اقدامات بہت ہی آسان ہیں۔ مثلاً، میک او ایس پر آپ اسے brew کا استعمال کرکے انسٹال کرسکتے ہیں۔

brew install ariga/tap/atlas

نوٹ: ایٹلس ایک عام ڈیٹا بیس مائیگریشن ٹول ہے جو مختلف ڈیٹا بیسوں کے لئے ٹیبل سکیما ورژن منیجمنٹ کرسکتا ہے۔ ایٹلس کا تفصیلی تعارف بعد کے بابوں میں فراہم کیا جائے گا۔

7.2. ERD اور SQL سکیما جنریشن

ایٹلس کا استعمال کرکے سکیماز دیکھنا اور ایکسپورٹ کرنا بہت ہی سیدھا ہے۔ ایٹلس کو انسٹال کرنے کے بعد، آپ مندرجہ ذیل کمانڈ کا استعمال کرکے انٹٹٹی-رشپیلشن ڈائیاگرام (ERD) دیکھ سکتے ہیں:

atlas schema inspect -d [database_dsn] --format dot

یا سیدھے اس کوء سی کوء ایسکیما جنریٹ کرسکتے ہیں:

atlas schema inspect -d [database_dsn] --format sql

جہاں [database_dsn] آپ کے ڈیٹا بیس کے ڈیٹا سورس نام (DSN) کو پوینٹ کرتا ہے۔ مثال کے طور پر، ایک SQLite ڈیٹا بیس کے لئے، یہ یو ایس ایکسٹراکٹ مائیٹ بہتا سکتا ہے:

atlas schema inspect -d "sqlite://file:ent.db?mode=memory&cache=shared" --format dot

ان کمانڈز سے جنریٹ کردہ اوٹپٹ کو مختلف ٹولز یا دستاویزات میں بدلنا مومکین ہے۔

8. سکیما مائیگریشن

8.1. خود کار مائیگریشن اور ورژن منیجر مائیگریشن

ent دو سکیما مائیگریشن استریٹیجیز کا سپورٹ کرتا ہے: خود کار مائیگریشن اور ورژند مائیگریشن۔ خود کار مائیگریشن وقت کے دوران سکیما کی تبدیلیوں کو دیکھتا ہے اور انہیں اطلاق کرنا ہوتا ہے، جو ترقی اور ٹیسٹنگ کے لئے مناسب ہوتا ہے۔ ورژند مائیگریشن شامل مائیگریشن اسکرپٹس جنریٹ کرنے کا عمل ہے اور اسے پروڈکشن ڈیپلوئمنٹ سے قبل دقیق جائزے اور ٹیسٹنگ کی ضرورت ہوتی ہے۔

ٹپ: خود کار مائیگریشن کے لئے، سیکشن 4.1 میں دی گئی مواد کا حوالہ دیں۔

8.2. ورژند مائیگریشن کرنے کا عمل

ورژند مائیگریشن کا عمل مائیگریشن فائلز کو ایٹلس کے ذریعے جنریٹ کرنے کے ذریعے ہوتا ہے۔ مندرجہ ذیل متعلقہ کمانڈز ہیں:

مائیگریشن فائلز جنریٹ کرنے کے لئے:

atlas migrate diff -d ent/schema/path --dir migrations/dir

اس کے بعد، یہ مائگریشن فائلز دیٹابیس پر اطلاق کیا جاسکتے ہیں:

atlas migrate apply -d migrations/dir --url database_dsn

اس پروسیس کے بعد، آپ دیتابیس مائیگریشن کی ایک تاریخ کو نگرانی کرسکتے ہیں اور ہر مائیگریشن سے پہلے بہترین جائزے کی اطلاق کرسکتے ہیں۔

ٹپ: مثال کے کوڈ کے لئے ریفرنس کے طور پر https://github.com/ent/ent/tree/master/examples/start کا اشارہ کریں۔