MySQL:: mysql_fetch_object

*บันทึกไว้อ่านกันลืม

พอดีวันนี้ได้กลับมาเขียน PHP แบบที่ต้องเชื่อมต่อกับฐานข้อมูลเองผ่าน mysql_* ที่ติดมากับ PHP หลังจากไม่ได้ใช้มานานแล้วตั้งแต่สมัยมหาวิทยาลัย แต่หลังจากใช้ไปได้สักพักก็เกิดความรู้สึกที่ว่าการใช้ mysql_fetch_array เหมือนที่เคยใช้มันไม่ค่อยสวยเท่าไหร่ พอดีเหลือบไปเห็น mysql_fetch_object เมื่อก่อนก็เคยใช้ แต่พี่งสังเกตว่าเราสามารถระบุคลาสมารับข้อมูลที่อ่านมาได้ เหมือนกับ Object Model ในพวก ORM วันนี้ก็เลยลองของสักหน่อย

ได้โค้ดทั้งหมดดังนี้

<?php
$connection = null;

function __db_connection() {
  global $connection; 

  $connection = mysql_pconnect( '127.0.0.1', 'foo', 'bar' );
  if ( ! $connection )
    $connection = mysql_pconnect( '127.0.0.1', 'foo', 'bar' );

  mysql_select_db( 'my_db_name' );
  mysql_query( 'set names utf8' );
};

class SL_Foo {
  protected $_foo = array(
    'ID'          => '',
    'message'     => '',
    'timestamp'   => ''
  );

  function __get( $name ) {
    global $datetime_format;
    if ( in_array( $name, array( 'timestamp' ) ) )
      return date( $datetime_format, strtotime( $this->_foo[ $name ] ) ) ;

    return @$this->_foo[ $name ];
  }

  function __set( $name, $value ) {
    $this->_foo[ $name ] = $value ;
  }

}

if ( ! $connection ) __db_connection();

$sql = sprintf( "select * from foo where ID = '%s'", addslashes( $_REQUEST['id'] ) );

$results = mysql_query( $sql, $connection ) ;
if ( ! $results ) printf( 'MySQL Error: %s', mysql_error() ) ;

$foos = array();
if ( mysql_num_rows( $results ) )
  while( $foo = mysql_fetch_object( $results, 'SL_Foo' ) ) 
    $foos[] = $foo;

mysql_free_results( $results );

// Do something with $foos
echo var_dump( $foos );

อธิบายโค้ดได้ดังนี้ครับ

  • บรรทัดที่ 2: สร้างตัวแปรเพื่อเก็บการเชื่อมต่อกับฐานข้อมูล
  • บรรทัดที่ 4-13: ฟังก์ชั่นสำหรับการเชื่อมต่อกับฐานข้อมูล
  • บรรทัดที่ 15-34: คือคลาสที่สร้างขึ้นเพื่อรับข้อมูลที่อ่านได้จากฐานข้อมูล โดยมีชื่อฟิลด์คือ ID, message, timestamp ซึ่งชื่อนี้ต้องเหมือนกันกับชื่อฟิลด์ที่มีในฐานข้อมูลแบบ case sensitive
  • บรรทัดที่ 22-32: เป็น Magic methods ของ PHP โดยที่ __get คือเมธอดที่ใช้อ่านค่าต่างๆ ของคลาส โดยสามารถกำหนดเงื่อนไขการการแสดงผลของค่าที่คืนไปได้ เช่นในตัวอย่างคือถ้าค่าที่ผู้ใช้ต้องการคือ timestamp ให้เปลี่ยนรูปแบบจากรูปแบบมาตรฐานในฐานข้อมูลให้กลายเป็นรูปแบบตามที่กำหนดในตัวแปร $datetime_format
  • บรรทัดที่ 36: สร้างการเชื่อมต่อ
  • บรรทัดที่ 38-41: สร้าง Query String และส่งข้อมูล Query เข้าสู่ฐานข้อมูล
  • บรรทัดที่ 45: อ่านค่าที่ได้จาก $results ออกมาเป็น Object โดยนำคลาส SL_Foo ที่กำหนดไว้ข้างต้นมารับค่าที่อ่านได้

จะเห็นได้ว่า ข้อดีของการสร้างคลาสเข้ามารองรับการทำงานแบบนี้คือเราสามารถกำหนดรูปแบบการแสดงผลของข้อมูลที่เราต้องการได้ อย่างเช่นเปลี่ยนแปลงรูปแบบวันที่ตามที่เราต้องการในตัวอย่าง นอกจากนั้นยังทำให้ดูแลจัดการโครงสร้างข้อมูลได้ง่ายขึ้น

* แก้ไข
เนื่องจากดูโค้ดแล้วมันดูเยิ้นเย้อในส่วนของฐานข้อมูลซึ่งสามารถลดลงได้ดังนี้

<?php
$connection = null;

function __db_connection() {
  global $connection; 

  $connection = mysql_pconnect( '127.0.0.1', 'foo', 'bar' );
  if ( ! mysql_ping( $connection ) ) {
    $connection = mysql_pconnect( '127.0.0.1', 'foo', 'bar' );
  }
  
  mysql_select_db( 'my_db_name' );
  mysql_query( 'set names utf8' );
};

class SL_Foo {
  protected $_foo = array();

  function __get( $name ) {
    global $datetime_format;
    if ( in_array( $name, array( 'timestamp' ) ) )
      return date( $datetime_format, strtotime( $this->_foo[ $name ] ) ) ;

    return @$this->_foo[ $name ];
  }

  function __set( $name, $value ) {
    $this->_foo[ $name ] = $value ;
  }

}

if ( ! $connection ) __db_connection();

$sql = sprintf( "select * from foo where ID = '%s'", addslashes( $_REQUEST['id'] ) );

$results = mysql_query( $sql, $connection ) ;
if ( ! $results ) printf( 'MySQL Error: %s', mysql_error() ) ;

$foos = array();
if ( mysql_num_rows( $results ) )
  while( $foo = mysql_fetch_object( $results, 'SL_Foo' ) ) 
    $foos[] = $foo;

mysql_free_results( $results );

// Do something with $foos
echo var_dump( $foos );
  • บรรทัดที่ 8-12 รวมเป็นบรรทัดเดียวกัน เพื่อลดการเชื่อมต่อกับฐานข้อมูลตอนสั่งเปิดการเชื่อมต่อ
  • คลาส SL_Foo ลบการใส่คีย์เป็นค่าเริ่มต้น เนื่องจากคลาสใช้ Magic Methods และด้วยวิธีการเขียนทำให้จะระบุหรือไม่ก็ไม่ส่งผลกระทบกับการทำงาน