#### 数据库操作类
在教程开头我说过,我最喜欢的就是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),就拼接字符串并执行,然后返回处理结果就行。