1. ہکس میکینزم

ہکس میکینزم ایک طریقہ ہے جو ہمیں ڈیٹا بیس آپریشنز میں مخصوص تبدیلیاں ہونے سے پہلے یا بعد میں کسٹم لاجک شامل کرنے کے لیے فراہم کرتا ہے۔ جب ہم ڈیٹا بیس سکیما کو تبدیل کرتے ہیں، جیسے کہ نؤوں کی نوڈز شامل کرنا، نوڈز کے درمیان تعلقات کو حذف کرنا، یا متعدد نوڈز کو حذف کرنا، ہم ہکس کا استعمال کرتے ہیں تاکہ ہم اس میں ڈیٹا کی تصدیق، لاگ ریکارڈنگ، اجازت کی چیکس یا کسٹم آپریشن کر سکیں۔ یہ معاون ہے تاکہ ہم ڈیٹا کی تسلسل اور کام کی قوانین کے مطابقت کو یقینی بنا سکیں، جبکہ یہ ساتھ ہی ساتھ ڈویلپرز کو اصل کاروباری منطق کو تبدیل کئے بغیر اضافی فنکشنلٹی شامل کرنے کا مواقع فراہم کرتا ہے۔

2. ہک رجسٹریشن میتھڈ

2.1 گلوبل ہکس اور لوکل ہکس

گلوبل ہکس (رن ٹائم ہکس) گراف کے تمام قسم کے آپریشنز کے لیے فعال ہوتے ہیں۔ یہ سارے ایپلیکیشن میں منطق شامل کرنے کے لیے مناسب ہوتے ہیں، جیسے کہ لاگنگ اور مانیٹرنگ۔ لوکل ہکس (اسکیما ہکس) مخصوص قسم سکیما کے اندر تعریف کیے جاتے ہیں اور صرف اُن تبدیلیوں پر لاگو ہوتے ہیں جو اُن سکیما کی قسم سے مشابہت رکھتی ہوں۔ لوکل ہکس استعمال کرنے سے مخصوص نوڈ کی ساری منطق ایک ہی جگہ میں جمع ہوتی ہے، یعنی سکیما کی تعریف میں۔

2.2 ہکس رجسٹریشن میں اقدامات

کوڈ میں ہک رجسٹر کرنا عموماً مندرجہ ذیل اقدامات کو شامل کرتا ہے:

  1. ہک فنکشن کی التعریف کرنا۔ یہ فنکشن ایک ent.Mutator لیتا ہے اور ایک ent.Mutator واپس بھیجتا ہے۔ مثلاً، ایک سادہ لاگنگ ہک بنانا۔
logHook := func(next ent.Mutator) ent.Mutator {
    return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
        // تبدیلی کے عمل سے پہلے لاگز پرنٹ کریں
        log.Printf("Before mutating: Type=%s, Operation=%s\n", m.Type(), m.Op())
        // تبدیلی کا عمل انجام دیں
        v, err := next.Mutate(ctx, m)
        // تبدیلی کے عمل کے بعد لاگز پرنٹ کریں
        log.Printf("After mutating: Type=%s, Operation=%s\n", m.Type(), m.Op())
        return v, err
    })
}
  1. کلائنٹ کے ساتھ ہک رجسٹر کریں۔ گلوبل ہکس کے لیے، آپ انہیں کلائنٹ کے Use میتھڈ کا استعمال کر کے رجسٹر کر سکتے ہیں۔ لوکل ہکس کے لیے، آپ قسم کی اسکیما میں Hooks میتھڈ کا استعمال کر کے انہیں رجسٹر کر سکتے ہیں۔
// گلوبل ہک رجسٹر کریں
client.Use(logHook)

// لوکل ہک رجسٹر کریں، صرف یوزر قسم کے لیے
client.User.Use(func(next ent.Mutator) ent.Mutator {
    return hook.UserFunc(func(ctx context.Context, m *ent.UserMutation) (ent.Value, error) {
        // مخصوص منطق شامل کریں
        // ...
        return next.Mutate(ctx, m)
    })
})
  1. آپ متعدد ہکس چین کرسکتے ہیں، اور وہ رجسٹر کرنے کے ترتیب کے تابع میں انجام دیئے جائیں گے۔

3. ہکس کی انجام دینے کا ترتیب

