#### 數據庫操作類
在教程開頭我說過,我最喜歡的就是TP中的數據庫連貫操作,寫起來非常爽,所以在這個框架里,我也自己封裝了一個數據庫操作類。
下面開始寫寫我的思路:
1.單例模式:問了不浪費資源,我認為需要用單例模式
2.在構造函數中創建PDO對象:這個類使用的是PDO連接,現在的PHP并不建議像以前那樣是用mysqli模塊來操作數據庫,建議使用PDO操作,PDO可以有效的防止注入,增加數據庫的安全性。
3.連貫操作的核心就是在每個中間函數(如where函數、limit函數)中,最后返回當前對象``$this``
廢話不多說,老規矩,先給出所有代碼,然后再慢慢細說,開始寫正題:
~~~
<?php
namespace S;
class Model{
protected static $db;
private static $model;
private static $tableName;
private $table_prefix;
private $sql;
private $sql_where= '';
private $sql_limit = '';
private $sql_insert = '';
private $sql_create_table;
private $sql_save = '';
private function init(){
$this->sql_where='';
$this->sql = '';
$this->sql_limit = '';
$this->sql_insert='';
$this->sql_create_table='';
$this->sql_save='';
}
/**
* @intro 單例模式 ,獲取唯一的對象
* @param $table_name
* @return Model
*/
public static function getInstance($table_name){
self::$tableName = $table_name;
if (!self::$model instanceof self){
self::$model = new self();
}
return self::$model;
}
/**
* Model constructor. 實例化本對象,讀取配置文件中數據庫的配置,并實例化pdo對象,返回本對象
* @throws S_Exception
*/
private function __construct(){
$dsn = C('database');
$this->table_prefix = $dsn['db_prefix'];
// new PDO('mysql:host=localhost;dbname=bocishangai', 'root', '815581420shenC');
try{
self::$db = new \PDO('mysql:host='.$dsn['db_host'] . ';dbname=' . $dsn['db_name'] . ';charset='. $dsn['db_charset'],$dsn['db_user'],$dsn['db_password']);
}catch (S_Exception $e){
throw new S_Exception('數據庫連接出現錯誤');
}
return $this;
}
private function __clone(){
}
/**
* @intor 查詢函數,要么不傳參,要么只能傳入一個數組,函數拼接sql語句,進行查詢
* @param null $parm 傳入的參數,傳入要查詢的列
* @return array 返回查詢結果(數組形式)
* @throws S_Exception
*/
public function select($parm = null){
//$parm 要么不傳,要么只能傳入一個數組
if (is_array($parm)){
$sqli = rtrim($this->mutliArr($parm),','); //把傳入的數組拼接成字符串
$this->sql = 'select ' . $sqli . ' from ' .$this->table_prefix . self::$tableName . $this->sql_where ;//拼接sql語句
}else{
if (!is_null($parm)){
throw new S_Exception( __METHOD__ . '傳入的參數錯誤!');
}
$this->sql = "select * from " . $this->table_prefix . self::$tableName . $this->sql_where . $this->sql_limit; //不是數組的話,就查詢所有列
}
$res = self::$db->query($this->sql);
$res->setFetchMode(\PDO::FETCH_ASSOC);
$arr = [];
foreach ($res as $row){
$arr[] = $row;
}
$this->init(); //由于是單例模式,每次執行完sql語句,要將原本的所有的變量都清空,防止多次執行時出錯
if (empty($arr)){
return false;
}
return $arr;
}
/**
* 把數組連接成字符串
* @param $array 傳入的數組
* @return string 返回生成的字符串
*/
public function mutliArr($array){
$sqli = '';
foreach ($array as $v){
$sqli .= $v . ',';
}
return $sqli;
}
/**
* @intro where函數,把傳進來的參數拼接成where字符串,并賦值給私有變量$sql_where ,然后返回本對象,實現聯動執行方法
* @param null $parm 傳入的條件查詢參數數組
* @return $this 返回本對象
* @throws S_Exception
*/
public function where($parm = null){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數錯誤!');
}else{
$this->sql_where = ' where ' . rtrim(trim($this->multiWhere($parm)),'and');
}
return $this;
}
/**
* @intro 把傳入的數組拼接成where字符串并返回
* @param $parm 傳入的數組
* @return string 返回拼接的字符串
* @throws S_Exception
*/
public function multiWhere($parm){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數錯誤!');
}
$where_prepare = '';
foreach ($parm as $k => $value) {
if (is_array($value)){
$where_prepare .=' '. $k . ' ' . $value[0] . $value[1] . ' and';
}else{
$where_prepare .= ' ' .$k . ' = '.'\'' . $value.'\'' . ' and';
}
}
return $where_prepare;
}
/**
* @intro 拼接limit語句,并返回本對象
* @param $first
* @param null $second
* @return $this
*/
public function limit($first, $second = null){
if (is_null($second)){
$this->sql_limit = ' limit ' . $first ;
}else{
$this->sql_limit = ' limit ' . $first . ',' . $second;
}
return $this;
}
public function add($parm = null){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數不正確!');
}
$sql_in = rtrim(trim($this->multiInsert($parm)),',');
$arr_in = $this->arrayInsert($parm);
$this->sql_insert = 'insert into ' . $this->table_prefix . self::$tableName . ' set ' . $sql_in;
$a = self::$db->prepare($this->sql_insert)->execute($arr_in);
$this->init();
return $a;
}
public function multiInsert($parm){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數不正確');
}
$sql_in = '';
foreach ($parm as $k => $v){
$sql_in .= $k . '=:'. $k . ',';
}
return $sql_in;
}
public function arrayInsert($parm){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數不正確');
}
$arr = [];
foreach ($parm as $k => $v){
$arr[':'.$k] = $v;
}
return $arr;
}
public function createDatabase(){
}
public function createTable($tableName,$str){
self::$db->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
$this->sql_create_table = "create table " . $this->table_prefix . $tableName ."( " . $str . " )";
self::$db->exec($this->sql_create_table);
$this->init();
return true;
}
public function setField($column,$value){
//修改
if (is_int($value)){
$this->sql_save='update ' . $this->table_prefix . self::$tableName . ' set ' . $column . '=' . $value . $this->sql_where;
}elseif (is_string($value)){
$this->sql_save='update ' . $this->table_prefix . self::$tableName . ' set ' . $column . '=\'' . $value .'\''. $this->sql_where;
}
$res=self::$db->exec($this->sql_save);
$this->init();
return $res;
}
public function save($parm){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數錯誤');
}
$multiSql = trim(rtrim($this->multiSave($parm)),',');
$this->sql_save = 'update ' . $this->table_prefix . self::$tableName . ' set ' . $multiSql . $this->sql_where;
$res = self::$db->exec($this->sql_save);
return $res;
}
public function multiSave($parm){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ ."參數不正確");
}
$str='';
foreach ($parm as $k =>$v){
if (is_int($v)){
$str .= $k . '=' . $v . ',';
}elseif (is_string($v)){
$str .=$k . '=\'' . $v .'\',';
}
}
return $str;
}
}
~~~
好的,我們回顧一下,上一章的M函數里首先獲取了這個類的單例 ``getInstance()``獲取單例,這個沒有什么可說的,單例模式就是這樣的,講系統配置類的時候我詳細的寫過,忘記的朋友可以返回去再看看。下面開始講解里面的函數
1.構造函數
~~~
private function __construct(){
$dsn = C('database'); //首先獲取配置文件中的dsn,即數據庫信息
$this->table_prefix = $dsn['db_prefix']; //把表的前綴賦值給一個變量,這樣在連接時就不用每次都添加表前綴了
try{
self::$db = new \PDO('mysql:host='.$dsn['db_host'] . ';dbname=' . $dsn['db_name'] . ';charset='. $dsn['db_charset'],$dsn['db_user'],$dsn['db_password']); //把dsn配置信息拼接成參數字符串,作為PDO類的參數,實例化PDO類
}catch (S_Exception $e){
throw new S_Exception('數據庫連接出現錯誤');
}
return $this; //由于需要連貫操作,這里要返回當前對象$this
}
~~~
這里我想多提一句,創建數據庫時添加表前綴是非常有必要的,因為很多爆破工具呀,啊D,明小子這種,它們的暴力破解方法就是跑字典,把常用的表名都注入一遍,如果你還在用``admin``做表名的話,很容易被爆出來,所以為了你的數據庫安全,請務必添加表名,比如``example_admin``,這樣就可以抵擋住很大一部分低級黑客(也許是腳本小子?)的攻擊
2.select()函數
~~~
public function select($parm = null){
//$parm 要么不傳,要么只能傳入一個數組
if (is_array($parm)){
$sqli = rtrim($this->mutliArr($parm),','); //把傳入的數組拼接成字符串
$this->sql = 'select ' . $sqli . ' from ' .$this->table_prefix . self::$tableName . $this->sql_where ;//拼接sql語句
}else{
if (!is_null($parm)){
throw new S_Exception( __METHOD__ . '傳入的參數錯誤!');
}
$this->sql = "select * from " . $this->table_prefix . self::$tableName . $this->sql_where . $this->sql_limit; //不是數組的話,就查詢所有列,拼接sql字符串
}
$res = self::$db->query($this->sql); //執行查詢
$res->setFetchMode(\PDO::FETCH_ASSOC); //設置返回格式,PDO有多種返回格式
$arr = [];
foreach ($res as $row){
$arr[] = $row;
}
$this->init(); //由于是單例模式,每次執行完sql語句,要將原本的所有的變量都清空,防止多次執行時出錯
if (empty($arr)){
return false;
}
return $arr;
}
public function mutliArr($array){ //把傳入的數組拼接成字符串
$sqli = '';
foreach ($array as $v){
$sqli .= $v . ',';
}
return $sqli;
}
~~~
傳入的參數一定要是一個數組,然后處理數組,拼接為sql語句,并執行查詢,并將得到的結果返回。``M('admin')->select()``查詢admin表中的所有數據
3.where函數
~~~
public function where($parm = null){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數錯誤!');
}else{
$this->sql_where = ' where ' . rtrim(trim($this->multiWhere($parm)),'and'); //設置where語句的值,并且返回當前對象用于連貫操作
}
return $this;
}
public function multiWhere($parm){ //把where中的數組值處理成Mysql能識別的字符串內
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數錯誤!');
}
$where_prepare = '';
foreach ($parm as $k => $value) {
if (is_array($value)){
$where_prepare .=' '. $k . ' ' . $value[0] . $value[1] . ' and';
}else{
$where_prepare .= ' ' .$k . ' = '.'\'' . $value.'\'' . ' and';
}
}
return $where_prepare;
}
~~~
寫的挺明白的吧.....好像沒法再詳細講解了,這樣的一個類,我就可以使用``M('admin')->where(array('id'=>'5'))->select()``查詢admin表里id為5的所有數據
4.limit函數
~~~
public function limit($first, $second = null){ //拼接limit字符串,并返回當前對象用于連貫操作
if (is_null($second)){
$this->sql_limit = ' limit ' . $first ;
}else{
$this->sql_limit = ' limit ' . $first . ',' . $second;
}
return $this;
}
~~~
沒什么可說的,設定limit字符串
5.add函數
~~~
public function add($parm = null){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數不正確!');
}
$sql_in = rtrim(trim($this->multiInsert($parm)),','); //處理傳進來的參數
$arr_in = $this->arrayInsert($parm);
$this->sql_insert = 'insert into ' . $this->table_prefix . self::$tableName . ' set ' . $sql_in; //拼接字符串
$a = self::$db->prepare($this->sql_insert)->execute($arr_in); //執行添加操作
$this->init(); //初始化值
return $a; //返回插入后的id
}
public function multiInsert($parm){
if (!is_array($parm)){
throw new S_Exception(__METHOD__ . '參數不正確');
}
$sql_in = '';
foreach ($parm as $k => $v){
$sql_in .= $k . '=:'. $k . ',';
}
return $sql_in;
}
~~~
里面所有的操作都是大同小異,思路就是:如果是中間函數(where、limit等),就處理完畢后返回``$this``,如果是結尾函數(select,add,setField),就拼接字符串并執行,然后返回處理結果就行。