Pada tulisan ini saya akan membuat series mengenai pemrograman dengan Yii Framework. Series ini akan disusun untuk membuat sebuah program sederhana yang memanfaatkan fitur-fitur umum yang sangat mungkin digunakan di banyak kasus. Tulisan ini akan menggunakan Basic Template Yii2 (yang menurut saya penggunaannya sedikit lebih sulit daripada advance template Yii2).

Secara garis besar (dan mungkin akan bertambah sesuai keperluan yang mungkin terjadi) outline dari seri tulisan ini akan terdiri dari hal berikut.

  1. Prainstalasi
  2. Instalasi YiiFramework 2.0
  3. Login
  4. Login dengan Database pada Yii2
  5. Bekerja dengan Gii
  6. Module pada Yii2
  7. Layout dasar dan Manipulasinya pada Yii2
  8. Costum Asset dan Asset Bundle pada Yii2
  9. Alias pada Yii2
  10. Bekerja dengan Form
  11. Timestamp, Blameable, dan Sluggable Behavior pada Yii2
  12. Menggunakan Rich Text Input CkEditor dan Alternatifnya pada Yii2
  13. Gridview dan Listview
  14. SEO Friendly Url dengan slug
  15. Scenario pada Model Yii2
  16. Retrieve data pada Yii2
  17. Relasi Database pada Yii2
  18. Menggunakan Bootstrap4
  19. Widget Kartik dan Kartik Gridview
  20. Select2 dengan Kartik Ekstension
  21. Dependent Dropdown pada Yii2
  22. Bekerja dengan Modals
  23. Membuat Costum Template untuk Gii
  24. Mengupload File
  25. Mengupload File dengan Kartik Widget
  26. Gridview atau Datatables?
  27. Session dan Cookie pada Yii2
  28. Menggunakan AdminLTE pada Yii2
  29. Membuat Themes pada Yii2
  30. Menggunakan GoogleMaps API pada Yii2
  31. Menggunakan Socket.io pada Yii2
  32. Handling Error
  33. Bekerja dengan AuthClient
  34. Menggunakan Amazon S3 pada Yii2
  35. Mengirim Email dengan Swiftmailer pada Yii2
  36. Middleware pada Yii2
  37. Mengenal RBAC pada Yii2
  38. Implementasi RBAC pada Yii2
  39. Notifikasi Real Time dengan Socket.io pada Yii2

Anda dapat mengunduh dan memantau progress dari series ini lewat repositori Github belajararief-yii2series. Silahkan bintangi (star) untuk dapat lebih mudah memantau perkembangan repositori.

Requirement

Untuk memudahkan anda dalam memahami petunjuk ini, maka beberapa hal yang perlu diperhatikan diantaranya:

  1. Sistem Operasi yang saya gunakan adalah Windows 10 64bit dengan terminal menggunakan powershell terminal (beberapa command seperti cd dapat berbeda dengan command prompt biasa), namun saya akan berusaha sebisa mungkin mencontohkan perintah pada sistem operasi lain jika memunkinkan.
  2. Stack yang digunakan adalah Wamp Server (Apache 2.2, MySQL, PHP 7).
  3. Yii yang digunakan adalah Yii 2.0 dengan catatan jquery yang digunakan bukan jquery3. Beberapa perintah jquery akan berbeda pada jquery3, dan beberapa extensions yang saya gunakan sepertinya belum mendukung jquery3

Modal Bootstrap 4

Modal adalah elemen UI yang muncul di atas aplikasi utama yang biasanya diletakkan pada layer terdepan yang dapat berinteraksi dengan pengguna. Modal secara UX memaksa pengguna untuk menyelesaikan tugas tertentu sebelum kembali pada workflow aplikasi tanpa harus berpindah halaman. Penggunaan modal memang menambah interaksi aplikasi dengan pengguna menjadi lebih dinamis. Tujuan dari penggunaan modal dari sisi UX terutama untuk memfokuskan pengguna pada apa isi dari Modal.  Agar penggunaan Modal tidak uverused dan mengganggu pengguna maka beberapa UX designer memiliki panduan dalam penggunaan modal, diantaranya:

  • The modal window should have a descriptive title (Modal harus memiliki judul)
  • The modal window should have a Cancel/Close button (Modal harus memiliki tombol cancel/close)
  • Escape key should close the window (Escape Key harus dapat digunakan untuk menutup modal)
  • Close when clicking outside the modal window (Modal tertutup ketika pengguna menekan area di luar Modal)
  • Add a drop shadow and a transparent background (tambahkan bayangan dan latar belakang yang transparan)
  • Don’t make the modal with too much contents in it. This is confusing (Jangan menambahkan konten yang tidak fokus dan terlalu banyak pada modal)
  • Avoid Modal on top Modal, it's not consistent, it's not focused, and it's overly complex (Hindari modal di atas modal, karena itu tidak konsisten, tidak fokus, dan terlalu kompleks)