ہکس کی انجام دینے کا ترتیب کلائنٹ کے ساتھ رجسٹر کرنے کے ترتیب پر منحصر ہوتا ہے۔ مثال کے طور پر، client.Use(f, g, h) ترتیب سے میوٹیشن آپریشن پر انجام دیں گے جیسے کہ f(g(h(...)))۔ اس مثال میں، fمخصوص ہکس ہوتا ہے جو سب سے پہلے انجام دیتا ہے، اس کے بعدg، اور آخر میں h`۔

اس بات کا خیال رکھنا اہم ہے کہ رن ٹائم ہکس (رن ٹائم ہکس) اسکیما ہکس (اسکیما ہکس) سے سبقت رکھتے ہیں۔ یعنی اگر g اور h اسکیما میں تعریف کی گئی ہوں جبکہ f client.Use(...) کا استعمال کر کے رجسٹر ہوا ہو، تو ان کا انجام دینے کا ترتیب f(g(h(...))) ہوگا۔ یہ یقینی بناتا ہے کہ گلوبل منطق، جیسے لاگنگ، سب سے پہلے سب ہکس کا انجام دے۔

4. ہکس سے پیدا ہونے والے مسائل کا حل

جب ہم ہکس کا استعمال کر کے ڈیٹا بیس آپریشنز کو تشکیل دیتے ہیں، تو ہمیں ہکس سے وابستہ مسائل کا سامنا کرنا پڑ سکتا ہے۔ عام طور پر یہ مسئلہ واقع ہوتا ہے جب سکیما ہکس کا استعمال کرنے کی کوشش کی جاتی ہے، کیونکہ ent/schema پیکیج ent کور پیکیج کو شامل کرے گا۔ اگر ent کور پیکیج بھی ent/schema کو شامل کرنے کی کوشش کرے، تو ایک دائرہ محیطی تعلق پیدا ہوگا۔

دائرہ محیطی تعلقات کی وجوہات

دائرہ محیطی تعلقات عام طور پر اس وجہ سے پیدا ہوتے ہیں کہ سکیما تعریفات اور تیار کردہ انٹٹٹی کوڈ کے درمیان دو طرفہ تعلقات ہوتے ہیں۔ یعنی ent/schema ent پر منحصر ہوتا ہے (کیونکہ اسے ent فریم ورک کے دوارہ استعمال کرنا پڑتا ہے)، جبکہ ent کا جنریٹ کردہ کوڈ بھی ent/schema پر منحصر ہوتا ہے (کیونکہ اسے اس کے اندر معین کی تعریف کی معلومات تک رسائی چاہئیے۔)

دائری تعلقات کا حل کرنا

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

  1. سب سے پہلے ، ent/schema میں استعمال کردہ ہوکس پر تبصرہ لگائیں۔
  2. اگلے ، ent/schema میں تعریف شدہ شخصی اقسام کو ایک نئی پیکیج میں منتقل کریں، مثلاً آپ ent/schema/schematype نام کی ایک پیکیج بنا سکتے ہیں۔
  3. go generate ./... کمانڈ چلائیں تاکہ ent پیکیج کو اپ ڈیٹ کیا جائے، تاکہ وہ نئے پیکیج کا راستہ دیکھے، اور اسکی سکیما میں قسم کے حوالوں کو اپ ڈیٹ کریں۔ مثلاً ، schema.T کو schematype.T میں تبدیل کریں۔
  4. پہلے تبصرہ کردہ ہوکس حوالوں کو آن تبصرہ کریں اور دوبارہ go generate ./... کمانڈ کو دوبارہ چلائیں۔ اس نقطے پر ، کوڈ جنریشن بغیر خرابیوں کے آگے بڑھنا چاہئے گا۔

ان اقدامات کی پیروی کر کے، آپ ہمیشہ کے لئے ہمزاد داخلی خرابی کو حل کر سکتے ہیں جو ہکس کے درآمدات سے پیدا ہوئی ہوتی ہے، اور ہمارے پلان اور ہکس کے تنظیم کا حسبہ اٹھانا یقینی بنا سکتے ہیں۔

5. ہک مددگار فنکشنز کا استعمال

ent فریم ورک ہمیں ایک سیٹ کے ہک مددگار فنکشنز فراہم کرتا ہے، جو ہمیں ہکس کی اجراء وقت کو کنٹرول کرنے میں مدد فراہم کر سکتے ہیں۔ ذیل میں کچھ عام طور پر استعمال ہونے والے ہک مددگار فنکشنز کی مثالیں ہیں:

// صرف UpdateOne اور DeleteOne عمل کے لئے ہک A کو کال کریں
hook.On(HookA(), ent.OpUpdateOne|ent.OpDeleteOne)

// ہک B کو Create عمل کے دوران نہیں چلائیں
hook.Unless(HookB(), ent.OpCreate)

// ہک C صرف جب Mutation "status" فیلڈ میں تبدیلی لارہی ہو اور "dirty" فیلڈ کو صاف کر رہی ہو تو چلائیں
hook.If(HookC(), hook.And(hook.HasFields("status"), hook.HasClearedFields("dirty")))

// Update (multiple) عمل کے دوران "password" فیلڈ کو تبدیل نہیں کیا جاسکتا ہے
hook.If(
    hook.FixedError(errors.New("password cannot be edited on update many")),
    hook.And(
        hook.HasOp(ent.OpUpdate),
        hook.Or(
            hook.HasFields("password"),
            hook.HasClearedFields("password"),
        ),
    ),
)

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

6. ٹرانزیکشن ہکس

ٹرانزیکشن ہکس وقت کو کنٹرول کرنے کی اجازت دیتے ہیں کہ کسی خاص ہکس کو جب ایک ٹرانزیکشن مکمل ہوتی ہے (Tx.Commit) یا واپس لینے (Tx.Rollback) والے وقت چلایا جائے۔ یہ ماہرین عملیات کی اتساکی اور آٹومیٹکو کی لئے بہت مفید ہوتا ہے۔

ٹرانزیکشن ہکس کا مثال

client.Tx(ctx, func(tx *ent.Tx) error {
    // ٹرانزیکشن ہکس رجسٹر کرنا - ہکBeforeCommit مکمل ہونے سے پہلے چلایا جائے گا
    tx.OnCommit(func(next ent.Committer) ent.Committer {
        return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error {
            // واقعی مکمل ہونے سے پہلے منطق یہاں رکھا جا سکتا ہے۔
            fmt.Println("Before commit")
            return next.Commit(ctx, tx)
        })
    })

    // ٹرانزیکشن کے اندر سلسلہ مندرج عملیات کو کریں...

    return nil
})

اوپر دیے گؓے کوڈ میں دکھایا گیا ہے کہ کیسے ایک ٹرانزیکشن مکمل ہونے سے پہلے ایک ٹرانزیکشن ہک رجسٹر کیا جائے گا۔ یہ ہک واقعی تمام ڈیٹا بیس عملیات کے بعد چلایا جائے گا اور ٹرانزیکشن واقعی مکمل ہونے سے پہلے۔