Memakai MVC dan Model Binding Di Laravel


MVC pada awalnya berasal dari dunia pemograman desktop. Komponen pada MVC terdiri atas model yang berisi data, view yang mempresentasikan data di model, dan controller yang memanipulasi model. Pada kebanyakan framework MVC desktop modern seperti Griffon, WPF dan JavaFX, terdapat binding antara model dan view (melalui observer pattern) sehingga perubahan yang dilakukan oleh controller pada model akan menyebabkan perubahan pada view, dan sebaliknya, perubahan pada view (misalnya akibat input dari pengguna) akan menyebabkan perubahan pada model. Pada MVC desktop, model murni dipakai untuk mewakili data pada view dan bukan sebuah domain model yang harus bisa berdiri sendiri tanpa terikat pada layer apapun termasuk presentation layer (MVC). Yup! Data pada GUI berbeda dengan yang ada di domain model (permasalahan bisnis), misalnya, pada GUI bisa saja ada data berupa total harga, total diskon, informasi kesalahan, nomor sementara dan sebagainya yang harus ditampilkan.

Pada pemograman web, MVC memiliki implementasi yang berbeda karena view ‘setengah’ berada di client sementara sisanya ada di server. Dengan demikian, tidak ada binding secara langsung antara model dan view. Perubahan pada model tidak akan langsung mempengaruhi view (tampilan di browser) dan juga sebaliknya! Salah satu bentuk implementasi pada Spring Web MVC adalah memakai model yang diwakili oleh Map. Model dalam bentuk Map tersebut dapat diterjemahkan menjadi sebuah class dengan menggunakan @ModelAttribute. Dalam banyak kasus, @ModelAttribute dipakai untuk menerjemahkan setiap elemen dalam Map langsung menjadi domain model. Ada juga aliran puritan yang ‘menentang’ dan tetap ingin mempertahankan view model pada Web MVC. Kecenderungan menganggap domain model sebagai view model kerap menghasilkan domain model yang hanya berisi data tanpa banyak operasi dan berdiri sendiri tanpa banyak kolaborasi (dengan kata lain: kurang OOP!).

Pada kesempatan ini, saya akan mencoba memakai Web MVC pada framework Laravel. Saya akan mulai dengan membuat model bernama Mahasiswa.php pada lokasi app/models. Pada Laravel, sama seperti Web MVC pada umumnya, model pada MVC langsung merujuk pada domain model. Laravel memiliki ORM (atau tepatnya sebuah implementasi active record pattern) yang diberi nama Eloquent ORM untuk menyimpan dan membaca domain model dari database. Ini memang subjektif, tapi bagi saya, simple-jpa tetap lebih praktis untuk dunia nyata yang memang kompleks🙂

Sebagai contoh, saya membuat isi Mahasiswa.php seperti berikut ini:

<?php

use CarbonCarbon;

class Mahasiswa extends Eloquent {

    protected $table = 'mahasiswa';

    protected $fillable = array('nama', 'alamat', 'tanggalLahir');

    protected $dates = array('tanggalLahir');

    public function getUsia() {     
        return isset($this->tanggalLahir)? $this->tanggalLahir->age: 0;
    }
}

?>

Saya menurunkan Mahasiswa dari Eloquent sehingga ia bisa disimpan dan dibaca dari database dengan active record pattern dari Eloquent. Pada PHP yang bersifat dinamis, saya tidak perlu mendefinisikan seluruh atribut yang ada pada Mahasiswa karena mereka bisa muncul sendiri saat ditambahkan. Ini bisa jadi sesuatu yang buruk. Oleh sebab itu, saya mendeklarasikan apa saja atribut yang ada dan dapat di-isi pada $fillable.

Pada simple-jpa, saya memakai Joda-Time karena penanganan tipe data tanggal yang sangat buruk di Java 7 (dan hal ini sudah diperbaiki di Java 8). Sama seperti yang saya lakukan, Laravel memakai library Carbon untuk meningkatkan kemampuan class DateTime bawaan PHP. Bila saya ingin memakai Carbon pada atribut yang mewakili tanggal seperti tanggalLahir, saya perlu mendaftarkannya di $dates. Bila ini tidak dilakukan, tanggalLahir akan dianggap seperti tipe data tanggal native di PHP yang berupa angka dan non-object.

Tidak seperti pada JPA yang memungkinkan pendekatan object-first dimana pengguna merancang domain model baru kemudian menghasilkan database berdasarkan hasil rancangan, pada Laravel, saya harus membuat tabel secara manual. Saya akan mulai dari mengubah informasi koneksi database di database.php. Laravel menyediakan fasilitas migration (sejenis Flyway di Java) untuk mempermudah memindahkan tabel dari development ke server produksi nanti. Untuk itu, saya akan memberikan perintah berikut ini:

php artisan migrate:make tabelAwal

Perintah di atas akan membuat file baru di app/database/migrations yang diawali dengan timestamp seperti 2014_05_03_09139_tabelAwal.php. Saya akan memakai fasilitas schema builder dari Laravel untuk membuat tabel Mahasiswa sehingga kode program akan terlihat seperti:

<?php

use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
use IlluminateSupportFacadesSchema;

class TabelAwal extends Migration {

    public function up()
    {
        Schema::create('mahasiswa', function($table) {
            $table->increments('id');
            $table->string('nama', 50);
            $table->string('alamat', 100);
            $table->date('tanggalLahir');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('mahasiswa');
    }

}

?>

Setelah ini, untuk mengerjakan kode program di atas, saya memberikan perintah:

php artisan migrate

Pada kasus yang kompleks dimana ada banyak domain model, saya akan sangat merindukan perintah griffon generate-schema dari simple-jpa yang dapat menghasilkan file SQL secara otomatis🙂

Berikutnya, saya dapat menguji class Mahasiswa dengan membuat unit test sederhana seperti berikut ini:

<?php

use CarbonCarbon;

class MahasiswaTest extends TestCase {

    public function testUsia() {
        $mahasiswa = new Mahasiswa();
        $mahasiswa->id = 1;
        $mahasiswa->nama = 'Solid Snake';
        $mahasiswa->tanggalLahir = Carbon::createFromDate(1985,12,25);
        $mahasiswa->save();

        $mahasiswa = Mahasiswa::find(1);                
        $this->assertEquals(28, $mahasiswa->getUsia());

        $mahasiswa->tanggalLahir = null;
        $this->assertEquals(0, $mahasiswa->getUsia());

        $mahasiswa->delete();                
    }

}

?>

Setelah memastikan bahwa model sudah siap, saya akan lanjut dengan membuat view. Laravel memiliki fasilitas templating untuk view yang disebut sebagai Blade. Untuk memakai Blade, saya hanya perlu mengakhiri nama file dengan .blade.php. Sebagai contoh, saya akan membuat view dengan nama tambahMahasiswa.blade.php di app\views yang isinya seperti berikut ini:

<html>
    <body>
        <h1>Entry Data Mahasiswa</h1>

        {{ Form::model($mahasiswa, array('action' => array('MahasiswaController@simpan', $mahasiswa->id))) }}

            <div>
            {{ Form::label('nama', 'Nama') }}
            {{ Form::text('nama') }}
            </div>

            <div>
            {{ Form::label('alamat', 'Alamat') }}
            {{ Form::text('alamat') }}            
            </div>

            <div>
            {{ Form::label('tanggalLahir', 'Tanggal Lahir') }}
            {{ Form::selectRange('tanggal', 1, 31, isset($mahasiswa->tanggalLahir)? $mahasiswa->tanggalLahir->day: 1)}}
            {{ Form::selectMonth('bulan', isset($mahasiswa->tanggalLahir)? $mahasiswa->tanggalLahir->month: 0) }}
            {{ Form::selectYear('tahun', 1940, 2013, isset($mahasiswa->tanggalLahir)? $mahasiswa->tanggalLahir->year: 1940) }}
            </div>

            <div>
            {{ Form::submit('Simpan') }}
            </div>

        {{ Form::close() }}

        <div>
            {{ $pesan }}
        </div>

    </body>
</html>

Pada view di atas, saya memakai form builder dari Laravel untuk menghasil HTML Form secara otomatis. Agar view di atas dapat ditampilkan, saya perlu menambahkan baris berikut ini pada file routes.php sehingga isinya menjadi seperti berikut ini:

Route::get('/tambah', function()
{
    return View::make('tambahMahasiswa', array('mahasiswa' => new Mahasiswa(), 'pesan' => NULL));   
});

Sekarang, saya bisa mengakses view tersebut dengan membuka URL seperti http://localhost/latihan/public/tambah di browser.

Komunikasi dari view ke controller terjadi bila tombol Simpan di-klik oleh pengguna. Pada contoh ini, method bernama simpan() di MahasiswaController akan dikerjakan. Sebelum method tersebut dapat dipanggil dari view, saya perlu menambahkan isi routes.php berupa:

Route::model('mahasiswa', 'Mahasiswa');

Route::post('/tambah/proses/{mahasiswa?}', 'MahasiswaController@simpan');

Pada Spring Web MVC, saya mendeklarasikan route secara langsung di controller melalui annotation @RequestMapping pada class tersebut atau masing-masing method miliknya. PHP tidak memiliki fasilitas annotation. Walaupun ada beberapa framework yang memakai ‘sejenis’ annotation, mereka hanya dianggap komentar (misalnya tidak ada fitur bahasa PHP untuk mencari daftar annotation pada sebuah class). Oleh sebab itu, pemetaan antara controller dan URL biasanya didefinisikan pada sebuah file terpisah. Pada Laravel, file tersebut adalah routes.php. Saya menggunakan {mahasiswa?} untuk menunjukkan bahwa parameter tersebut tidak wajib ada (untuk membedakan antara operasi tambah dan operasi edit).

Dengan menggunakan Route::model('mahasiswa', 'Mahasiswa'), Laravel akan menerjemahkan id di semua parameter bernama mahasiswa menjadi model Mahasiswa. Laravel akan men-query ke database dan memberikan object Mahasiswa sebagai parameter di controller nanti. Ini adalah sesuatu yang mirip seperti @ModelAttribute di Spring Web MVC. Bedanya, @ModelAttribute hanya menerjemahkan setiap parameter terpisah (dalam bentuk Map) yang di-isi oleh pengguna ke dalam sebuah class model. Route::model() hanya menerima sebuah id dan melakukan query ke database untuk mengembalikan object berdasarkan id tersebut.

Sekarang, saya siap untuk membuat implementasi dari controller tersebut dalam bentuk sebuah file PHP bernama MahasiswaController.php di folder app/controllers yang isinya seperti berikut ini:

<?php

use IlluminateSupportFacadesInput;
use CarbonCarbon;

class MahasiswaController extends BaseController {

    public function simpan($mahasiswa = NULL) {
        if (is_null($mahasiswa)) {
            $mahasiswa = new Mahasiswa();           
            $pesan = 'Data mahasiswa ini berhasil disimpan!';
        } else {
            $pesan = 'Data mahasiswa berhasil diupdate!';
        }
        $mahasiswa->nama = Input::get('nama');
        $mahasiswa->alamat = Input::get('alamat');
        $mahasiswa->tanggalLahir = Carbon::createFromDate(Input::get('tahun'), Input::get('bulan'), Input::get('tanggal'));
        if ($mahasiswa->getUsia() > 20) {
            $pesan = 'Penyimpanan gagal karena kamu terlalu tua!';
        } else {        
            $mahasiswa->save();
        }                       
        return View::make('tambahMahasiswa', array('mahasiswa' => $mahasiswa, 'pesan' => $pesan));
    }

}

?>

Sekarang, bila saya membuka URL seperti http://localhost/latihan/public/tambah, saya dapat mengisi form dan men-klik tombol Simpan untuk menyimpannya ke database. Setelah itu, view yang sama akan kembali ditampilkan lengkap dengan data seperti pada saat di-isi. Bila kali ini tombol Simpan di-klik, maka data yang sudah ada akan di-update.

Pola komunikasi web MVC yang terjadi pada contoh di atas adalah seperti berikut ini:

  1. Komunikasi controller -> view: Controller melewatkan model ke view sehingga nilai pada form akan terisi sesuai dengan nilai di model. Untuk mencapai hal seperti ini, saya memakai Form::model() di view.
  2. Komunikasi controller <- view: Pada saat view memanggil controller, method pada controller akan memperoleh sebuah model yang dibuat secara otomatis berdasarkan id-nya. Selain itu, nilai di view juga dapat diakses secara manual dengan menggunakan Input::get().

Perihal Solid Snake
I'm nothing...

3 Responses to Memakai MVC dan Model Binding Di Laravel

  1. Mahrizal mengatakan:

    Makasih sharingnya mas

  2. Ham Zah mengatakan:

    cara menggunakan unit test itu spesifiknya bagaimana mas?
    saya kesulitan memanggil classnya

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: