目前共有10篇帖子。
【示例】PHP用面向對象的方法訪問數據表
1樓 巨大八爪鱼 2016-2-18 18:31
設數據表Users:


則在PHP中可以定義一個User類和基類DataObject,使得訪問數據表可以用下面的方法:
<?php
include_once('conn.php');
include_once('DataObject.php');
include_once('User.php');

// 添加一個新用戶
$user = new User();
$user->name = 'oct1158';
$user->password = '789012';
$user->useFunction('birthday', 'NOW()');
echo 'Field birthday uses MySQL Function: ', $user->birthday, '<br>';
if ($user->insert()) {
    echo 'New User ID: ', $user->id, '<br>';
   
    // 更新用戶信息
    $user->password = '112233';
    $user->update();
} else {
    echo 'INSERT Failed<br>';
}

// 獲取一個用戶
$sql = 'SELECT * FROM Users WHERE UserName = ?';
$stmt = $dbh->prepare($sql);
$stmt->execute(array('admin'));
$admin_user = $stmt->fetchObject('User');
echo 'Admin ID is ', $admin_user->id, '.<br>';
echo 'Admin Birthday is ', $admin_user->birthday, '.<br>';

$users = User::GetAll();
echo '<br>';
echo $users[0]->name, ', ', $users[0]->birthday, '<br>';
echo $users[1]->name, ', ', $users[1]->birthday, '<br>';
echo $users[2]->name, ', ', $users[2]->birthday, '<br>';
echo '<br>';

// 刪除一個用戶
$user = new User();
$user->insert();
$user->delete();
2樓 巨大八爪鱼 2016-2-18 18:32
【User類的定義】
<?php
class User extends DataObject {
    // 設置PHP類對應的數據表的名稱,以及數據表裡所有主鍵的字段名稱
    // auto為AUTO_INCREMENT
    protected static $_table = array('name' => 'Users', 'key' => 'UserID', 'auto' => 'UserID');
    // PHP類屬性名與數據表字段名的對應關係
    protected static $_propertyList = array('id' => 'UserID', 'name' => 'UserName', 'password' => 'UserPassword', 'birthday' => 'UserBirthday');
   
    public static function GetAll() {
        global $dbh;
        $sql = 'SELECT * FROM Users';
        $stmt = $dbh->query($sql);
        $users = array();
        while ($user = $stmt->fetchObject(__CLASS__)) {
            $users[] = $user;
        }
        return $users;
    }
}
3樓 巨大八爪鱼 2016-2-18 18:32
【基類DataObject】
<?php
class DataObject {
    private $changedFields = array(); // 更新了的字段列表
    private $data = array(); // 從PDO取得的一條記錄
    private $funcFields = array(); // 使用函數的字段列表
   
    // 以上幾個字段都是本類私有的,因此子類即便是定義同名的屬性
    // 或者將同名屬性關聯到數據表的某個字段中,也絲毫不會影響本類的這些變量
   
    function __get($property) {
        if (isset($this::$_propertyList[$property])) {
            return $this->data[$this::$_propertyList[$property]]; // 根據PHP屬性名直接訪問字段
        } else {
            return $this->$property; // 如果沒有對應的屬性,則顯示PHP默認的錯誤信息
        }
    }
   
    function __set($property, $value) {
        if (isset($this::$_propertyList[$property])) {
            $field = $this::$_propertyList[$property];
            $this->data[$field] = $value; // 更新$_data[字段名]的值
           
            // 記錄更新了的字段
            if (!in_array($field, $this->changedFields)) {
                array_push($this->changedFields, $field);
            }
            $index = array_search($field, $this->funcFields);
            if ($index !== false) {
                unset($this->funcFields[$index]);
                $this->funcFields = array_values($this->funcFields);
            }   
        } else {
            $this->data[$property] = $value; // 將不存在的屬性全部統一存入$_data
        }
    }
   
