Tags
Lanjutan dari tulisan sebelumnya Yii2:Lebih Jauh Tentang Validation. Kali ini kita akan membahas lebih detil mengenai mass assignment
. salah satu alasan diciptakannya framework
atau library-library yang lain adalah untuk memanjakan programer pemalas sehinggah lebih sedikit code yang dia tulis untuk mendapatkan hasil yang sama. Jika setelah memakai framework jumlah baris yang ditulis masih sama panjangnya dengan memakai native. Patut dipertanyakan kemalasannya.
Misal kita punya model User
dengan attributnya adalah id
, nama
, email
, alamat
dan no_telp
. Untuk menset nilainya kita lakukan
$model = new User(); $post = Yii::$app->request->post('User',[]); $model->nama = $post['nama']; $model->email = $post['email']; $model->alamat = $post['alamat']; $model->no_telp = $post['no_telp']; $model->save();
Bagi sebagian orang, kode di atas mungkin baik-baik saja. Tetapi sebagian yang lain akan bertanya, “Adakah cara lebih ringkas yang bisa kita lakukan?”. Kabar baiknya adalah, memang ada cara yang lebih ringkas.
$model = new User(); $model->load(Yii::$app->request->post()); $model->save();
Selain lebih ringkas, format di atas juga lebih independet terhadap model. Kita bisa copy paste
kode tersebut ke tempat lain dengan sedikit perubahan. Dengan mengganti bagian new User();
dengan model yang lain kita sudah bisa menyimpan tabel yang berbeda. Itulah yang dinamakan mass assigment
, mengisi nilai secara glondongan. Satu masalah selesai, programer bisa melanjutkan tidurnya sampai pagi.
Tunggu dulu. Masalah yang lain belum selesai. Mass assignment
ternyata memiliki masalah serius terkait dengan skuritas. Sangat tidak lucu kalau validator yang diharapkan mengamankan integritas data justru berpeluang mengundang bahaya.
Kita kembali ke model User
yang tadi. Di sana ada field id
. Idealnya, field id
adalah autoincrement dan nilainya tidak boleh dirubah. Jika ada user yang iseng memasukkan key id
di $_POST
tentu akan berakibat fatal. Karena itu model
harus cukup cerdas untuk membedakan mana field yang aman untuk diisi glondongan dan mana yang tidak aman.
Di framework sebelah (sebut saja namanya bunga) untuk membedakan field yang aman(safe)
atau tidak untuk diisi glondongan, kita harus mendefinisikannya di property $fillable
.
namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['nama', 'email', 'alamat', 'no_telp']; }
Karena field id
tidak ada di $fillable
, maka ketika di $_POST
ada value id
tetep tidak akan diassign ke model. Kita tidak perlu lagi kuatir ada yang iseng memasukkan id lewat $_POST
. Lalu bagaimana Yii melakukannya? Yii tidak melakukan seperti yang bunga lakukan. Karena di Yii, safe attribute
terintegrasi dengan validasi.
class User extends ActiveRecord { public function rules() { return [ [['name', 'email'], 'required'], [['alamat', 'no_telp'], 'safe'], ]; } }
Semua field yang yang terdaftar di rules()
otomatis akan menjadi safe attribute
. Karena field id
tidak ada di rules()
maka dia termasuk yang unsafe attribute
. Cukup cerdas bukan. Sekali lempar, dua mangga tetangga kita dapat.
“Apa artinya masalah kita sudah selesai?”
“Sayangnya belum.”
“Apa lagi yang kurang? Kita bisa menset banyak field sekaligus. Kita juga bisa memilah mana field yang boleh dan tidak boleh diset barengan”
safe
validator adalah validator yang tidak melakukan apapun tetapi hanya membuat attribute tersebut aman untuk diset glondongan. Dengan validator ini, maka attribute tersebut menjadi safe
walaupun tidak divalidasi. Kekurangan dari Yii2 adalah, tidak ada proses kebalikannya. Kita tidak bisa membuat suatu attribute tetap divalidasi dan pada saat yang sama tidak safe
untuk mass assignment
. Di Yii1 ada validator untuk ini yaitu unsafe
validator. Entah karena alasan apa tidak dipakai lagi di Yii2.
Misal, di model ‘Userkita tambahkan lagi field
status`. field ini tidak boleh null di database dengan nilai default adalah 0(inactive).
class User extends ActiveRecord { public function rules() { return [ [['status'], 'default', 'value' => 0], [['name', 'email'], 'required'], [['alamat', 'no_telp'], 'safe'], ]; } }
Pada saat yang sama kita tidak ingin field status
ini bisa diinput dari end user. Setidaknya ada dua solusi untuk masalah ini dan keduanya bukanlah solusi yang menarik.
1.Menghapus dari rules()
Dengan menghapus status
dari rules()
otomatis field tersebut menjadi unsafe
. Itu berarti kita juga kehilangan nilai default dari status tersebut. Kita harus menambahkan nilai default tersebut secara manual setiap kali create new user.
$model = new User(); $model->load(Yii::$app->request->post()); $model->status = 0; $model->save();
Lalu bagaimana jika nilai defaultnya dari perhitungan yang kompleks seperti dalam tulisan sebelumnya. Apalagi jika model yang sama dipakai dalam beberapa form
, tentu akan banyak redundansi.
2.Mengoverride method scenarios()
public function scenarios() { return [ 'default' => ['name', 'email', 'alamat', 'no_telp', '!status'], ]; }
Field status
diawali dengan karakter !
. Ini menujukkan bahwa field tersebut akan tetap divalidasi tetapi tidak masuk dalam safe attribute
. Cara ini juga tidak terlalu menarik. Jika lebih banyak scenario tentu akan lebih ribet lagi.
Masih berharap Yii2 akan memperkenalkan cara baru yang lebih efektif.
Disclaimer: Kadang ini bukan tentang berapa baris yang bisa kita hemat. Tetapi tentang berapa ego yang bisa kita tunjudkan 😀
Pingback: Yii2: Lebih Jauh Tentang Validation | D Blog
Topi Markotop said:
Master, Ketika model dibuat dengan new dan ID nya kita beri nilai (dengan tujuan mau update), tapi kok kenapa gak mau update tapi tetap insert? apakah setiap mau update harus menggunakan findOne()?
LikeLike
Misbahul D Munir said:
Ya, idealnya memang dari findOne().
Tetapi ada cara lainnya yaitu dengan menset
isNewRecord
jadifalse
.LikeLike
Rino said:
Master, bagaimana caranya validasi compare attribute dengan value dari model lain, code dibawah knpa ga berhasil yh?
public function rules()
{
return [
[[‘item_id’, ‘qty’, ‘uom_id’, ‘to_dept’, ‘user_site’], ‘required’],
[[‘material_req_id’, ‘item_id’, ‘uom_id’, ‘to_dept’, ‘user_site’,’loc_id’], ‘integer’],
[[‘qty’], ‘number’],
[
‘qty’,
‘compare’,
‘type’ => ‘number’,
‘compareValue’ => WitemsStock::find()->where([‘item_id’=>$this->item_id,’location_id’=>$this->loc_id])->select(‘qty’)->one(),
‘operator’ => ‘<=’
],
[[‘material_req_id’], ‘exist’, ‘skipOnError’ => true, ‘targetClass’ => WmaterialRequest::className(), ‘targetAttribute’ => [‘material_req_id’ => ‘id’]],
];
}
salam
Rino
LikeLike
Misbahul D Munir said:
coba ganti
one()
denganscalar()
.LikeLike
Rino said:
masih error mas:
WitemsStock::find()->where([‘item_id’=>$this->item_id,’location_id’=>$this->loc_id])->select(‘qty’)->scalar(),
hasilnya >> Qty must be less than or equal to “”.
misal isi manual
WitemsStock::find()->where([‘item_id’=>2,’location_id’=>2])->select(‘qty’)->scalar(),
hasilnya >> Qty must be less than or equal to “100”.
menggunakan tabular input,
controller:
public function actionCreate()
{
$model = new WmaterialRequest();
form:
Item List
$model->details,
‘model’ => WmaterialRequestDtl::className(),
‘form’ => $form,
‘columns’ => [
[‘class’ => ‘mdm\widgets\SerialColumn’],
[
‘attribute’ => ‘item_id’,
‘items’ => ArrayHelper::map(Witems::find()->all(),’id’,’item_name’),
],
[
‘attribute’ => ‘loc_id’,
‘items’ => ArrayHelper::map(WitemsLocation::find()->all(),’id’,’location_code’),
],
‘qty’,
[
‘attribute’ => ‘uom_id’,
‘items’ => ArrayHelper::map(Wuom::find()->all(),’id’,’uom_code’),
],
[
‘attribute’ => ‘to_dept’,
‘items’ => ArrayHelper::map(Hdepartment::find()->all(),’id’,’code’),
],
[
‘attribute’ => ‘user_site’,
‘items’ => ArrayHelper::map(AcompanySite::find()->all(),’company_site_id’,’initial_company_site’),
],
[‘class’ => ‘mdm\widgets\ButtonColumn’]
],
‘hiddens’=>[
‘id’
]
])
?>
LikeLike