Laravel کی $casts پراپرٹی ان خاموش سہولیات میں سے ایک ہے جو زیادہ توجہ حاصل کیے بغیر آپ کے ڈیٹا ہینڈل کرنے کے طریقے کو تشکیل دیتی ہے۔ اس ایرے (array) میں boolean یا datetime جیسی بلٹ ان ٹائپ شامل کریں، اور Eloquent خود بخود ڈیٹا بیس کی خام (raw) اسٹرنگز کو آپ کے کنٹرولرز یا ویوز تک پہنچنے سے پہلے کسی آسان فارمیٹ میں تبدیل کر دیتا ہے۔ یہ آپ کے کوڈ کو صاف ستھرا رکھتا ہے۔ لیکن جیسے ہی آپ اس ایرے کے اندر براہ راست اپنا ہیلپر (helper) کال کرنے کی کوشش کرتے ہیں، فریم ورک اس کی اجازت نہیں دیتا۔ 'username' => 'encrypt_data()' جیسا کچھ لکھنا کام نہیں کرے گا۔ Laravel یا تو کسی نیٹیو کاسٹ کی ورڈ (native cast keyword) کی توقع کرتا ہے یا پھر ایسی کلاس کی جو CastsAttributes انٹرفیس کو امپلیمنٹ (implement) کرتی ہو۔ یہ ضرورت اس لیے ہے کیونکہ کاسٹنگ لیئر Eloquent کے hydration اور serialization سائیکل کے اندر گہرائی میں کام کرتی ہے؛ اسے کسی بھی وقت موجود نہ ہونے والی فنکشن اسٹرنگ کے بجائے ایک واضح معاہدے (contract) کے ساتھ ایک قابلِ پیش گوئی آبجیکٹ کی ضرورت ہوتی ہے۔

$casts ایرے میں ہیلپر فنکشنز کیوں ناکام ہو جاتے ہیں

جب Eloquent ایک ماڈل لوڈ کرتا ہے، تو وہ ہر کالم کو تبدیل کرنے کا فیصلہ کرنے کے لیے $casts ایرے کا جائزہ لیتا ہے۔ فریم ورک مخصوص پرائمٹیوز (primitives)—جیسے string, integer, array, encrypted وغیرہ—اور ان مکمل طور پر کوالیفائیڈ کلاس ناموں کو پہچانتا ہے جو CastsAttributes کو امپلیمنٹ کرتے ہیں۔ یہ اسٹرنگ کو کوڈ کے طور پر ایویلیویٹ (evaluate) نہیں کرتا۔ اس لیے 'encrypt_data()' کو ایک نامعلوم کاسٹ ٹائپ سمجھا جاتا ہے، جس سے ایرر (error) پیدا ہوتا ہے۔ اگر Laravel اسے پارس (parse) کر بھی لیتا، تب بھی ماڈل انسٹنس، ایٹریبیوٹ کی (attribute key)، موجودہ ویلیو، اور ارد گرد کے ایٹریبیوٹس کو درست ترتیب سے آپ کے فنکشن میں پاس کرنے کا کوئی قابلِ اعتماد طریقہ نہ ہوتا۔ یہ انٹرفیس خاص طور پر آپ کے کسٹم لاجک اور Eloquent کے اندرونی نظام کے درمیان اس عمل کو معیاری بنانے کے لیے موجود ہے۔

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

طریقہ 1: ایک کسٹم کاسٹ کلاس لکھیں

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

app/Casts/CustomEncrypt.php بنا کر شروع کریں۔ نیم اسپیس (namespace) آپ کے آٹ لوڈنگ سیٹ اپ کے مطابق ہونا چاہیے، عام طور پر App\Casts۔ کلاس کا Illuminate\Contracts\Database\Eloquent\CastsAttributes کو امپلیمنٹ کرنا ضروری ہے، جو آپ کو دو میتھڈز (methods) کی تعریف کرنے پر مجبور کرتا ہے: get اور set۔

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class CustomEncrypt implements CastsAttributes
{
    public function get(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        return decrypt_data($value);
    }

    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        return encrypt_data($value);
    }
}

میتھڈ کے سگنیچر (signatures) اہم ہیں۔ Eloquent ہر میتھڈ میں چار آرگیومنٹس (arguments) پاس کرتا ہے۔ $model وہ انسٹنس ہے جسے پاپولیٹ (populate) یا سیو (save) کیا جا رہا ہے، جو آپ کو دوسرے فیلڈز کا معائنہ کرنے کی اجازت دیتا ہے اگر آپ کا لاجک ان پر منحصر ہو۔ $key وہ کالم کا نام ہے جس کی کاسٹنگ ہو رہی ہے۔ $value ڈیٹا بیس سے آنے والی خام اسٹرنگ یا null ہے (جب get استعمال ہو رہا ہو)، یا صارف کی طرف سے فراہم کردہ ویلیو ہے (جب set استعمال ہو رہا ہو)۔ $attributes اس رو (row) کے تمام کالمز کا خام ایرے ہے۔ آپ کو ہر بار چاروں استعمال کرنے کی ضرورت نہیں ہے، لیکن انٹرفیس کے لیے یہ ضروری ہیں۔

