Tags

, , , ,

Salah satu kasus yang sering muncul dalam pemrograman adalah bagaimana cara menyimpan header dan detail sekaligus dari satu halaman web. Contoh mudahnya data penjualan dengan beberapa item produk. Yii sebenarnya telah memperkenalkan konsep tabular input. Sayangnya konsep ini masih agak kurang fleksibel dimana kita tidak bisa create, update dan delete detail sekaligus dalam satu halaman.
Screenshot from 2016-02-06 14:09:04

Sebagai solusi untuk kasus ini. Saya memperkenalkan ekstensi yang akan mempermudah menyimpan data secara massal. Ekstensi tersebut adalah mdmsoft/yii2-ar-behaviors. Untuk cara instalnya silakan baca di READMEnya.

Saya hanya akan membahas trait mdm\behavior\ar\RelationTrait saja. Cara menggunakannya dalam koding.
Misal kita memiliki 2 table yaitu order dan item dimana tabel order dihubukan ke item dengan relasi one to many. Setelah digenerate akan tercreate 2 model yaitu Order dan Item.

Penggunaan di model

class Order extends ActiveRecord
{
    use \mdm\behavior\ar\RelationTrait;

    ....
    public function getItems()
    {
        return $this->hasMany(Item::className(),['order_id'=>'id']);
    }

    public function setItems($value)
    {
        $this->loadRelated('items', $value);
    }

}


class Item extends ActiveRecord
{
    ...
    public function getOrder()
    {
        return $this->hasOne(Order::className(),['id'=>'order_id']);
    }
}

Perhatikan bagian kode

    public function setItems($value)
    {
        $this->loadRelated('items', $value);
    }

Ini akan membuat property items menjadi read and write. Ketika menulis ke properti items, trait akan membaca detail yg sudah ada, membandingkannya dengan data yang baru. Jika ada item baru, maka akan dicreate, jika itemnya sama maka diupdate jika itemnya tidak ada maka akan didelete.

Cara memakainya di controller

public function actionCreate()
{
    $model = new Order();
    if($model->load(Yii::$app->request->post()){
        $transaction = Yii::$app->db->beginTransaction();
        try{
            $model->items = Yii::$app->request->post('Item',[]);
            if($model->save()){
                $transaction->commit();
                return $this->redirect(['view','id'=>$model->id]);
            }else{
                $transaction->rollback();
            }
        }catch(Exception $e){
            $transaction->rollback();
            throw $e;
        }
    }
    return $this->render('create',['model'=>$model]);
}

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    if($model->load(Yii::$app->request->post()){
        $transaction = Yii::$app->db->beginTransaction();
        try{
            $model->items = Yii::$app->request->post('Item',[]);
            if($model->save()){
                $transaction->commit();
                return $this->redirect(['view','id'=>$model->id]);
            }else{
                $transaction->rollback();
            }
        }catch(Exception $e){
            $transaction->rollback();
            throw $e;
        }
    }
    return $this->render('update',['model'=>$model]);
}

Baris

$model->items = Yii::$app->request->post('Item',[]);

yang ditangani oleh trait dengan logic yang panjang.

Pengaturan view

Berikutnya adalah penanganan viewnya. Kita gunakan ekstensi lain yaitu mdmsoft/yii2-widgets. Silakan baca cara instalasinya di sana.
Kita gunakan widget mdm\widgets\TabularInput untuk menangani inputan detail karena widget bisa punya fitur tambah dan hapus baris.

// file _form.php
    use mdm\widgets\TabularInput;
    use app\models\Order;
    use app\models\Item;

    <?php $form = ActiveForm::begin(); ?>
    <div class="box-body">
        field($model, 'vendor_id')->dropDownList(Vendor::selectOptions()); ?>

        field($model, 'date')->widget('yii\jui\DatePicker', [
                            'options' => ['class' => 'form-control', 'style' => 'width:50%'],
                                //'dateFormat' => 'php:d-m-Y',
                ]); ?>

        field($model, 'description')->textInput(['maxlength' => 64]) ?>

        <!-- Tabular Input -->
        <table class="tabular table-striped">
            <thead>
            <th class="col-lg-2">Product</th>
            <th class="col-lg-2">Qty</th>
            <th class="col-lg-2">@Price</th>
            <th class="col-lg-1"><a id="add-row" title="Add" href="#"><span class="glyphicon glyphicon-add"></span></a></th>
            </thead>
              'detail-grid',
                'allModels' => $model->items,
                'modelClass' => Item::className(),
                'options' => ['tag' => 'tbody'],
                'itemOptions' => ['tag' => 'tr'],
                'itemView' => '_item_detail',
                'clientOptions'=>[
                     'btnAddSelector'=>'#add-row',
                ]
            ]);
            ?>
        </table>
    </div>
    <div class="box-footer">
        isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>

    </div>
    <?php ActiveForm::end(); ?>

Untuk detail row-nya ditulis dalam file lain

// file _item_detail.php

<td class="col-lg-2">
    <?= Html::activeTextInput($model, "[$key]product_id", ['class'=>'form-control','required' => true]) ?>
</td>
<td class="col-lg-2">
    <?= Html::activeTextInput($model, "[$key]qty", ['class'=>'form-control','required' => true]) ?>
</td>
<td class="col-lg-2">
    <?= Html::activeTextInput($model, "[$key]price", ['class'=>'form-control','required' => true]) ?>
</td>
<td  class="col-lg-1" style="text-align: center">
    <a data-action="delete" title="Delete" href="#"><span class="glyphicon glyphicon-trash"></span></a>
</td>
Advertisements