    private function checkPrimaryKey() {
        if (is_string($this::$_table['key'])) {
            $this::$_table['key'] = array($this::$_table['key']);
        }
    }
   
    private function clear() {
        $this->changedFields = array();
        $this->funcFields = array();
    }
   
    public function delete() {
        global $dbh;
        if (empty($this::$_table['key'])) {
            return false;
        }
        $sql = 'DELETE FROM ' . $this::$_table['name'] . ' WHERE ';
        $this->checkPrimaryKey();
        $sql .= join(' = ? AND ', $this::$_table['key']) . ' = ?';
        $stmt = $dbh->prepare($sql);
        $param = 1;
        foreach ($this::$_table['key'] as $key) {
            $stmt->bindValue($param++, $this->data[$key]);
        }
        $rs = $stmt->execute();
        if ($rs) {
            $this->changedFields = array();
        }
        return $rs;
    }
   
    public function insert() {
        global $dbh;
        if (empty($this->changedFields)) {
            $stmt = $dbh->prepare('INSERT INTO ' . $this::$_table['name'] . ' VALUES ()');
            $rs = $stmt->execute();
        } else {
            $fields = join(', ', $this->changedFields);
            $placeholders = array();
            foreach ($this->changedFields as $field) {
                if (in_array($field, $this->funcFields)) {
                    $placeholders[] = $this->data[$field];
                } else {
                    $placeholders[] = '?';
                }
            }
            $placeholders = join(', ', $placeholders);
            $sql = 'INSERT INTO ' . $this::$_table['name'] . " ($fields) VALUES ($placeholders)";
            $stmt = $dbh->prepare($sql);
            $param = 1;
            foreach ($this->changedFields as $field) {
                if (!in_array($field, $this->funcFields)) {
                    $stmt->bindValue($param++, $this->data[$field]);
                }
            }
            $rs = $stmt->execute();
        }
        if ($rs) {
            $this->clear();
            if (isset($this::$_table['auto'])) {
                $this->data[$this::$_table['auto']] = $dbh->lastInsertId();
            }
        }
        return $rs;
    }
   
    public function update() {
        global $dbh;
        if (empty($this->changedFields) || empty($this::$_table['key'])) {
            return false;
        }
       
        // 禁止直接更新主鍵字段,如果必須要更新主鍵字段,請手動執行SQL語句
        $this->checkPrimaryKey();
        if (array_intersect($this->changedFields, $this::$_table['key'])) {
            trigger_error("Forbidden to update the primary key of a row", E_USER_WARNING);
            return false;
        }
       
        $sql = 'UPDATE ' . $this::$_table['name'] . ' SET ';
        $list = array();
        foreach ($this->changedFields as $field) {
            if (in_array($field, $this->funcFields)) {
                $list[] = "$field = " . $this->data[$field];
            } else {
                $list[] = "$field = ?";
            }
        }
        $sql .= join(', ', $list);
        $sql .= ' WHERE ' . join(' = ? AND ', $this::$_table['key']) . ' = ?';
       
        $stmt = $dbh->prepare($sql);
        $param = 1;
        foreach ($this->changedFields as $field) {
            if (!in_array($field, $this->funcFields)) {
                $stmt->bindValue($param++, $this->data[$field]);
            }
        }
        foreach ($this::$_table['key'] as $key) {
            $stmt->bindValue($param++, $this->data[$key]);
        }
        $rs = $stmt->execute();
        if ($rs) {
            $this->clear();
        }
        return $rs;
    }
   
    public function useFunction($property, $function) {
        if (!isset($this::$_propertyList[$property])) {
            return false;
        }
        $field = $this::$_propertyList[$property];
        $this->checkPrimaryKey();
        if (in_array($field, $this::$_table['key'])) {
            return false;
        }
        if (!in_array($field, $this->funcFields)) {
            array_push($this->changedFields, $field);
            array_push($this->funcFields, $field);
        }
        $this->data[$field] = $function;
        return true;
    }
}
4樓 巨大八爪鱼 2016-2-18 18:32
【頁面輸出】
Field birthday uses MySQL Function: NOW()
INSERT Failed
Admin ID is 1.
Admin Birthday is 2016-02-18.

