今天有空再寫一篇php的數據庫模型類開發,這次的數據庫類採用的是pdo來實現的,因爲pdo有較好的適用性,所以使用pdo比mysql,mysqli都要好用。
首先就是要確定基本的思路,首先要有一個模型類model.php,然後這個模型類有一個保護成員變量,這個變量用來保存數據庫的對象,所以這裏要使用一個數據庫管理類db.php來規範管理所有的數據庫對象,這些對象都保存在一個數據中,然後就可以非常的容易切換,之後就是各個數據庫驅動mysql,mysqli等,這些驅動都要繼承最基本的驅動類dirver.php這個驅動基本類。
基本的結構如下圖:
數據庫驅動文件類,這個類封裝了基本的sql方法和變量,因爲大多數的sql都使用到這些方法,所以做一個封裝,然後讓其它類繼承。
driver類:
<?php
/**
* Created by PhpStorm.
* User: DMF
* Date: 2017/9/23
* Time: 15:59
*/
namespace Dphp\core\database;
class driver
{
//數據庫配置
protected $config = array(
'type' => 'mysql', // 數據庫類型
'hostname' => '127.0.0.1', // 服務器地址
'database' => 'test', // 數據庫名
'username' => 'root', // 用戶名
'password' => 'root', // 密碼
'hostport' => '8081', // 端口
'dsn' => 'mysql:host=localhost;dbname=test', //pdo連接信息
);
//數據庫pdo連接id 支持多個連接
protected $linkId = array();
//當前pdo連接id
protected $_linkId = null;
//pdo操作實例
protected $pdoStatement = null;
// 事務指令數
protected $transTimes = 0;
//當前sql指令
protected $querySql = '';
// 錯誤信息
protected $error = '';
//pdo連接參數
protected $options = array(
);
protected $bind = array(); // 參數綁定
//連接數據庫函數
public function connect($config=array()){
if(empty($config)){
$config = $this->config;
}
try{
$this->_linkId = new \PDO($config['dsn'],$config['username'],$config['password']);
echo "連接成功<br/>";
}catch (\PDOException $e) {
echo $e->errorInfo(),$e->getMessage();
die ("Error!: " . $e->getMessage() . "<br/>");
}
return $this->_linkId;
}
//釋放查詢結果
public function free(){
$this->pdoStatement = null;
}
//執行查詢,並返回結果集
public function query($sql,$fetchSql=false){
echo '<br/>'.$sql.'<br/>';
$this->initConnect();
//判斷是否初始化失敗
if(!$this->_linkId)return fasle;
try{
//pdo執行sql語句準備
$this->pdoStatement = $this->_linkId->prepare($sql);
}catch (\PDOException $e) {
echo $e->getMessage();
return false;
}
if($this->pdoStatement === false){
return false;
}
//數據綁定
foreach($this->bind as $k=>$v){
$this->pdoStatement->bindParam($k,$v);
}
//執行完成清空綁定數據數組
$this->bind = array();
//執行sql語句
$this->pdoStatement->execute();
//返回sql執行結果集
return $this->pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
}
//執行查詢,並返回結果集
public function execute($sql,$fetchSql=false){
echo '<br/>'.$sql.'<br/>';
$this->initConnect();
//判斷是否初始化失敗
if(!$this->_linkId)return fasle;
try{
//pdo執行sql語句準備
$this->pdoStatement = $this->_linkId->prepare($sql);
}catch (\PDOException $e) {
echo $e->getMessage();
return false;
}
if($this->pdoStatement === false){
return false;
}
//數據綁定
foreach($this->bind as $k=>$v){
$this->pdoStatement->bindParam($k,$v);
}
//執行完成清空綁定數據數組
$this->bind = array();
//執行sql語句
return $this->pdoStatement->execute();
//var_dump($this->pdoStatement->errorInfo());
}
//啓動事務
public function startTransaction(){
$this->connect();
if(!$this->_linkId)return false;
if($this->transTimes == 0){
$this->_linkId->beginTransaction();
}
$this->transTimes++;
}
//提交數據,要非自動提交
public function commit(){
if($this->transTimes>0){
$result = $this->_linkId->commit();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
//回滾數據
public function rollback(){
if($this->transTimes>0){
$result = $this->_linkId->rollback();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
//初始化連接
protected function initConnect(){
//判斷是否存在連接對象
if(!$this->_linkId)$this->_linkId = $this->connect();
}
//關閉數據庫
public function close(){
$this->_linkId = null;
}
//數據庫錯誤信息
public function error(){
if($this->pdoStatement){
$error = $this->pdoStatement->errorInfo();
$this->error = $error[1].':'.$error[2];
}else{
$this->error = '';
}
return $this->error;
}
//析構函數
public function __destruct()
{
// TODO: Implement __destruct() method.
if($this->pdoStatement){
$this->free();
}
//關閉數據庫連接
$this->close();
}
/*
* 數據綁定
* $name:名
* $value:值
*/
public function bindParam($name,$value){
$this->bind[':'.$name] = $value;
}
/*
* 插入數據
* $table:數據表
* $data:插入數據
*/
public function insert($table='',$data=array()){
if(empty($data)){
die('數據不能爲空!');
}
//字段名處理
foreach($data as $k=>$v){
$fields[] = $k;
$values[] = $v;
$this->bindParam($k,$v);
}
//insert的sql語句處理
$sql = 'INSERT INTO '.$table.' ( '.implode(',',$fields)
.') VALUES (';
foreach($fields as $k=>$v){
$sql.=':'.$v.',';
}
$sql = rtrim($sql,',');
$sql .= ')';
echo '<br/>'.$sql.'<br/>';
return $this->execute($sql);
}
/*
* 刪除數據
* $table:表名
* $data:插入數據
*/
public function delete($table='',$id=''){
$sql = 'DELETE FROM '.$table.' WHERE id='.$id;
echo $sql.'<br/>';
return $this->execute($sql);
}
/*
* 修改數據
* $table:表名
* $data:插入數據
*/
public function update($table='',$data=array()){
if(empty($data)){
die('數據不能爲空!');
}
$sql = 'UPDATE '.$table.' SET ';
//字段名處理
foreach($data as $k=>$v) {
$sql = $sql.$k.'=:'.$k.',';
$this->bindParam($k,$v);
}
$sql = rtrim($sql,',');
$sql = $sql.' WHERE id='.$data['id'];
#echo $sql.'<br/>';
return $this->execute($sql);
}
/*
* 查詢數據
* $table:表名
* $data:插入數據
*/
public function select($table='',$option=array()){
$sql = 'SELECT * FROM '.$table.' WHERE ';
foreach($option as $k=>$v){
$sql .= $k.'=:'.$k .' AND ';
}
$sql = rtrim($sql,' AND ');
echo $sql.'<br/>';
foreach($option as $k=>$v) {
$this->bindParam($k,$v);
}
return $this->query($sql);
}
}
繼承驅動類的mysqli類,這裏可以寫一些mysqli獨有的方法,方便拓展開來
mysqli類:
<?php
/**
* Created by PhpStorm.
* User: DMF
* Date: 2017/9/23
* Time: 17:12
*/
namespace Dphp\core\database\sqlDriver;
class mysqli extends \Dphp\core\database\driver
{
}
db類其實就是用來全局靜態的方法獲取數據庫實例對象,這樣做的好處是,在使用同一個數據庫對象的時候,我只要new一次就可以了。這裏防止重複new相同的數據庫,所以性能上是會有一定的提升。
db類:
<?php
/**
* Created by PhpStorm.
* User: DMF
* Date: 2017/9/23
* Time: 15:55
*/
namespace Dphp\core;
class db
{
static private $instance = array(); //數據庫連接實例
static private $_instance = null; //當前數據庫實例
//獲取數據庫實例
static public function getInstance($config=array()){
$md5 = md5(serialize($config));
//判斷是否存在數據庫實例對象
if(!isset(self::$instance[$md5])){
$class = 'Dphp\\core\\database\\sqlDriver\\mysqli';
self::$instance[$md5] = new $class();
echo 'new了一個數據庫對象<br/>';
}
else {
echo '緩存一個數據庫對象<br/>';
}
return self::$instance[$md5];
}
}
直接提供操作數據庫的接口方法。
model類:
<?php
/**
* Created by PhpStorm.
* User: DMF
* Date: 2017/9/25
* Time: 13:12
*/
namespace Dphp\core;
class model
{
//數據庫對象
protected $db;
//初始化數據庫對象
public function __construct($name='')
{
$this->db();
}
//數據庫對象實例化
public function db(){
if($this->db)return $this->db;
//獲取數據庫對象
$this->db = \Dphp\core\db::getInstance();
return $this->db;
}
/*
* 數據添加
* $table:表名
* $data:添加的數據
*/
public function add($table='',$data=array()){
if(empty($table)||empty($data)){
die('數據表爲空或數據爲空');
}
return $this->db->insert($table,$data);
//var_dump($data2);
}
/*
* 數據更新
* $table:表名
* $data:添加的數據
*/
public function update($table='',$data=array()){
if(empty($table)||empty($data)){
die('數據表爲空或數據爲空');
}
return $this->db->update($table,$data);
//var_dump($data2);
}
/*
* 數據刪除
* $table:表名
* $data:添加的數據
*/
public function delete($table='',$id=null){
if(empty($table)||empty($id)){
die('數據表爲空或數據爲空');
}
return $this->db->delete($table,$id);
//var_dump($data2);
}
/*
* 數據查找
* $table:表名
* $data:添加的數據
*/
public function select($table='',$option=array()){
if(empty($table)||empty($option)){
die('數據表爲空或數據爲空');
}
return $this->db->select($table,$option);
//var_dump($data2);
}
}
2017.10.31
在上面的數據庫寫法上,其實存在着一個bug和不方便的地方。
這裏面我的數據庫driver裏面沒寫人性化,因爲query的時候沒用給你綁定數據的參數傳入。所以更改後應該是這樣子的。
<?php
/**
* Created by PhpStorm.
* User: DMF
* Date: 2017/9/23
* Time: 15:59
*/
namespace core\database;
class driver
{
//數據庫配置
protected $config = array(
'type' => 'mysql', // 數據庫類型
'hostname' => '127.0.0.1', // 服務器地址
'database' => '123', // 數據庫名
'username' => 'root', // 用戶名
'password' => 'root', // 密碼
'hostport' => '3306', // 端口
'dsn' => 'mysql:host=localhost;dbname=automobileRepairStation', //pdo連接信息
);
//數據庫pdo連接id 支持多個連接
protected $linkId = array();
//當前pdo連接id
protected $_linkId = null;
//pdo操作實例
protected $pdoStatement = null;
// 事務指令數
protected $transTimes = 0;
//當前sql指令
protected $querySql = '';
// 錯誤信息
protected $error = '';
//pdo連接參數
protected $options = array(
);
protected $bind = array(); // 參數綁定
//連接數據庫函數
public function connect($config=array()){
if(empty($config)){
$config = $this->config;
}
try{
$this->_linkId = new \PDO($config['dsn'],$config['username'],$config['password']);
#echo "連接成功<br/>";
}catch (\PDOException $e) {
#echo $e->errorInfo(),$e->getMessage();
die ("Error!: " . $e->getMessage() . "<br/>");
}
return $this->_linkId;
}
//釋放查詢結果
public function free(){
$this->pdoStatement = null;
}
//啓動事務
public function startTransaction(){
$this->connect();
if(!$this->_linkId)return false;
if($this->transTimes == 0){
$this->_linkId->beginTransaction();
}
$this->transTimes++;
}
//提交數據,要非自動提交
public function commit(){
if($this->transTimes>0){
$result = $this->_linkId->commit();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
//回滾數據
public function rollback(){
if($this->transTimes>0){
$result = $this->_linkId->rollback();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}
//初始化連接
protected function initConnect(){
//判斷是否存在連接對象
if(!$this->_linkId)$this->_linkId = $this->connect();
}
//關閉數據庫
public function close(){
$this->_linkId = null;
}
//數據庫錯誤信息
public function error(){
if($this->pdoStatement){
$error = $this->pdoStatement->errorInfo();
$this->error = $error[1].':'.$error[2];
}else{
$this->error = '';
}
return $this->error;
}
//析構函數
public function __destruct()
{
// TODO: Implement __destruct() method.
if($this->pdoStatement){
$this->free();
}
//關閉數據庫連接
$this->close();
}
//執行查詢,並返回結果集
public function query($sql,$fetchSql=false){
#echo '<br/>'.$sql.'<br/>';
$this->initConnect();
//判斷是否初始化失敗
if(!$this->_linkId)return fasle;
try{
//pdo執行sql語句準備
$this->pdoStatement = $this->_linkId->prepare($sql);
}catch (\PDOException $e) {
echo $e->getMessage();
return false;
}
return $this;
}
/*
* 數據綁定
* $name:名
* $value:值
*/
public function bindParam($name,$value){
$this->bind[':'.$name] = $value;
}
/*
* 數據綁定
* $params:鍵值對參數數組
*/
public function bind($params=null){
if(is_array($params)){
foreach($params as $k=>$v){
$this->bindParam($k,$v);
}
}
return $this;
}
/*
* 執行並返回數據
*/
public function fetch(){
if(!empty($this->bind)){
//數據綁定
foreach($this->bind as $k=>&$v){
$this->pdoStatement->bindParam($k,$v);
}
}
//執行完成清空綁定數據數組
$this->bind = array();
//執行sql語句
$this->pdoStatement->execute();
//返回sql執行結果集
return $this->pdoStatement->fetch(\PDO::FETCH_ASSOC);
}
/*
* 執行並返回數據集
*/
public function fetchAll(){
if(!empty($this->bind)){
//數據綁定
foreach($this->bind as $k=>&$v){
$this->pdoStatement->bindParam($k,$v);
}
}
//執行完成清空綁定數據數組
$this->bind = array();
//執行sql語句
$this->pdoStatement->execute();
//返回sql執行結果集
return $this->pdoStatement->fetchAll(\PDO::FETCH_ASSOC);
}
//執行查詢
public function execute(){
//數據綁定
foreach($this->bind as $k=>&$v){
$this->pdoStatement->bindParam($k,$v);
}
//執行sql語句
$flag = $this->pdoStatement->execute();
//執行完成清空綁定數據數組
$this->bind = array();
if($flag){
return true;
}
return false;
}
/*
* 插入數據
* $table:數據表
* $data:插入數據
*/
public function insert($table='',$data=array()){
if(empty($data)){
die('數據不能爲空!');
}
//字段名處理
foreach($data as $k=>$v){
$fields[] = $k;
$values[] = $v;
$this->bindParam($k,$v);
}
//insert的sql語句處理
$sql = 'INSERT INTO '.$table.' ( '.implode(',',$fields)
.') VALUES (';
foreach($fields as $k=>$v){
$sql.=':'.$v.',';
}
$sql = rtrim($sql,',');
$sql .= ')';
echo '<br/>'.$sql.'<br/>';
return $this->execute($sql);
}
/*
* 刪除數據
* $table:表名
* $data:插入數據
*/
public function delete($table='',$id=''){
$sql = 'DELETE FROM '.$table.' WHERE id='.$id;
echo $sql.'<br/>';
return $this->execute($sql);
}
/*
* 修改數據
* $table:表名
* $data:插入數據
*/
public function update($table='',$data=array()){
if(empty($data)){
die('數據不能爲空!');
}
$sql = 'UPDATE '.$table.' SET ';
//字段名處理
foreach($data as $k=>$v) {
$sql = $sql.$k.'=:'.$k.',';
$this->bindParam($k,$v);
}
$sql = rtrim($sql,',');
$sql = $sql.' WHERE id='.$data['id'];
#echo $sql.'<br/>';
return $this->execute($sql);
}
/*
* 查詢數據
* $table:表名
* $data:插入數據
*/
public function select($table='',$option=array()){
$sql = 'SELECT * FROM '.$table.' WHERE ';
foreach($option as $k=>$v){
$sql .= $k.'=:'.$k .' AND ';
}
$sql = rtrim($sql,' AND ');
echo $sql.'<br/>';
foreach($option as $k=>$v) {
$this->bindParam($k,$v);
}
return $this->query($sql);
}
}
仔細觀察的話,我是將裏面的方法細化了,加多了bind方法,fetch方法,fetchAll方法,execute方法。這樣子就能夠將query方法獲取到pdoStatement,後再綁定數據到全局變量bind裏面,然後再執行返回什麼方法的結果集。
在使用bind的方法中,肯定會使用到bindParam的方法,這裏面存在一個坑點,那就是bindParam(鍵,&值)的形式來進行綁定值的,所以當你使用foreach($values as $k=>$v)的時候,就會出現一直都是引用$v這個變量的值,所以一直都是最後面的獲取到的值。所以這裏要採用foreach($values as $k=>&$v)或是binValue這個函數。