Skip navigation


Menyambung tulisan dari Bung Aqsath mengenai pengenalan OOP di PHP, gw ada sedikit tambahan yang menarik yakni mengenai konsep ORM.

ORM atau Object Relational Mapping adalah teknik yang menghubungkan basisdata relasional dengan konsep OOP. Dengan menggunakan ORM kita dapat dengan mudah mempertahankan persistensi objek pada database. Kita dapat melakukan pembuatan obyek dan manipulasi-manipulasinya sembari mempertahankan isi data pada database yang direpresentasikan oleh objek tersebut.

Teknik ini sudah banyak sekali diimplementasikan diframework-framework seperti KohanaPHP, Symfony, CakePHP, dll.

Kadang-kadang permasalahan yang muncul saat kita harus menggabungkan lojik dari sebuah objek dengan pengambilan data di basisdata. Biasanya kita melakukan hal-hal di bawah ini

<?php
//file: index.php
    require_once('database.php');
    require_once('Page.php');

    $page = new Page();
    $id = 1;

    $result = mysql_query('SELECT * FROM `pages` WHERE `id` = '.$id.' LIMIT 1');

    $row = mysql_fetch_assoc($result);

    $page->id = $row['id'];
    $page->user_id = $row['user_id'];
    $page->title = $row['title'];
    $page->content = $row['content'];
    $page->post_time = $row['post_time'];
    var_dump($page);
//end of file

Sebelumnya kita punya basis data seperti ini

