2011-07-18 25 views
1

假設我有一個數據庫架構這樣的功能:編寫用於SQL結果行轉換爲對象

http://img98.imageshack.us/img98/786/samplequ.png

我用下面的SQL從客戶表中選擇所有行,也所有相關行:

SELECT c.*, 
cty.id AS country__id, cty.name AS country__name, 
o.id AS order__id, o.date AS order__date, o.total AS order__total, 
ol.id AS order__orderline__id, ol.quantity AS order__orderline__quantity, ol.sub_total AS order__orderline__sub_total, 
p.id AS order__orderline__product__id, p.name AS order__orderline__product__name, p.price AS order__orderline__product__price, 
s.id AS order__shop__id, s.name AS order__shop__name 
FROM customer c 
JOIN country cty ON cty.id=c.country_id 
JOIN order o ON o.customer_id=c.id 
JOIN shop s ON s.id=o.shop_id 
JOIN orderline ol ON ol.order_id=o.id 
JOIN product p ON ol.product_id=p.id 
ORDER BY 
c.last_name ASC, 
c.first_name ASC, 
o.date DESC, 
ol.id ASC 

你可以看到,儘管從客戶表中的列,所有其他列使用,反映了它的別名可用於構建對象的關係。別名中的雙下劃線用於標識關係層次結構。

我想編寫一個PHP函數,它接受結果行並返回一個數組Customer對象。這些客戶對象應加載與其他相關對象:

$customers=convert_result_rows_to_objects($result_rows,'Customer'); 
foreach($customers as $cust){ 
echo $cust->id; 
echo $cust->get_full_name(); 
echo $cust->country->name; 
foreach($cust->orders as $o){ 
    echo $o->id; 
    echo $o->date; 
    echo $o->shop->name; 
    foreach($o->orderlines as $ol){ 
    echo $ol->product->name 
    echo $ol->quantity; 
    echo $ol->sub_total; 
    } 
    echo $o->total; 
} 
} 

我已經寫爲每個數據庫表的模型,例如:

class Customer{ 
public function get_full_name(){...} 
} 

爲了簡單起見,我忽略了getter和setter適用於所有其他領域。

但我該怎麼寫convert_result_rows_to_objects函數?

我不想使用ORM。

我在我的大腦已經搞到現在,函數應該是這樣的,

