目前共有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許可協議進行許可。