اوپر دیے گئے get میتھڈ میں، decrypt_data($value) اس وقت چلتا ہے جب Eloquent ڈیٹا بیس سے رو حاصل کر لیتا ہے اور ویلیو آپ کے ماڈل تک پہنچنے سے پہلے۔ set میتھڈ میں، encrypt_data($value) INSERT یا UPDATE سے پہلے چلتا ہے، جس سے یہ یقینی بنتا ہے کہ ڈیٹا بیس کبھی بھی پلین ٹیکسٹ (plaintext) نہ دیکھے۔

اسے جوڑنے کے لیے، اپنے ماڈل کے $casts ایرے کے اندر کلاس کا حوالہ دیں:

protected $casts = [
    'username' => CustomEncrypt::class,
    'password' => CustomEncrypt::class,
];

چونکہ آپ کلاس کانسٹنٹ (constant) استعمال کر رہے ہیں، اس لیے آٹ لوڈر باقی کام سنبھال لیتا ہے۔ اگر آپ کی ایپلی کیشن کو بعد میں انکرپشن اسکیم تبدیل کرنے کی ضرورت پڑتی ہے، تو آپ صرف ایک فائل ایڈٹ کرتے ہیں، اور ہر میپ شدہ فیلڈ کا طرزِ عمل فوری طور پر بدل جاتا ہے۔ جب آپ بہت سے ٹیبلز میں حساس ڈیٹا کا انتظام کر رہے ہوں، تو اس طرح کی سینٹرلائزیشن (centralization) کا مقابلہ کرنا مشکل ہے۔

طریقہ 2: ایکسیسر (Accessor) اور میوٹیشن (Mutator) کا استعمال کریں

بعض اوقات آپ ایسی تبدیلی کے لیے نئی فائل نہیں بنانا چاہتے جو صرف ایک جگہ اہم ہو۔ Laravel کی Attribute کلاس آپ کو PHP 8+ کلوزر (closure) سنٹیکس کا استعمال کرتے ہوئے براہ راست ماڈل پر get اور set لاجک کی تعریف کرنے کی اجازت دیتی ہے۔

use Illuminate\Database\Eloquent\Casts\Attribute;

protected function username(): Attribute
{
    return Attribute::make(
        get: fn ($value) => decrypt_data($value),
        set: fn ($value) => encrypt_data($value),
    );
}

یہاں میتھڈ کا نام اس کالم یا ایٹریبیوٹ سے ملنا چاہیے جسے آپ ٹارگٹ کر رہے ہیں۔ جب Eloquent ڈیٹا بیس سے username پڑھتا ہے، تو یہ خام ویلیو کو get کلوزر کے ذریعے پاس کرتا ہے۔ جب آپ ماڈل کی username پراپرٹی کو نئی ویلیو دیتے ہیں، تو set کلوزر اسے Eloquent کے ذریعے کوئری بنانے سے پہلے انکرپٹ کر دیتا ہے۔

یہ طریقہ پروٹو ٹائپس (prototypes) یا لیگیسی (legacy) ماڈلز میں بہترین ہے جہاں کاسٹ کلاسز کی ایک مکمل ڈائریکٹری بنانا ضرورت سے زیادہ محسوس ہوتا ہے۔ اس کا نقصان تکرار (repetition) ہے۔ اگر آپ بعد میں فیصلہ کرتے ہیں کہ email, phone, اور backup_code کو بھی اسی طرح کے علاج کی ضرورت ہے، تو آپ کو ان کلوزرز کو متعدد میتھڈز یا ماڈلز میں کاپی کرنا پڑے گا۔ یہ اضافی کوڈ (noise) بڑھتا چلا جاتا ہے۔ یہ آپ کو پورے ماڈل کو بوٹ (boot) کیے بغیر یونٹ ٹیسٹ میں لاجک کو دوبارہ استعمال کرنے سے بھی روکتا ہے۔

ان دونوں میں سے انتخاب کرنا

ایک کسٹم کاسٹ کلاس (custom cast class) کا استعمال تب کریں جب آپ ری یوز (reuse)، ٹیسٹنگ، اور ماڈلز کو ہلکا پھلکا رکھنے کو اہمیت دیتے ہوں۔ یہ دوسرے ڈویلپرز کو یہ اشارہ دیتا ہے کہ یہ تبدیلی آپ کی ایپلی کیشن میں ایک بنیادی تصور (first-class concept) ہے، نہ کہ کوئی عارضی حل (one-off hack)۔

