Laravel-ன் $casts property என்பது நீங்கள் தரவைக் கையாளும் முறையைத் தீர்மானிக்கும் ஒரு அமைதியான வசதியாகும். boolean அல்லது datetime போன்ற ஒரு built-in வகையை அந்த array-க்குள் சேர்த்தால், Eloquent தானாகவே தரவுத்தளத்திலிருந்து வரும் raw strings-களை உங்கள் controllers அல்லது views-க்குச் செல்லும் முன்பே எளிமையான வடிவத்திற்கு மாற்றுகிறது. இது உங்கள் குறியீட்டை (code) நேர்த்தியாக வைத்திருக்கும். ஆனால், அந்த array-க்குள் உங்கள் சொந்த helper function-ஐ நேரடியாக அழைக்க முயலும்போது, framework அதை அனுமதிக்காது. 'username' => 'encrypt_data()' என்பது போன்ற ஒன்றை எழுதினால் அது வேலை செய்யாது. Laravel ஒரு native cast keyword அல்லது CastsAttributes interface-ஐச் செயல்படுத்தும் (implement) ஒரு class-ஐ எதிர்பார்க்கிறது. இந்தத் தேவை ஏன் இருக்கிறது என்றால், casting layer என்பது Eloquent-ன் hydration மற்றும் serialization சுழற்சியின் ஆழத்தில் இயங்குகிறது; அதற்கு runtime-ல் இருக்காது என்று தெரியாத ஒரு தன்னிச்சையான function string-ஐ விட, தெளிவான ஒப்பந்தத்தைக் (contract) கொண்ட ஒரு கணிக்கக்கூடிய object தேவைப்படுகிறது.

ஏன் $casts Array-ல் Helper Functions தோல்வியடைகின்றன

Eloquent ஒரு model-ஐ ஏற்றும்போது, ஒவ்வொரு column-ஐயும் எவ்வாறு மாற்றுவது என்பதைத் தீர்மானிக்க $casts array-ஐ ஆய்வு செய்கிறது. framework குறிப்பிட்ட primitives-களை—string, integer, array, encrypted போன்றவற்றை—மற்றும் CastsAttributes-ஐச் செயல்படுத்தும் fully-qualified class names-களை அடையாளம் காணும். இது அந்த string-ஐ ஒரு code-ஆகக் கருதாது (evaluate செய்யாது). எனவே 'encrypt_data()' என்பது ஒரு தெரியாத cast type ஆகக் கருதப்பட்டு, பிழையை (error) ஏற்படுத்தும். ஒருவேளை Laravel அதை parse செய்தாலும், model instance, attribute key, தற்போதைய மதிப்பு (current value) மற்றும் சுற்றியுள்ள attributes ஆகியவற்றைச் சரியான வரிசையில் உங்கள் function-க்குள் அனுப்புவதற்கு நம்பகமான வழி இருக்காது. உங்கள் custom logic மற்றும் Eloquent-ன் internals ஆகியவற்றுக்கு இடையேயான அந்தத் தொடர்பை (handshake) தரப்படுத்துவதற்காகவே (standardize) இந்த interface உருவாக்கப்பட்டுள்ளது.

இந்த இடைவெளியைக் குறைக்க உங்களிடம் இரண்டு சிறந்த வழிகள் உள்ளன. ஒன்று நீண்ட கால மறுபயன்பாட்டிற்கு (reuse) ஏற்றது. மற்றொன்று ஒரு குறிப்பிட்ட model-க்குள் மட்டும் விரைவான மாற்றத்தை (quick patch) செய்ய விரும்பும்போது வேகத்திற்கு ஏற்றது.

முறை 1: ஒரு Custom Cast Class-ஐ எழுதுதல்