Dan untungnya Modal pada Bootstrap 4 sudah mendukung best practice dalam penggunaan modal sehingga dapat memaksimalkan pengelaman penggunaan aplikasi kita. Selengkapnya tentang Bootstrap Modal dapat anda baca di link ini.

Eksekusi Modal

Yii2 hadir secara default dengan Bootstrap 3. Namun pada series kita kali ini kita telah mengupgrade aplikasi kita untuk menggunakan Bootstrap 4. Modal merupakan salah satu komponen pada Bootstrap yang akan kita gunakan pada aplikasi kita.

Untuk menggunakan modal pada view maka kita dapat menggunakan kode berikut.

use yii\bootstrap\Modal;

Modal::begin([
    'title' => 'Hello world',
    'toggleButton' => ['label' => 'click me'],
]);

echo 'Say hello...';

Modal::end();

Kode di atas dimulai dengan method begin untuk memulai wrapper Modal, kemudian lengkapi dengan atribut modal kita mulai dari title, dan toggleButton (jika dibutuhkan). Modal diakhiri dengan method End. Semua tampilan baik itu echo PHP maupun HTML yang ada di antara method begin dan End akan menjadi bagian Body dari modal yang kita buat.

Ajax CRUD Modal

Oke saat ini kita akan mengoptimasi laman administrasi blog kita sehingga operasi CRUD dapat berjalan menggunakan Ajax CRUD Modal. Kita akan memodifikasi operasi pada create, update, dan view sehingga bukannya berpindah halaman, aplikasi akan memunculkan modal yang dipanggil dengan Ajax.

Pertama tambahkan template modal pada halaman app\modules\blog\views\index.php pada module manajemen blog.  Kita akan menggunakan modal dengan ukuran large, dengan id myModal dan set tabindex bernilai false (ini diperlukan agar select2 dapat berjalan dengan baik di Modal).

use yii\bootstrap4\Modal;


// ----------------


Modal::begin([
    'title' => 'Modal Title',
    'options' => [
        'id' => 'myModal',
        'tabindex' => false // important for Select2 to work properly
    ],
    'size' => 'modal-lg',
]);

echo '...';

Modal::end();

Selanjutnya kita modifikasi tombol create , update dan view sehingga bagian view akan menjadi seperti ini.

            <p>
                <?= Html::a('Create Blog', ['create'], [
                    'class' => 'btn btn-success',
                    'data-toggle'=>"modal",
                    'data-target'=>"#myModal",
                    'data-title'=> "Create Blog",
                ]) ?>
            </p>


// -------------------------------

                    [
                        'class' => 'kartik\grid\ActionColumn',
                        'noWrap' => true,
                        'template' => '{view} {update} {delete}',
                        'buttons' => [
                            'view' => function( $url, $model){
                                return Html::a('<i class="fas fa-eye"></i>', $url, [
                                    'data-toggle'=>"modal",
                                    'data-target'=>"#myModal",
                                    'data-title'=> "View Post #{$model->id}",
                                ]);
                            },
                            'update' => function( $url, $model){
                                return Html::a('<i class="fas fa-pencil-alt"></i>', $url, [
                                    'data-toggle'=>"modal",
                                    'data-target'=>"#myModal",
                                    'data-title'=> "Update Post #{$model->id}",
                                ]);
                            },
                        ],
                    ],  

Selanjutnya tambahkan JS untuk memanggil halaman tersebut via ajax call.