ایکسیسر (accessor) کا استعمال تب کریں جب لاجک واقعی مقامی (localized) ہو، تجرباتی ہو، یا اس بات کا امکان نہ ہو کہ وہ موجودہ اسپرنٹ (sprint) سے زیادہ چلے گی۔ یہ آپ کو کوڈ بیس میں چھوٹی چھوٹی فائلیں بکھیرے بغیر تیزی سے کام کرنے کی اجازت دیتا ہے۔ بس اس بات کے لیے تیار رہیں کہ جب یہی پیٹرن دوسری یا تیسری بار سامنے آئے تو اسے کاسٹ کلاس میں ری فیکٹر (refactor) کر لیں۔

عملی تفصیلات جنہیں ذہن میں رکھنا ضروری ہے

کسٹم کاسٹس طاقتور ہوتے ہیں، لیکن وہ ایسا رویہ متعارف کرواتے ہیں جو اگر آپ محتاط نہ ہوں تو آپ کو حیران کر سکتا ہے۔ سب سے پہلے، یاد رکھیں کہ get وہ سب کچھ وصول کرتا ہے جو ڈیٹا بیس نے واپس کیا ہو، بشمول null۔ اگر decrypt_data نل (null) ان پٹ کو قبول نہیں کرتا، تو اس کے خلاف حفاظتی تدبیر (guard) اختیار کریں:

public function get(Model $model, string $key, mixed $value, array $attributes): mixed
{
    return is_null($value) ? null : decrypt_data($value);
}

دوسرا، کاسٹس ایرے (array) اور JSON سیریلائزیشن (serialization) کے دوران چلتے ہیں۔ جب آپ کسی ماڈل کو API ریسورس کے طور پر واپس کرتے ہیں یا toArray() کال کرتے ہیں، تو کاسٹ get لاجک اب بھی لاگو ہوتی ہے۔ عام طور پر آپ یہی چاہتے ہیں، لیکن اگر آپ ڈیکریپٹ شدہ (decrypted) ویلیوز ظاہر کر رہے ہیں اور ان پر اضافی ویزیبیلیٹی کنٹرولز (visibility controls) لگانا چاہتے ہیں، تو یہ بات یاد رکھنا ضروری ہے۔

تیسرا، اور سب سے اہم بات یہ کہ، کاسٹنگ ڈیٹا حاصل کرنے کے بعد ہوتی ہے۔ آپ تبدیل شدہ ویلیو کے خلاف کوئری (query) نہیں کر سکتے۔ User::where('username', 'john_doe')->first() جیسی کوئری اسٹرنگ john_doe کو براہ راست ڈیٹا بیس کو بھیجتی ہے۔ یہ کبھی بھی آپ کی ڈیکریپٹ لاجک سے نہیں گزرتی۔ اگر کالم ڈیٹا میں محفوظ (at rest) ہونے کے دوران انکرپٹڈ ہے، تو وہ کوئری کچھ بھی نہیں پائے گی جب تک کہ آپ خود انکرپٹڈ سائفر ٹیکسٹ (ciphertext) کے خلاف تلاش نہ کریں۔ اپنے ڈیٹا بیس ایکسیس پیٹرنز کی منصوبہ بندی اسی کے مطابق کریں، کیونکہ کاسٹس نیٹیو ڈیٹا بیس فنکشنز یا انڈیکسڈ پلین ٹیکسٹ کالمز کا متبادل نہیں ہیں۔

کسٹم کاسٹ کلاسز کنفیگریشن کے چھوٹے حصوں کے لیے بھی بہترین جگہ فراہم کرتی ہیں۔ اگر آپ کو کنسٹرکٹر آرگومنٹ (constructor arguments) کی ضرورت ہے—مثلاً سائفر موڈ (cipher mode) یا فارمیٹ اسٹرنگ پاس کرنا—تو Laravel انہیں $casts ایرے کے ذریعے ایک ایکسپریشن جیسے 'field' => CustomEncrypt::class . ':arg' کے ساتھ سپورٹ کرتا ہے، اگرچہ یہ یہاں بیان کردہ بنیادی سیٹ اپ سے ایک قدم آگے کی بات ہے۔

اصل حاصلِ کلام

$casts میں براہ راست ہیلپر فنکشنز ڈالنے کی پابندی کوئی بے مقصد بیوروکریسی نہیں ہے۔ یہ آپ کو ایسے کوڈ کی طرف مائل کرتی ہے جو واضح (explicit)، ٹیسٹ ایبل (testable) اور ری یوز ایبل (reusable) ہو۔ کسٹم کاسٹ کلاسز بکھری ہوئی ان لائن لاجک کو قابل اعتماد اجزاء (components) میں بدل دیتی ہیں جنہیں آپ بغیر کسی تکرار کے مختلف ماڈلز میں شیئر کر سکتے ہیں۔ ایکسیسرز اس وقت فوری اور مقامی حل کے لیے راستہ کھلا رکھتے ہیں جب ایک الگ فائل بنانا ضرورت سے زیادہ رسمی (ceremony) محسوس ہو۔ دونوں میں مہارت حاصل کریں، مسئلے کی نوعیت کے مطابق انتخاب کریں، اور آپ کا Eloquent لیئر ایپلی کیشن کے بڑھنے کے بعد بھی پڑھنے میں آسان رہے گا۔