ஒரே மாதிரியான மாற்றம் பல fields-களுக்குப் பொருந்தினால் அல்லது பல models-களில் பரவியிருந்தால், ஒரு பிரத்யேக cast class-ஐப் பயன்படுத்துவதே சிறந்த முறையாகும். இது தனியான ஒரு file-ல் இருக்கும், தனித்து unit-test செய்ய முடியும், மேலும் உங்கள் models-களைத் திரும்பத் திரும்பத் தேவைப்படும் boilerplate குறியீடுகளிலிருந்து விடுவிக்கிறது.

முதலில் app/Casts/CustomEncrypt.php-ஐ உருவாக்கத் தொடங்குங்கள். namespace என்பது உங்கள் autoloading அமைப்பிற்கு ஏற்ப இருக்க வேண்டும், பொதுவாக இது App\Casts என்று இருக்கும். அந்த class கண்டிப்பாக Illuminate\Contracts\Database\Eloquent\CastsAttributes-ஐ implement செய்ய வேண்டும், இது நீங்கள் get மற்றும் set ஆகிய இரண்டு methods-களை வரையறுக்கக் கட்டாயப்படுத்துகிறது.

<?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);
    }
}

Method signatures மிக முக்கியமானவை. Eloquent ஒவ்வொரு method-க்கும் நான்கு arguments-களை அனுப்புகிறது. $model என்பது நிரப்பப்படும் அல்லது சேமிக்கப்படும் instance ஆகும், இது உங்கள் logic மற்ற fields-களைச் சார்ந்து இருந்தால் அவற்றை ஆய்வு செய்ய அனுமதிக்கிறது. $key என்பது தற்போது cast செய்யப்படவுள்ள column பெயர் ஆகும். $value என்பது get-ன் போது தரவுத்தளத்திலிருந்து வரும் raw string அல்லது null, அல்லது set-ன் போது பயனர் வழங்கும் மதிப்பு ஆகும். $attributes என்பது அந்த row-க்கான அனைத்து column-களின் raw array ஆகும். நீங்கள் எப்போதும் நான்கு argument-களையும் பயன்படுத்த வேண்டிய அவசியமில்லை, ஆனால் interface-ன் படி அவை தேவைப்படுகின்றன.

மேலே உள்ள get method-ல், Eloquent தரவுத்தளத்திலிருந்து row-வை எடுத்த பிறகு மற்றும் அந்த மதிப்பு உங்கள் model-க்குச் செல்லும் முன்பே decrypt_data($value) இயங்குகிறது. set method-ல், INSERT அல்லது UPDATE செய்வதற்கு முன்பே encrypt_data($value) இயங்குகிறது, இதனால் தரவுத்தளம் ஒருபோதும் plaintext-ஐப் பார்ப்பதில்லை.

இதை இணைக்க, உங்கள் model-ன் $casts array-க்குள் அந்த class-ஐக் குறிப்பிடவும்:

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

நீங்கள் class constant-ஐப் பயன்படுத்துவதால், மீதமுள்ளவற்றை autoloader கவனித்துக் கொள்ளும். உங்கள் application-ல் பின்னர் encryption முறையை மாற்ற வேண்டியிருந்தால், நீங்கள் ஒரு file-ஐ மட்டும் மாற்றினால் போதும், அதனுடன் இணைக்கப்பட்ட அனைத்து field-களின் செயல்பாடும் உடனடியாக மாறிவிடும். பல tables-களில் முக்கியமான தரவுகளை (sensitive data) நீங்கள் கையாளும்போது, இந்த மையப்படுத்தப்பட்ட (centralization) முறை மிகவும் சிறந்தது.

முறை 2: Accessor மற்றும் Mutator-ஐப் பயன்படுத்துதல்

சில நேரங்களில், ஒரே ஒரு இடத்தில் மட்டும் தேவைப்படும் ஒரு மாற்றத்திற்காகப் புதிய file-ஐ உருவாக்க நீங்கள் விரும்பமாட்டீர்கள். Laravel-ன் Attribute class மூலம் PHP 8+ closure syntax-ஐப் பயன்படுத்தி, model-லேயே நேரடியாக get மற்றும் set logic-களை வரையறுக்க முடியும்.

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),
    );
}

இங்கே method பெயர் நீங்கள் இலக்கு வைத்துள்ள column அல்லது attribute-க்கு இணையாக இருக்க வேண்டும். Eloquent தரவுத்தளத்திலிருந்து username-ஐப் படிக்கும்போது, அது raw value-வை get closure வழியாக அனுப்புகிறது. நீங்கள் model-ன் username property-க்கு ஒரு புதிய மதிப்பை வழங்கும் போது, Eloquent query-யை உருவாக்குவதற்கு முன்பே set closure அதை encrypt செய்கிறது.

cast classes-களுக்கான முழுமையான directory தேவையில்லை என்று தோன்றும் prototypes அல்லது legacy models-களுக்கு இந்த அணுகுமுறை மிகவும் பயனுள்ளதாக இருக்கும். இதன் குறைபாடு என்னவென்றால், மீண்டும் மீண்டும் எழுத வேண்டியிருக்கும் (repetition). ஒருவேளை email, phone, மற்றும் backup_code ஆகியவற்றுக்கும் இதே போன்ற மாற்றம் தேவை என்று நீங்கள் முடிவு செய்தால், அந்த closures-களைப் பல methods அல்லது models-களில் நகலெடுக்க வேண்டியிருக்கும். இது குறியீட்டின் அளவை (noise) அதிகரிக்கும். மேலும், முழு model-ஐயும் boot செய்யாமல், ஒரு unit test-ல் அந்த logic-ஐ மறுபயன்பாடு செய்வதையும் இது தடுக்கும்.

இரண்டிற்கும் இடையிலானத் தேர்வு

மறுபயன்பாடு (reuse), சோதனை (testing) மற்றும் மாடல்களை (models) எளிமையாக (slim) வைத்திருப்பது ஆகியவற்றில் நீங்கள் கவனம் செலுத்தும்போது, ஒரு custom cast class-ஐப் பயன்படுத்தவும். இது இந்த மாற்றம் உங்கள் பயன்பாட்டில் ஒரு முதன்மையான கருத்தாக்கம் (first-class concept) என்பதையும், இது ஒரு தற்காலிகத் தீர்வு (one-off hack) அல்ல என்பதையும் மற்ற டெவலப்பர்களுக்கு உணர்த்துகிறது.

தர்க்கம் (logic) முற்றிலும் ஒரு குறிப்பிட்ட இடத்திற்கு மட்டுமே உரியதாகவோ, சோதனை முயற்சியாகவோ அல்லது தற்போதைய sprint காலத்திற்குப் பிறகு தேவைப்படாது என்றோ தோன்றினால், ஒரு accessor-ஐப் பயன்படுத்தவும். இது codebase முழுவதும் சிறிய கோப்புகளைத் தூவிக்கொண்டிருக்காமல், விரைவாகச் செயல்பட உதவும். அதே முறை இரண்டாவது அல்லது மூன்றாவது முறை வரும்போது, அதை ஒரு cast class-ஆக மாற்றத் தயாராக இருங்கள்.

நினைவில் கொள்ள வேண்டிய நடைமுறை விவரங்கள்

Custom casts மிகவும் சக்திவாய்ந்தவை, ஆனால் நீங்கள் கவனிக்கவில்லை என்றால் அவை எதிர்பாராத மாற்றங்களைச் செய்யலாம். முதலாவதாக, get என்பது தரவுத்தளம் (database) எதைக் கொடுத்தாலும் (null உட்பட) அதைப் பெற்றுக்கொள்ளும் என்பதை நினைவில் கொள்ளுங்கள். decrypt_data ஆனது null உள்ளீட்டை ஏற்கவில்லை என்றால், அதைப் பாதுகாக்கவும்:

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

