返回列表 发帖

用 PHP 走向动态5 -- 走向完全动态

删除 get_ 和 set_ 方法只是一个起点。要创建完全动态的数据库对象,必须向类提供表和字段的名称,还不能有硬编码的引用。清单 5 显示了这个变化。


清单 5. 完全动态的数据库对象类
  1. <?php
  2. require_once("DB.php");
  3. $dsn = 'mysql://root:password@localhost/bookdb';
  4. $db =& DB::Connect( $dsn, array() );
  5. if (PEAR::isError($db)) { die($db->getMessage()); }
  6. class DBObject
  7. {
  8.   private $id = 0;
  9.   private $table;
  10.   private $fields = array();
  11.   function __construct( $table, $fields )
  12.   {
  13.     $this->table = $table;
  14.     foreach( $fields as $key )
  15.       $this->fields[ $key ] = null;
  16.   }
  17.   function __call( $method, $args )
  18.   {
  19.     if ( preg_match( "/set_(.*)/", $method, $found ) )
  20.     {
  21.       if ( array_key_exists( $found[1], $this->fields ) )
  22.       {
  23.         $this->fields[ $found[1] ] = $args[0];
  24.         return true;
  25.       }
  26.     }
  27.     else if ( preg_match( "/get_(.*)/", $method, $found ) )
  28.     {
  29.       if ( array_key_exists( $found[1], $this->fields ) )
  30.       {
  31.         return $this->fields[ $found[1] ];
  32.       }
  33.     }
  34.     return false;
  35.   }
  36.   function load( $id )
  37.   {
  38.     global $db;
  39.     $res = $db->query(
  40.   "SELECT * FROM ".$this->table." WHERE ".
  41.   $this->table."_id=?",
  42.       array( $id )
  43.     );
  44.     $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
  45.     $this->id = $id;
  46.     foreach( array_keys( $row ) as $key )
  47.       $this->fields[ $key ] = $row[ $key ];
  48.   }
  49.   function insert()
  50.   {
  51.     global $db;
  52.     $fields = $this->table."_id, ";
  53.     $fields .= join( ", ", array_keys( $this->fields ) );
  54.     $inspoints = array( "0" );
  55.     foreach( array_keys( $this->fields ) as $field )
  56.       $inspoints []= "?";
  57.     $inspt = join( ", ", $inspoints );
  58. $sql = "INSERT INTO ".$this->table." ( $fields )
  59.    VALUES ( $inspt )";
  60.     $values = array();
  61.     foreach( array_keys( $this->fields ) as $field )
  62.       $values []= $this->fields[ $field ];
  63.     $sth = $db->prepare( $sql );
  64.     $db->execute( $sth, $values );
  65.     $res = $db->query( "SELECT last_insert_id()" );
  66.     $res->fetchInto( $row );
  67.     $this->id = $row[0];
  68.     return $row[0];
  69.   }
  70.   function update()
  71.   {
  72.     global $db;
  73.     $sets = array();
  74.     $values = array();
  75.     foreach( array_keys( $this->fields ) as $field )
  76.     {
  77.       $sets []= $field.'=?';
  78.       $values []= $this->fields[ $field ];
  79.     }
  80.     $set = join( ", ", $sets );
  81.     $values []= $this->id;
  82. $sql = 'UPDATE '.$this->table.' SET '.$set.
  83.   ' WHERE '.$this->table.'_id=?';
  84.     $sth = $db->prepare( $sql );
  85.     $db->execute( $sth, $values );
  86.   }
  87.   function delete()
  88.   {
  89.     global $db;
  90.     $sth = $db->prepare(
  91.    'DELETE FROM '.$this->table.' WHERE '.
  92.    $this->table.'_id=?'
  93.     );
  94.     $db->execute( $sth,
  95.       array( $this->id ) );
  96.   }
  97.   function delete_all()
  98.   {
  99.     global $db;
  100.     $sth = $db->prepare( 'DELETE FROM '.$this->table );
  101.     $db->execute( $sth );
  102.   }
  103. }
  104. $book = new DBObject( 'book', array( 'author',
  105.    'title', 'publisher' ) );
  106. $book->delete_all();
  107. $book->set_title( "PHP Hacks" );
  108. $book->set_author( "Jack Herrington" );
  109. $book->set_publisher( "O'Reilly" );
  110. $id = $book->insert();
  111. echo ( "New book id = $id\n" );
  112. $book->set_title( "Podcasting Hacks" );
  113. $book->update();
  114. $book2 = new DBObject( 'book', array( 'author',
  115.   'title', 'publisher' ) );
  116. $book2->load( $id );
  117. echo( "Title = ".$book2->get_title()."\n" );
  118. $book2->delete( );
  119. ? >
复制代码
在这里,把类的名称从 Book 改成 DBObject。然后,把构造函数修改成接受表的名称和表中字段的名称。之后,大多数变化发生在类的方法中,过去使用一些硬编码结构化查询语言(SQL),现在则必须用表和字段的名称动态地创建 SQL 字符串。

代码的惟一假设就是只有一个主键字段,而且这个字段的名称是表名加上 _id。所以,在 book 表这个示例中,有一个主键字段叫做 book_id。主键的命名标准可能不同;如果这样,需要修改代码以符合标准。

这个类比最初的 Book 类复杂得多。但是,从类的客户的角度来看,这个类用起来仍很简单。也就是说,我认为这个类能更简单。具体来说,我不愿意每次创建图书的时候都要指定表和字段的名称。如果我四处拷贝和粘贴这个代码,然后修改了 book 表的字段结构,那么我可能就麻烦了。在清单 6 中,通过创建一个继承自 DBObject 的简单 Book 类,我解决了这个问题。


清单 6. 新的 Book 类
  1. ..
  2. class Book extends DBObject
  3. {
  4.   function __construct()
  5.   {
  6.     parent::__construct( 'book',
  7.       array( 'author', 'title', 'publisher' ) );
  8.   }
  9. }
  10. $book = new Book( );
  11. $book->delete_all();
  12. $book->{'title'} = "PHP Hacks";
  13. $book->{'author'} = "Jack Herrington";
  14. $book->{'publisher'} = "O'Reilly";
  15. $id = $book->insert();
  16. echo ( "New book id = $id\n" );
  17. $book->{'title'} = "Podcasting Hacks";
  18. $book->update();
  19. $book2 = new Book( );
  20. $book2->load( $id );
  21. echo( "Title = ".$book2->{'title'}."\n" );
  22. $book2->delete( );
  23. ?>
复制代码
现在,Book 类真的是简单了。而且 Book 类的客户也不再需要知道表或字段的名称了。

返回列表