CREATE TABLE `pages` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `user_id` int(10) unsigned NOT NULL,
  `title` text collate latin1_general_ci NOT NULL,
  `content` text collate latin1_general_ci NOT NULL,
  `post_time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=1 ;

INSERT INTO `pages` VALUES (1, 1, 'Halo', 'Halo Bandung', '2009-03-07 20:30:11');

dan class seperti ini

<?php
//file Page.php
class Page {
    public $id;
    public $user_id;
    public $title;
    public $content;
    public $post_time;
}
//end of file
&#91;/sourcecode&#93;

Keluaran dari <em>index.php</em> adalah
<pre>object(Page)#1 (5) {
  ["id"]=&gt;
  string(1) "1"
  ["user_id"]=&gt;
  string(1) "1"
  ["title"]=&gt;
  string(4) "Halo"
  ["content"]=&gt;
  string(12) "Halo Bandung"
  ["post_time"]=&gt;
  string(19) "2009-03-07 20:30:11"
}</pre>
Secara manual melakukan koneksi ke basisdata dan melakukan <em>fetch</em> kemudian mengisikan <em>property </em>pada sebuah objek. Permasalahan yang akan timbul adalah ketika kita mengubah data-data pada objek tersebut dan kita harus menyimpannya dalam basisdata berkali-kali. Akan jadi mimpi buruk harus melakukan penyimpanan berkali-kali untuk setiap objek yang dihasilkan


<?php
//file: index.php
    require_once('database.php');
    require_once('Page.php');

    $page = new Page();
    $id = 1;

    $result = mysql_query('SELECT * FROM `pages` WHERE `id` = '.$id.' LIMIT 1', $conn);

    if (!mysql_num_rows($result)) die('No row fetched');

    $row = mysql_fetch_assoc($result);

    $page->id = $row['id'];
    $page->user_id = $row['user_id'];
    $page->title = $row['title'];
    $page->content = $row['content'];
    $page->post_time = $row['post_time'];

    $page->content = "Hello Bandung";

    mysql_query('UPDATE `pages` SET `user_id` = '.$page->user_id.', `title` = \''.$page->title.'\', `content` = \''.$page->content.'\', `post_time` = \''.$post->post_time.'\' WHERE `id` = '.$id);

//end of file

Untuk membuat lebih mudah, fungsi-fungsi mysqlnya bisa diembbed di kode class yang bersangkutan.

<?php
//file Page.php
class Page {
    public $id;
    public $user_id;
    public $title;
    public $content;
    public $post_time;

    private $_conn;

    public function __construct($id)
    {
        $this->_conn = $GLOBALS['conn'];
        $result = mysql_query('SELECT * FROM `pages` WHERE `id` = '.$id.' LIMIT 1', $this->_conn);
        $row = mysql_fetch_assoc($result);
        $this->id = $row['id'];
        $this->user_id = $row['user_id'];
        $this->title = $row['title'];
        $this->content = $row['content'];
        $this->post_time = $row['post_time'];
    }

    public function save()
    {
        mysql_query('UPDATE `pages` SET `user_id` = '.$this->user_id.', `title` = \''.$this->title.'\', `content` = \''.$this->content.'\', `post_time` = \''.$post->post_time.'\' WHERE `id` = '.$this->id, $this->_conn);
    }
}
//end of file

Akan sangat memudahkan nantinya

<?php
//file: index.php
    require_once('database.php');
    require_once('Page.php');

    $page = new Page(1);
    echo $page->content;
    $page->content = "Hello Bandung!";
    $page->save();
//end of file

Itu baru ngeload doang. Kadang-kadang kita butuh melakukan instansiasi membuat objek baru dan menyimpannya ke database. Nah, untuk itu kita perlu memodifikasi kode Classnya supaya kalau id tidak dipass ke konstruktor maka objek akan berusaha masuk ke dalam basis data saati disimpan.

<?php
//file Page.php
class Page {
    public $id;
    public $user_id;
    public $title;
    public $content;
    public $post_time;
    public $loaded;
    private $_conn;

    public function __construct($id = NULL)
    {
        $this->_conn = $GLOBALS['conn'];
        $this->loaded = FALSE;
        $this->id = NULL;
        if ($id != NULL) //Jika $id dipass di konstruktor
        {
            $this->loaded = $this->__load($id);
        }
    }

    //Menyimpan
    public function save()
    {
        if ($this->loaded) //Jika sudah diload sebelumnya
        {
            mysql_query('UPDATE `pages` SET `user_id` = '.$this->user_id.', `title` = \''.$this->title.'\', `content` = \''.$this->content.'\', `post_time` = \''.$post->post_time.'\' WHERE `id` = '.$this->id, $this->_conn);
        }
        else
        {

            $query = 'INSERT INTO `pages` (`user_id`, `title`, `content`, `post_time`) VALUES ('.$this->user_id.',\''.$this->title.'\',\''.$this->content.'\', CURRENT_TIMESTAMP)';
            mysql_query($query, $this->_conn);
            echo $query.PHP_EOL;
            $this->id = mysql_insert_id();
            $this->__load($this->id);
            $this->loaded = TRUE;
        }
    }

    //Mengambil data dari basis data berdasarkan id
    private function __load($id)
    {
        $result = mysql_query('SELECT * FROM `pages` WHERE `id` = '.$id.' LIMIT 1', $this->_conn);
        if (!mysql_num_rows($result))
            return FALSE;
        $row = mysql_fetch_assoc($result);
        $this->id = $row['id'];
        $this->user_id = $row['user_id'];
        $this->title = $row['title'];
        $this->content = $row['content'];
        $this->post_time = $row['post_time'];
        return TRUE;
    }
}
//end of file

Pada kode di atas, Class akan mengecek keberadaan ID pada konstruktor. Jika tidak ada maka ketika nanti disave, objek akan melakukan insert ke database. Dan dengan mudahnya kita dapat membuat-buat objek-objek baru.

<?php
//file: index.php
    require_once('database.php');
    require_once('Page.php');

    $page = new Page(1);
    echo $page->content;
    $page->content = "Hello Bandung!";
    $page->save();

    $page2 = new Page();
    $page2->user_id = 2;
    $page2->title = "Hello2 Bandung";
    $page2->content = "Hello Hello Bandung";
    $page2->save();

//end of file

Solusi di atas masih kurang generik, karena masih menyusahkan ketika kita ingin membuat class baru dengan fitur sama. Cara mudahnya adalah membuat sebuah class abstract yang baru yang nantinya memiliki fitur-fitur tersebut ketika diextends oleh Class yang kita ingin buat.

Masalah pertama adalah bagaimana mengembalikan dan menyimpan property dengan cara yang cukup generik? Jawabannya adalah dengan menggunakan method overload __get dan __set. Fungsi __get adalah method yang dipanggil ketika kita membutuhkan sebuah property pada sebuah objek yang tidak didefinisikan pada Class. Sama halnya,  __set adalah method yang dipanggil ketika kita akan menyimpan property pada sebuah objek yang tidak didefinisikan pada Class. Method ada di semua Class dan hanya ada di PHP5 dan tidak kompatibel di PHP4.
Pertama-tama kita membuat sebuah Class abstrak bernama ORM yang nantinya akan diinherit oleh berbagai macam Class.

<?php
//file ORM.php
abstract class ORM {
    public $id;
    public $loaded;
    private $_conn;
    private $_classname;
    private $_tablename;
    private $_properties;

    public function __construct($id = NULL)
    {
        $this->_conn = $GLOBALS['conn'];
        $this->loaded = FALSE;
        $this->id = NULL;

        $this->_classname = get_class($this); //Mengambil nama class dari instansiasi
        $this->_tablename = strtolower($this->_classname).'s'; //Mengambil nama tabel dari class. Asumsi pakai suffix 's'

        if ($id != NULL) //Jika $id dipass di konstruktor
        {
            $this->loaded = $this->__load($id);
        }
    }

Kita menggunakan fungsi get_class yang akan mengembalikan nama Class dari sebuah instance. Kita juga mendefinisikan bahwa table yang akan digunakan merupakan pluralisme dari nama Class. Asumsi dengan menggunakan suffix ‘s’ dulu, misalnya Post menjadi posts, page menjadi pages, User menjadi users.

Kemudian kita definisikan fungsi loadnya.

    private function __load($id)
    {
        $tablename = $this->_tablename;
        $result = mysql_query('SELECT * FROM `'.$tablename.'` WHERE `id` = '.$id.' LIMIT 1', $this->_conn);
        if (!mysql_num_rows($result))
            return FALSE;
        $row = mysql_fetch_assoc($result);
        $fields = array_keys($row);
        $this->_properties = array();
        //memasukkan ke array _properties
        foreach($fields as $field)
        {
            $this->_properties[$field] = $row[$field];
        }
        return TRUE;
    }

Di sini kita menggunakan fungsi yang sangat saya sukai, yakni array_keys yang mengambil key dari array. Kita kemudian memasukkan setiap isi dari hasil fetch dari database ke dalam variabel properties milik objek.

Lalu kita mendefinisikan fungsi __set dan __get sehingga dapat mengembalikan nilai dari property yang diminta

function __get($prop_name)
    {
        if (isset($this->_properties[$prop_name])) {
            return $this->_properties[$prop_name];
        } else {
            return false;
        }
    }

    function __set($prop_name, $prop_value)
    {
        $this->__properties[$prop_name] = $prop_value;
        return true;
    }

Method-method tersebut akan menerima nama property sebagai parameter. Lalu kita akan mengambil dan menyimpan nilai yang ada di variabel properties berdasarkan parameter tersebut.

Terakhir kita akan menyempurnakan fungsi savenya.

    //Menyimpan
    public function save()
    {
        if ($this->loaded) //Jika sudah diload sebelumnya
        {
            $keys = array_keys($this->_properties);
            $query = 'UPDATE `pages` SET ';
            foreach($keys as $key)
            {
                if ($key == 'id') continue;
                $query .= '`'.$key.'` = \''.$this->_properties[$key].'\', ';
            }
            $query = substr($query, 0, strlen($query) - 2);
            $query .= ' WHERE `id` = '.$this->_properties['id'];
            mysql_query($query, $this->_conn);
        }
        else
        {
            $keys = array_keys($this->_properties);
            $query = 'INSERT INTO `pages` (';
            foreach($keys as $key)
            {
                if ($key == 'id') continue;
                $query .= '`'.$key.'`, ';
            }
            $query = substr($query, 0, strlen($query) - 2);
            $query .= ') VALUES (';
            foreach($keys as $key)
            {
                if ($key == 'id') continue;
                $query .= '\''.$this->_properties[$key].'\', ';
            }
            $query = substr($query, 0, strlen($query) - 2);
            $query .= ')';
            mysql_query($query, $this->_conn);
            $this->id = mysql_insert_id();
            $this->__load($this->id);
            $this->loaded = TRUE;
        }
    }

Fungsi ini pertama-tama akan membangunquery berdasarkan nilai apa saja yang ada di variabel properties. Setelah itu dengan mudahnya kita dapat menyimpan objek-objek langsung ke dalam basis data.

Kode penuhnya

<?php
//file Page.php
abstract class ORM {
    public $id;
    public $loaded;
    private $_conn;
    private $_classname;
    private $_tablename;
    private $_properties;

    public function __construct($id = NULL)
    {
        $this->_conn = $GLOBALS['conn'];
        $this->loaded = FALSE;
        $this->id = NULL;

        $this->_classname = get_class($this); //Mengambil nama class dari instansiasi
        $this->_tablename = strtolower($this->_classname).'s'; //Mengambil nama tabel dari class. Asumsi pakai suffix 's'

        if ($id != NULL) //Jika $id dipass di konstruktor
        {
            $this->loaded = $this->__load($id);
        }
    }

    //Menyimpan
    public function save()
    {
        $tablename = $this->_tablename;
        if ($this->loaded) //Jika sudah diload sebelumnya
        {
            $keys = array_keys($this->_properties);
            $query = 'UPDATE `'.$tablename.'` SET ';
            foreach($keys as $key)
            {
                if ($key == 'id') continue;
                $query .= '`'.$key.'` = \''.$this->_properties[$key].'\', ';
            }
            $query = substr($query, 0, strlen($query) - 2);
            $query .= ' WHERE `id` = '.$this->_properties['id'];
            mysql_query($query, $this->_conn);
        }
        else
        {
            $keys = array_keys($this->_properties);
            $query = 'INSERT INTO `'.$tablename.'` (';
            foreach($keys as $key)
            {
                if ($key == 'id') continue;
                $query .= '`'.$key.'`, ';
            }
            $query = substr($query, 0, strlen($query) - 2);
            $query .= ') VALUES (';
            foreach($keys as $key)
            {
                if ($key == 'id') continue;
                $query .= '\''.$this->_properties[$key].'\', ';
            }
            $query = substr($query, 0, strlen($query) - 2);
            $query .= ')';
            mysql_query($query, $this->_conn);
            $this->id = mysql_insert_id();
            $this->__load($this->id);
            $this->loaded = TRUE;
        }
    }

    //Mengambil data dari basis data berdasarkan id
    private function __load($id)
    {
        $tablename = $this->_tablename;
        $result = mysql_query('SELECT * FROM `'.$tablename.'` WHERE `id` = '.$id.' LIMIT 1', $this->_conn);
        if (!mysql_num_rows($result))
            return FALSE;
        $row = mysql_fetch_assoc($result);
        $fields = array_keys($row);
        $this->_properties = array();
        foreach($fields as $field)
        {
            $this->_properties[$field] = $row[$field];
        }
        return TRUE;
    }

    function __get($prop_name)
    {
        if (isset($this->_properties[$prop_name])) {
            return $this->_properties[$prop_name];
        } else {
            return false;
        }
    }

    function __set($prop_name, $prop_value)
    {
        $this->_properties[$prop_name] = $prop_value;
        return true;
    }
}
//end of file

Kode pada Page.php hanya tinggal di bawah ini. Tapi kita bisa menambahkan method lojik. Sangat menyenangkan🙂

<?php
//file Page.php
class Page extends ORM {

}
//end of file
&#91;/sourcecode&#93;

Dan kode index hanya tinggal di bawah ini.

&#91;sourcecode language='php'&#93;
<?php
//file: index.php
    require_once('database.php');
    require_once('ORM.php');
    require_once('Page.php');

    $page = new Page();

    echo $page->title;
    $page->title = "TES TES TES";
    $page->save();

//end of file

Kita juga dapat menambahkan Class baru misalnya User hanya dengan menambahkan sebuah file yang tidak jauh berbeda dengan class Page dan juga sebuah table.

<?php
//file User.php
class User extends ORM {

}
//end of file
&#91;/sourcecode&#93;

&#91;sourcecode language='sql'&#93;
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `username` varchar(32) collate latin1_general_ci NOT NULL,
  `password` varchar(32) collate latin1_general_ci NOT NULL,
  `full_name` text collate latin1_general_ci NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=3 ;
&#91;/sourcecode&#93;

&#91;sourcecode language='php'&#93;
<?php
//file: index.php
    require_once('database.php');
    require_once('ORM.php');
    require_once('Page.php');
    require_once('User.php');

    $user = new User();
    $user->username = "nama user";
    $user->password = "password user";
    $user->full_name = "nama panjang user";
    $user->save();

//end of file

ORM yang dibahas kali ini adalah ORM yang sederhana, masih belum bisa merepresentasikan relasi antara class.

Teknik ini biasanya dikombinasikan dengan fungsiautoload seperti yang di postingan sebelumnya sehingga membuatan class dapat dengan mudah dilakukan tanpa melakukan include yang banyak.

Kode bisa diambil di sini. As usual, direname ekstensinya ke *.zip.

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

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: