Sifat $casts Laravel adalah salah satu kemudahan senyap yang membentuk cara anda mengendalikan data tanpa menarik banyak perhatian. Masukkan jenis terbina dalam seperti boolean atau datetime ke dalam tatasusunan tersebut, dan Eloquent secara automatik menukar rentetan pangkalan data mentah kepada sesuatu yang lebih mesra sebelum nilainya sampai ke pengawal (controller) atau paparan (view) anda. Ia memastikan kod anda kemas. Namun, sebaik sahaja anda cuba memanggil fungsi pembantu (helper) anda sendiri secara terus di dalam tatasusunan tersebut, rangka kerja (framework) akan menolaknya. Menulis sesuatu seperti 'username' => 'encrypt_data()' tidak akan berjaya. Laravel menjangkakan sama ada kata kunci cast asli atau kelas yang melaksanakan antara muka (interface) CastsAttributes. Keperluan itu wujud kerana lapisan penukaran (casting layer) berjalan jauh di dalam kitaran penghidratan (hydration) dan penserialan (serialization) Eloquent; ia memerlukan objek yang boleh diramal dengan kontrak yang jelas berbanding rentetan fungsi rawak yang mungkin tidak wujud semasa masa larian (runtime).
Mengapa Fungsi Pembantu Gagal dalam Tatasusunan $casts
Apabila Eloquent memuatkan model, ia mengimbas tatasusunan $casts untuk memutuskan cara menukar setiap lajur. Rangka kerja tersebut mengenali primitif tertentu—string, integer, array, encrypted, dan sebagainya—serta mengenali nama kelas berkualifikasi penuh yang melaksanakan CastsAttributes. Ia tidak menilai rentetan tersebut sebagai kod. Jadi 'encrypt_data()' dianggap sebagai jenis cast yang tidak dikenali, yang mencetuskan ralat. Walaupun Laravel berjaya mencerakinnya (parse), tidak akan ada cara yang boleh dipercayai untuk menghantar instans model, kunci atribut, nilai semasa, dan atribut sekeliling ke dalam fungsi anda mengikut urutan yang betul. Antara muka tersebut wujud khusus untuk menyeragamkan "jabat tangan" (handshake) antara logik tersuai anda dengan bahagian dalaman Eloquent.
Anda mempunyai dua cara mantap untuk merapatkan jurang ini. Satu mengutamakan penggunaan semula jangka panjang. Satu lagi mengutamakan kepantasan apabila anda hanya memerlukan tampalan pantas di dalam satu model sahaja.
Kaedah 1: Tulis Kelas Cast Tersuai
Jika transformasi yang sama digunakan pada pelbagai medan atau tersebar merentasi beberapa model, kelas cast khusus adalah pilihan yang lebih bersih. Ia berada dalam failnya sendiri, boleh diuji unit secara berasingan, dan memastikan model anda bebas daripada kod boilerplate yang berulang.
Mulakan dengan mencipta app/Casts/CustomEncrypt.php. Ruang nama (namespace) harus sepadan dengan tetapan pemuatan automatik (autoloading) anda, biasanya App\Casts. Kelas tersebut mesti melaksanakan Illuminate\Contracts\Database\Eloquent\CastsAttributes, yang mewajibkan anda mentakrifkan dua kaedah: get dan 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);
}
}
Tandatangan (signature) kaedah adalah penting. Eloquent menghantar empat argumen ke dalam setiap kaedah. $model ialah instans yang sedang diisi atau disimpan, yang membolehkan anda memeriksa medan lain jika logik anda bergantung padanya. $key ialah nama lajur yang sedang ditukar. $value ialah rentetan mentah atau null yang datang daripada pangkalan data semasa get, atau nilai yang dibekalkan pengguna semasa set. $attributes ialah keseluruhan tatasusunan mentah lajur untuk baris tersebut. Anda tidak perlu menggunakan keempat-empatnya setiap masa, tetapi antara muka tersebut mewajibkannya.
Dalam kaedah get di atas, decrypt_data($value) berjalan selepas Eloquent mengambil baris daripada pangkalan data dan sebelum nilai tersebut sampai ke model anda. Dalam kaedah set, encrypt_data($value) berjalan sebelum INSERT atau UPDATE, memastikan pangkalan data tidak pernah melihat teks biasa (plaintext).
Untuk menyambungkannya, rujuk kelas tersebut di dalam tatasusunan $casts model anda:
protected $casts = [
'username' => CustomEncrypt::class,
'password' => CustomEncrypt::class,
];
Oleh kerana anda menggunakan pemalar kelas (class constant), pemuat automatik (autoloader) akan mengendalikan selebihnya. Jika aplikasi anda kemudiannya perlu menukar skema penyulitan, anda hanya perlu mengedit satu fail, dan setiap medan yang dipetakan akan berubah tingkah laku secara serta-merta. Pemusatan (centralization) tersebut sukar ditandingi apabila anda menguruskan data sensitif merentasi banyak jadual.
Kaedah 2: Gunakan Accessor dan Mutator
Kadangkala anda tidak mahukan fail baharu untuk transformasi yang hanya penting di satu tempat. Kelas Attribute Laravel membolehkan anda mentakrifkan logik get dan set secara terus pada model menggunakan sintaks closure PHP 8+.
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),
);
}
Di sini, nama kaedah mesti sepadan dengan lajur atau atribut yang anda sasarkan. Apabila Eloquent membaca username daripada pangkalan data, ia menghantar nilai mentah melalui closure get. Apabila anda menetapkan nilai baharu kepada sifat username model, closure set akan menyulitkannya sebelum Eloquent membina query.
Pendekatan ini sangat berguna dalam prototaip atau model lama di mana satu direktori penuh kelas cast terasa berlebihan. Kelemahannya ialah pengulangan. Jika anda kemudian memutuskan bahawa email, phone, dan backup_code memerlukan rawatan yang sama, anda akhirnya akan menyalin closure tersebut merentasi pelbagai kaedah atau model. Gangguan (noise) tersebut akan bertambah. Ia juga menghalang anda daripada menggunakan semula logik tersebut dalam ujian unit tanpa memulakan (booting) keseluruhan model.
Memilih Antara Kedua-duanya
Gunakan kelas cast tersuai apabila anda mementingkan penggunaan semula, pengujian, dan mengekalkan model yang ringkas. Ia memberi isyarat kepada pembangun lain bahawa transformasi ini adalah konsep kelas pertama dalam aplikasi anda, bukannya sekadar penyelesaian sementara.
Gunakan accessor apabila logik tersebut benar-benar setempat, bersifat eksperimental, atau tidak mungkin bertahan lebih lama daripada sprint semasa. Ia membolehkan anda bergerak pantas tanpa menyebarkan fail-fail kecil ke seluruh codebase. Cuma bersedialah untuk melakukan refactor kepada kelas cast sebaik sahaja corak yang sama muncul buat kali kedua atau ketiga.
Perincian Praktikal untuk Diingati
Cast tersuai adalah berkuasa, tetapi ia memperkenalkan tingkah laku yang boleh mengejutkan anda jika anda tidak berwaspada. Pertama, ingat bahawa get menerima apa sahaja yang dikembalikan oleh pangkalan data, termasuk null. Jika decrypt_data tidak menyokong input null, lindungi daripadanya:
public function get(Model $model, string $key, mixed $value, array $attributes): mixed
{
return is_null($value) ? null : decrypt_data($value);
}
Kedua, cast dijalankan semasa penyerialan array dan JSON. Apabila anda mengembalikan model sebagai sumber API atau memanggil toArray(), logik get cast tersebut masih terpakai. Itu biasanya apa yang anda mahukan, tetapi ia perlu diingat jika anda mendedahkan nilai yang telah dinyahsulit dan perlu menambah lapisan kawalan keterlihatan di atasnya.
Ketiga, dan yang paling penting, casting berlaku selepas pengambilan data. Anda tidak boleh membuat pertanyaan terhadap nilai yang telah diubah suai tersebut. Pertanyaan seperti User::where('username', 'john_doe')->first() menghantar string john_doe terus ke pangkalan data. Ia tidak pernah melalui logik nyahsulit anda. Jika kolum tersebut disulitkan semasa dalam simpanan, pertanyaan itu tidak akan menemui apa-apa melainkan anda mencari pada ciphertext yang disulitkan itu sendiri. Rancang corak capaian pangkalan data anda sewajarnya, kerana cast bukanlah pengganti kepada fungsi pangkalan data asli atau kolum teks biasa berindeks.
Kelas cast tersuai juga merupakan tempat yang sesuai untuk bahagian konfigurasi yang kecil. Jika anda memerlukan argumen constructor—mungkin menghantar mod penyulitan atau string format—Laravel menyokongnya melalui array $casts menggunakan ekspresi seperti 'field' => CustomEncrypt::class . ':arg', walaupun itu adalah langkah yang lebih maju daripada tetapan asas yang diterangkan di sini.
Kesimpulan Sebenar
Sekatan terhadap penggunaan fungsi pembantu secara terus ke dalam $casts bukanlah birokrasi sewenang-wenangnya. Ia mendorong anda ke arah kod yang eksplisit, boleh diuji, dan boleh digunakan semula. Kelas cast tersuai menukarkan logik dalam baris yang berselerak kepada komponen boleh dipercayai yang boleh anda kongsi merentasi model tanpa duplikasi. Accessor mengekalkan ruang untuk pembetulan setempat yang pantas apabila fail berasingan terasa seperti bebanan formaliti yang tidak perlu. Kuasai kedua-duanya, pilih berdasarkan skop masalah, dan lapisan Eloquent anda akan kekal mudah dibaca lama selepas aplikasi berkembang.