admin, 2016-02-18
oct1158, 2016-02-18
fifi, 1997-06-15


5樓 巨大八爪鱼 2016-2-18 19:06
【原理】
在PHP自帶的PDOStatement類中有一個以對象方式獲取一條記錄的方法:FetchObject方法,其中參數指定類名,如果失敗則返回NULL。例如執行$obj = $stmt->fetchObject(『User』),那麼返回的就是一個User類的對象,本來該對象有下列屬性:$obj->UserID,$obj->UserName,$obj->UserPassword,$user->UserBirthDay。但是由於User類的父類DataObject有一個魔術方法_set(),該方法導致了執行fetchObject時這些屬性都被重定向到了父類私有數組DataObject::$data中,例如$data[『UserPassword』]。
子類User的靜態屬性$_propertyList指定了PHP屬性名與數據表字段名的對應關係。在這個例子中,訪問$obj->id就被魔術方法_get()重定向到訪問$obj->data[『UserID』]。
如果執行$obj->id = 10,那麼就會被_set()重定向,相當於執行$obj->data[『UserID』] = 10,並且此時_set()自動記錄字段名UserID到changedFields數組中,這樣insert()和update()方法就能知道需要更新哪些字段。
另外,執行fetchObject的時候,fetchObject內部執行的$obj->UserID = 10;被_set()重定向到$obj->data[『UserID』] = 10;,這個時候_set()是不會在changedFields數組中記錄相應的字段名的,見_set()方法的最外面的else部分。
子類的靜態屬性$_table指定了該PHP類對應哪一張MySQL數據表,以及該表的主鍵字段,以及AUTO_INCREMENT字段。如果主鍵由多個字段組成,參數』key』可以使用數組。如果沒有AUTO_INCREMENT字段,請不要寫出』auto』參數。
6樓 巨大八爪鱼 2016-2-18 19:09
在DataObject類及其派生類中,除了訪問$obj->屬性名時程序需要查閱$_propertyList數組進行重定向外,其餘時候都是直接用的MySQL字段名而非屬性名,這樣可以減少頻繁的array_flip和array_search,大幅度提高性能。
7樓 巨大八爪鱼 2016-2-18 19:20
在User類中,還可以定義一些與用戶有關的方法:
    public static function GetUserByName($name) {} // 通過用戶名獲取User對象
    public static function GetUserByID($id) {} // 通過用戶ID獲取User對象
   
    public function checkPassword($password) {} // 判斷此用戶的密碼是否與輸入的密碼匹配
    public function showLink() { // 顯示此用戶的鏈接
        return "<a href=\"user.php?i={$this->id}\">{$this->name}</a>";
    }
8樓 巨大八爪鱼 2016-2-23 12:38
【數據表分頁類示例】
<?php
class DataPage {
    public $sql;
    public $order = '';
    public $page;
    public $pageSize = 30;
   
    public $linkClickEvent = '';
    public $urlTemplate = '?p=%d';
   
    private $_pageCount;
    private $_rowCount;
    private $_rowStart;
    private $_stmt;
   
    function __get($property) {
        return $this->{'_' . $property};
    }
   
    public function execute($params = NULL) {
        global $dbh;
        $stmt = $dbh->prepare($this->sql);
        $stmt->execute($params);
        $this->_rowCount = $stmt->rowCount();
        unset($stmt);
       
        $this->_pageCount = ceil($this->_rowCount / $this->pageSize);
        if ($this->_pageCount < 1) {
            $this->_pageCount = 1;
        }
        if ($this->page < 1) {
            $this->page = 1;
        } else if ($this->page > $this->_pageCount) {
            $this->page = $this->_pageCount;
        }
        $this->_rowStart = ($this->page - 1) * $this->pageSize;
       
        $sql = sprintf('%s %s LIMIT %d, %d', $this->sql, $this->order, $this->_rowStart, $this->pageSize);
        $this->_stmt = $dbh->prepare($sql);
        $this->_stmt->execute($params);
    }
   