$this->registerJs(<<<JS

    // this will generate Modal from crud
    $('#myModal').on('show.bs.modal', function (event) {
        var button = $(event.relatedTarget)
        var modal = $(this)
        var title = button.data('title') 
        var href = button.attr('href') 
        modal.find('.modal-title').html(title)
        modal.find('.modal-body').html('<i class=\"fas fa-spinner fa-spin\"></i>')
        $.post(href)
        .done(function( data ) {
            modal.find('.modal-body').html(data)
        });
    })
JS
);

Kemudian modifikasi controller pada  app\modules\blog\controllers\BlogController.php  agar ketika sebuah halaman dipanggil dengan ajax maka akan melakukan renderAjax dan pada create dan update memberikan tanda keberhasilan menyimpan.

    /**
     * Displays a single Blog model.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionView($id)
    {
        $request = Yii::$app->request;
        $render = "render";

        // if ajax request comes, overrider render to renderAjax
        if($request->isAjax) $render = "renderAjax";

        return $this->{$render}('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new Blog model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $request = Yii::$app->request;
        $render = "render";

        // if ajax request comes, overrider render to renderAjax
        if($request->isAjax) $render = "renderAjax";

        $model = new Blog();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            if($request->isAjax) return 1;
            return $this->redirect(['view', 'id' => $model->id]);
        }elseif($model->load(Yii::$app->request->post()) && !$model->save()){
            if($request->isAjax) return 0;
        }

        return $this->{$render}('create', [
            'model' => $model,
        ]);
    }

    /**
     * Updates an existing Blog model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionUpdate($id)
    {
        $request = Yii::$app->request;
        $render = "render";

        // if ajax request comes, overrider render to renderAjax
        if($request->isAjax) $render = "renderAjax";

        $model = $this->findModel($id);

        // if user who update different from user who create, scenario set to editByOther
        if($model->created_by != Yii::$app->user->identity->id) $model->scenario = 'editByOther';

        if ($model->load(Yii::$app->request->post())) {
            // prevent user from change title if that article is not created by them
            if($model->created_by != Yii::$app->user->identity->id) $model->title = $model->oldAttributes['title'];
            if($model->save()){
                if($request->isAjax) return 1;
                return $this->redirect(['view', 'id' => $model->id]);
            }else{
                if($request->isAjax) return 0;
            }
        }

        return $this->{$render}('update', [
            'model' => $model,
        ]);
    }

Terakhir modifikasi  app\modules\blog\views\_form.php agar jika halaman tersebut dipanggil via ajax call maka submit akan menunggu status penyimpanan dari server.

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use dosamigos\ckeditor\CKEditor;
use kartik\select2\Select2;
use yii\helpers\ArrayHelper;

/* @var $this yii\web\View */
/* @var $model app\models\Blog */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="blog-form">

    <?php $form = ActiveForm::begin(['id' => $model->formName()]); ?>

    <?= $form->field($model, 'title')->textInput(['maxlength' => true, 'disabled' => $model->scenario == 'editByOther' ? true : false]) ?>

    <?= $form->field($model, 'category_id')->widget(Select2::classname(), [
        'data' => ArrayHelper::map($model->categoryList, 'id', 'name'),
        'options' => ['placeholder' => 'Select a category ...'],
        'pluginOptions' => [
            'allowClear' => true
        ],
    ]) ?>

    <?= $form->field($model, 'body')->widget(CKEditor::class, [
        'options' => ['rows' => 6],
        'preset' => 'basic'
    ]) ?>

    <div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
    </div>

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

</div>
<?php
// when from ajax request, submit button will lock submit button and hide modal if success
if(Yii::$app->request->isAjax) $this->registerJs(<<<JS
    $('form#{$model->formName()}').on('beforeSubmit',function(e)
    {
        var \$form = $(this);
        $.post(
            \$form.attr("action"), //serialize Yii2 form 
            \$form.serialize()
        )
            .done(function(result){
                if(result == 1)
                {
                    $("#myModal").modal('hide'); //hide modal after submit
                    //$(\$form).trigger("reset"); //reset form to reuse it to input
                    $.pjax.reload({container:'#kv-grid-demo-pjax'});
                }else
                {
                    $("#message").html(result);
                }
            }).fail(function(){
                console.log("server error");
            });
        return false;
    });
JS
);

Demikian tulisan ini, semoga bermanfaat dan Happy Coding!