இரண்டாவதாக, array மற்றும் JSON serialization நடைபெறும் போது casts இயங்கும். நீங்கள் ஒரு model-ஐ API resource-ஆகத் திருப்பிக் கொடுக்கும்போதோ அல்லது toArray() என்று அழைக்கும்போதோ, cast get தர்க்கம் தொடர்ந்து செயல்படும். இது பொதுவாக நீங்கள் விரும்புவதே, ஆனால் நீங்கள் டிகிரிப்ட் செய்யப்பட்ட (decrypted) மதிப்புகளை வெளிப்படுத்தும்போது, கூடுதல் பாதுகாப்பு கட்டுப்பாடுகளை (visibility controls) சேர்க்க வேண்டியிருந்தால் இதை நினைவில் கொள்வது அவசியம்.

மூன்றாவதாக, மற்றும் மிக முக்கியமாக, casting என்பது தரவைப் பெற்றெடுத்த பிறகு நடக்கிறது. மாற்றப்பட்ட மதிப்பைக் கொண்டு நீங்கள் query செய்ய முடியாது. User::where('username', 'john_doe')->first() போன்ற ஒரு query, john_doe என்ற string-ஐ நேரடியாகத் தரவுத்தளத்திற்கு அனுப்பும். இது உங்கள் decrypt தர்க்கத்தின் வழியாக ஒருபோதும் செல்லாது. அந்தத் தரவுத்தளத் தூண் (column) சேமிக்கப்படும் போதே என்கிரிப்ட் செய்யப்பட்டிருந்தால் (encrypted at rest), நீங்கள் என்கிரிப்ட் செய்யப்பட்ட ciphertext-ஐயே தேடாதவரை அந்த query எதையும் கண்டறியாது. எனவே, உங்கள் database access patterns-ஐ அதற்கேற்பத் திட்டமிடுங்கள், ஏனெனில் casts என்பது native database functions அல்லது indexed plaintext columns-களுக்கு மாற்றாகாது.

Custom cast classes சிறிய அளவிலான கட்டமைப்புத் தேவைகளுக்கும் (configuration) சிறந்த இடமாகும். உங்களுக்கு constructor arguments தேவைப்பட்டால்—உதாரணமாக ஒரு cipher mode அல்லது format string-ஐ அனுப்ப வேண்டுமானால்—Laravel அதை $casts array மூலம் 'field' => CustomEncrypt::class . ':arg' போன்ற ஒரு expression பயன்படுத்தி ஆதரிக்கிறது, இருப்பினும் இது இங்கே விவரிக்கப்பட்டுள்ள அடிப்படை அமைப்பிற்கு அப்பாற்பட்டது.

உண்மையான முடிவு

$casts-க்குள் நேரடியாக helper functions-களைப் பயன்படுத்துவதைத் தடுக்கும் கட்டுப்பாடு என்பது ஒரு தேவையற்ற நடைமுறை அல்ல. இது உங்களை வெளிப்படையான (explicit), சோதனை செய்யக்கூடிய (testable) மற்றும் மறுபயன்பாடு செய்யக்கூடிய (reusable) குறியீட்டை நோக்கித் தூண்டுகிறது. Custom cast classes என்பது சிதறிக்கிடக்கும் inline logic-களை, நகல் எடுக்கத் தேவையில்லாத (without duplication), மாடல்களுக்கு இடையே பகிரக்கூடிய நம்பகமான கூறுகளாக (components) மாற்றுகிறது. ஒரு தனி கோப்பை உருவாக்குவது தேவையற்ற வேலையாகத் தோன்றும் போது, விரைவான மற்றும் குறிப்பிட்ட இடத்திற்கான தீர்வுகளுக்கு Accessors கதவைத் திறந்து வைக்கின்றன. இரண்டையும் கற்றுக்கொள்ளுங்கள், சிக்கலின் அளவைப் பொறுத்துத் தேர்ந்தெடுங்கள், உங்கள் பயன்பாடு வளர்ந்த பிறகும் உங்கள் Eloquent layer எளிதாகப் புரியும் வகையில் இருக்கும்.