    public function inputPage($variable = 'p', $input_type = INPUT_GET) {
        $this->page = intval(filter_input($input_type, $variable, FILTER_SANITIZE_NUMBER_INT));
    }
   
    public function showLinks() {
        $html = array();
        $html[] = $this->showPageLink(1, _('First'));
        if ($this->page == 1) {
            $html[] = htmlspecialchars(_('<Prev'));
        } else {
            $html[] = $this->showPageLink($this->page - 1, _('<Prev'));
        }
        if ($this->page == $this->_pageCount) {
            $html[] = htmlspecialchars(_('Next>'));
        } else {
            $html[] = $this->showPageLink($this->page + 1, _('Next>'));
        }
        $html[] =  $this->showPageLink($this->_pageCount, _('Last'));
        return join(' ', $html);
    }
   
    public function showPageLink($page, $text = NULL) {
        if (is_null($text)) {
            $text = $page;
        }
        if (empty($this->linkClickEvent)) {
            $html = '<a href="';
            $html .= htmlspecialchars(sprintf($this->urlTemplate, $page));
            $html .= '">' . htmlspecialchars($text) . '</a>';
        } else {
            $html = '<a href="#" onclick="';
            $html .= htmlspecialchars(sprintf($this->linkClickEvent, $page));
            $html .= ';return false">' . htmlspecialchars($text) . '</a>';
        }
        return $html;
    }
}
9樓 巨大八爪鱼 2016-2-23 12:39
【使用數據表分頁類分頁顯示數據】
<?php
include_once('conn2.php');
include_once('DataObject.php');
include_once('DataPage.php');

class Entry extends DataObject {
    protected static $_table = array('name' => 'wareEntry', 'key' => 'id', 'auto' => 'id');
    protected static $_propertyList = array('id' => 'id', 'name' => 'name', 'imageURL' => 'imgUrl', 'price' => 'price', 'type' => 'type_id');
   
    public function showType() {
        global $dbh;
        $sql = 'SELECT name FROM wareType WHERE id = ?';
        $stmt = $dbh->prepare($sql);
        $stmt->execute(array($this->type));
        return $stmt->fetchColumn();
    }
}

$p = new DataPage();
$p->sql = 'SELECT * FROM wareEntry WHERE id > ?';
$p->order = 'ORDER BY id DESC';
$p->pageSize = 6;
$p->inputPage();
$p->execute(array(5));
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Pages</title>
</head>

<body>
<table border="1" cellpadding="3">
  <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Image</th>
    <th>Price</th>
    <th>Type</th>
  </tr>
<?php while ($entry = $p->stmt->fetchObject('Entry')): ?>
  <tr>
    <td><?=$entry->id?></td>
    <td><?=$entry->name?></td>
    <td><?=$entry->imageURL?></td>
    <td><?=$entry->price?></td>
    <td><?=$entry->showType()?></td>
  </tr>
<?php
endwhile;

$p->urlTemplate = "?p=%1\$d&msg=THIS_IS_PAGE_%1\$d";
//$p->linkClickEvent = "alert('This is page %d')"; // For Ajax
?>
  <tr>
    <td align="center" colspan="5"><?=$p->showLinks()?></td>
  </tr>
  <tr>
    <td colspan="5">There are <?=$p->pageCount?> pages.</td>
  </tr>
</table>
</body>
</html>
10樓 巨大八爪鱼 2016-2-23 12:39
運行效果:

生成的分頁鏈接示例:
testpage.php?p=2&msg=THIS_IS_PAGE_2

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
 
 
©2010-2024 Arslanbar [手機版] [桌面版]
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。