function convert_result_rows_to_objects($result_rows, $main_class_name){ 

    $main_objs = array(); //containing the main objects to be returned, customer objects in my example 
    $main_obj = NULL; 
    $previous_row = NULL; 
    $next_row = NULL; 

    for($i = 0; $i<count($result_rows); $i++){ 

     $previous_row = ($i > 0 ? ($result_rows[$i - 1]) : NULL); 
     $this_row = $result_rows[$i]; 
     $next_row = $i === (count($result_rows) - 1) ? NULL : ($result_rows[$i + 1]); 

     if ($previous_row === NULL || $previous_row->id !== $this_row->id) { 
      $main_obj = new $main_class_name(); //create the main object 

     //what should be done next? 

    } 
} 

回答

2

編輯:

對不起,你說你已經有模型我沒有注意。首先,填充需要識別字段的對象(當然)。由於您有前綴,因此您可以使用strstrstrpos來搜索對象標識(每個標識),並使用substr獲取字段的名稱。然後你準備好填充對象。如果你想要它是真正通用的,你需要通過雙下劃線來分割字段並動態地創建對象。

如果您可以顯示返回的數據的示例,那將會很好。

+0

我可以,但我想寫一個泛型函數,可以在大多數情況下使用。 – bobo

+0

您想要一個通用函數,但您的查詢不是通用的。那麼有什麼意義呢? – fromvega

+0

看來你確實需要一個ORM – fromvega

3

編輯:我沒有在原始文章中指出的一點是我使用視圖將所有相關的列放在一起。示例代碼不會突出顯示它,但如果您創建的視圖將所需的所有內容集合在一起,則可以基於此創建模型,並且查詢以及模型將非常簡單。 /編輯

這並不難。你需要確保你的模型類看起來像你的數據庫行,否則你將不得不做手動映射。我有一個基類,我的數據庫模型,如下所示:

class Ashurex_Model 
{ 
    public function __construct($args = null) 
    { 
     if(is_array($args)) 
     { 
      $this->setOptions($args); 
     } 
    } 

    // Magic setter changes user_id to setUserId 
    public function __set($name, $value) 
    { 
     $method = 'set' . Ashurex_Utilities::underscoreToCamelCase($name); 
     if (method_exists($this, $method)) 
     { 
      $this->$method($value); 
     } 
    } 

    // Magic getter changes user_id to getUserId 
    public function __get($name) 
    { 
     $method = 'get' . Ashurex_Utilities::underscoreToCamelCase($name); 
     if (method_exists($this, $method)) 
     { 
      return $this->$method(); 
     } 
    } 

    public function __call($name, $args) 
    { 
     if (method_exists($this, $name)) 
     { 
      return call_user_func_array(array($this, $name), $args); 
     } 
    } 

    // Used for initializing an object off the database row 
    // transforms all the row names (like user_id to UserId) 
    // from underscores to camel case 
    protected function setOptions(array $options) 
    { 
     foreach($options as $key => $value) 
     { 
      $this->__set($key,$value); 
     } 
     return $this; 
    } 
} 

一個例子用戶類是這樣的:

class Ashurex_Model_User extends Ashurex_Model 
{ 
    protected $_id; 
    protected $_username; 
    protected $_userpass; 
    protected $_firstName; 

    { ... } 

    public function getId(){ return $this->_id; } 
    public function getUsername(){ return $this->_username; } 
    public function getUserpass(){ return $this->_userpass; } 
    public function getFirstName(){ return $this->_firstName; } 

    { ... } 

    public function setId($id){ $this->_id = $id; } 
    public function setUsername($username){ $this->_username = $username; } 
    public function setUserpass($password){ $this->_userpass = $password; } 
    public function setFirstName($firstName){ $this->_firstName = $firstName; } 

    { ... } 

    // This function will help when automatically saving the object back to the database 
    // The array keys must be named exactly what the database columns are called 
    public function toArray() 
    { 
     $data = array(
      'id'     => $this->getId(), 
      'username'    => $this->getUsername(), 
      'userpass'    => $this->getUserpass(), 
      'first_name'   => $this->getFirstName(), 
      { ... } 
     ); 

     return $data; 
    } 
} 

數據庫表如下所示:

CREATE TABLE IF NOT EXISTS `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `username` varchar(64) NOT NULL, 
    `userpass` varchar(160) NOT NULL, 
    `first_name` varchar(64) NOT NULL, 
    `last_name` varchar(64) NOT NULL, 
    `email` varchar(64) NOT NULL, 
    `role_id` tinyint(4) NOT NULL, 
    `is_active` tinyint(1) NOT NULL DEFAULT '1', 
    `force_password_change` tinyint(1) NOT NULL DEFAULT '0', 
    `creation_datetime` datetime NOT NULL, 
    `updated_datetime` datetime NOT NULL, 
    `user_salt` varchar(32) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `username` (`username`), 
    KEY `role_id` (`role_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

正如您所看到的,最重要的方面是您可以以適當的方式命名您的列,以便您的基礎模型類可以「自動」將列名映射到方法。

最後,這就是保存代碼看起來像...

public function save(Ashurex_Model $obj) 
    { 
     try 
     { 
      $data = $obj->toArray(); 
        // If no id is set, we are inserting a new row 
      if(null === ($id = $data['id'])) 
      { 
       unset($data['id']); 
       $this->getDbTable()->insert($data); 
       return $this->getDb()->lastInsertId(); 
      } 
      else 
      { 
          // We have an Id, do an update 
       $where = $this->getDbTable()->getAdapter()->quoteInto('id = ?',array($id)); 
       $this->getDbTable()->update($data,$where); 
       return $id; 
      } 
     } 
     catch(Exception $e) 
     { 
      self::logException(__METHOD__,$e); 
     } 
     return false; 
    } 

示例中找到的代碼看起來是這樣的,你可以看到它從數據庫中結果行根據列權初始化新對象名稱:

public function find($id) 
    { 
     try 
     { 
      $table = $this->getDbView(); 
      $stmt = $table->select() 
         ->where('id = ?') 
         ->bind(array($id)); 

      $row = $table->fetchRow($stmt); 
      if(!is_null($row)) 
      { 
       $r = $row->toArray(); 
       $obj = new Ashurex_Model_User($r); 
       return $obj; 
      } 
      else 
      { 
       return null; 
      } 
     } 
     catch(Exception $ex) 
     { 
      self::logException(__METHOD__,$ex); 
      return null; 
     } 
    } 
1

敢肯定你想要的信息可以在這裏找到: http://php.net/manual/en/book.reflection.php

我想日在您處理所有邊界情況時,在將數據庫列映射到getter/setter時避免加載整個數據庫等等,您最終會編寫一個ORM。