- 封装数据库操作类:
- 因为目前所使用的mysqli扩展实现数据库的操作还比较零散,如果想要高效的使用,就必须要进行二次加工
- 在面向对象编程中,所有操作都应该是由类来实现完成
- 封装的完整程度是根据业务的需求来定
步骤
1、确定要封装的业务:基于mysqli的数据库底层实现,完成数据库操作的基本诉求
- 简化初始化操作
- 实现错误处理
- 实现增删改查
2、明确封装的类的作用,确定内部实现机制
- 方法独立性:一个方法只做一件事情
- 方法独立性:方法只负责执行,不对结果进行任何处理,交由外部调用处判定
- 灵活性:封装的所有操作应该是灵活的,不是写死的内容
3、根据思路明确封装类的数据和数据操作
- 数据使用属性保留:数据需要跨方法或者对外提供数据支持
- 数据库操作的数据:主机地址、端口、用户名、密码、数据库名字、字符集
- 数据库连接资源跨方法:连接资源
- 错误信息对外数据支持:错误信息、错误编号
- 数据操作具体功能
- 初始化资源工作:构造方法,实现属性初始化
- 初始化数据库资源:实现数据库的连接认证、字符集设置和数据库选择:失败返回false并记录错误
- SQL指令语法检查:SQL执行并完成错误处理:失败返回false并记录错误
- 写操作:实现增伤改指令的执行:调用SQL指令语法检查,成功返回受影响行数
- 自增长ID获取:实现自增长id获取
- 读操作:单记录获取和多记录获取:调用SQL指令语法检查
4、确定类的控制
- 不需要外部访问和使用的私有
- 明确外部需要用到的公有
- 如果数据安全性要求高,那么可以属性私有,但是允许对外提供可以操作的公有方法(内部安全处理)
示例
1、一个类通常就是一个文件,所以要先确定文件的名字:通常类文件命名规范有两种
- 文件名字与类名字一样,如Sql.php
- 为了区分普通PHP文件,增加中间类描述,如Sql.class.php
- 现在PHP几乎都是面向对象编程,所以通常采用第一种方式:因此当前命名数据类的文件为:Sql.php
2、确定类文件名字后其实也就确定了类名字,因此可以创建一个Sql类
# 数据库操作类
class Sql{}
3、类的创建分两种:一是特定使用,即类里面的所有内容只为某次使用;二是通用,即工具类,以后很多地方可以用。
- 特定使用,功能可以不用太灵活
- 通用工具,功能应该大众化,数据的变化会比较多
数据库类以后凡是要操作数据库的地方都可以用得到,很多项目都会用到,所以应该是个通用工具类,因此要考虑其到处可用的特性,让其能够灵活
4、数据库的操作最基本的特性不会改变:即需要连接认证,而连接认证的信息是灵活的,所以可以通过设定属性来控制,这些信息也都是不同使用者不同的,应该可以改变,所以可以通过构造方法来实现数据传入
# 数据库操作类
class Sql{
# 设置属性:数据库初始化信息
public $host;
public $port;
public $user;
public $pass;
public $dbname;
public $charset;
# 构造方法初始化数据:数据较多,应该使用数组来传递数据,关联数组,而且绝大部分的开发者本意是用来测试,所以基本都是本地,因此可以给默认数据
/*
$info = array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'pass' => 'root',
'dbname' => 'blog',
'charset' => 'utf8'
)
*/
public function __construct(array $info = array()){
# 初始化:确保用户传入了数据,否则使用默认值
$this->host = $info['host'] ?? 'localhost';
$this->port = $info['port'] ?? '3306';
$this->user = $info['user'] ?? 'root';
$this->pass = $info['pass'] ?? 'root';
$this->dbname = $info['dbname'] ?? 'test';
$this->charset = $info['charset'] ?? 'utf8';
}
}
注意:方法设定的原则是一个方法只实现一个简单的功能,不要多个功能堆积到一个方法中。
5、数据库属性会在实例化Sql对象的时候自动初始化
# 接上述代码(类外测试)
$s1 = new Sql(); # 使用默认数据库信息
$db = array(
'host' => '192.168.0.1',
'user' => 'admin',
'pass' => 'admin',
'dbname' => 'Taobao'
);
$s2 = new Sql($db); # 使用外部数据库信息
6、数据库要操作的第一件事就是连接认证,所以需要一个连接认证的功能。这里可以使用mysqli面向过程的方法。但是需要建立一个方法来实现连接认证:连接是否成功?
# 对外提供属性,记录错误数据(外部如何处理,不需要我们管)
public $errno;
public $error;
# mysqli的连接资源对象是任何mysqli扩展操作的基础,因此需要该连接对象能够在其他方法中使用:属性处理
public $link;
# 在上述类中增加一个方法:实现连接认证功能
public function connect(){
# 利用属性可以跨方法访问:5个参数分别为:主机、用户名、密码、数据库、端口
# 利用错误抑制符抑制可能出现的错误
$this->link = @mysqli_connect($this->host,$this->user,$this->pass,$this->dbname,$this->port);
# 判定连接是否成功
if(!$this->link){
# 将错误信息保存到记录错误的属性中,返回false
$this->errno = mysqli_connect_errno();
$this->error = mysqli_connect_error();
return false;
}
# 返回一个连接结果:不需要返回资源对象,为真即可表示成功
return true;
}
7、连接认证包括数据库选择设定好后,此时还存在一个细节问题:字符集,为了保证数据库连接的正常操作,需要新增一个方法设定字符集
# 在Sql类中增加设定字符集的方法
public function charset(){
# 调用mysqli的设置字符集的函数
$res = mysqli_set_charset($this->link,$this->charset);
# 判定是否成功
if(!$res){
$this->errno = mysqli_errno($this->link);
$this->error = mysqli_error($this->link);
return false;
}
return true;
}
8、初始化完成后,可以实现具体的业务处理:所有的SQL都需要使用mysqli_query执行,也都可能产生错误,因此封装一个专门执行SQL并检查错误的方法
# SQL执行以及错误检查
public function check($sql){
# 执行SQL
$res = mysqli_query($this->link,$sql);
# 判定结果
if(!$res){
$this->errno = mysqli_errno($this->link);
$this->error = mysqli_error($this->link);
return false;
}
# 返回正确结果
return $res;
}
9、上述功能本质也可以是一个写操作(不完整),但是写操作是有业务性的:返回受影响的行数,因此独立增加一个写操作方法,调用上述方法实现,并根据结果返回受影响的行数
# 写操作
public function write($sql){
# 调用方法检查执行
$res = $this->check($sql);
# 判定执行结果:成功返回受影响的行数,失败返回false,错误已经在check方法中记录
return $res ? mysqli_affected_rows($this->link) : false;
}
10、写操作中可能会有新增需求,因此也对外提供一个获取自增长Id的方法
# 自增长id
public function insert_id(){
return mysql_insert_id($this->link);
}
11、读取操作:读取一条记录(利用check进行SQL执行和错误检查):读取一条数据可能需要获取当前查询结果的列数,增加属性保留
# 属性:记录查询结果中的列数
public $columns = 0;
# 读操作
public function read_one($sql){
# 执行SQL错误检查
$res = $this->check($sql);
# 读取记录列数
$this->columns = @mysqli_field_count($this->link);
# 判定结果,进行加工:成功读取一条记录,失败返回错误信息
return $res ? mysqli_fetch_assoc($res) : $res;
}
12、读取操作:读取多条记录:可能需要知道总的记录数以及查询结果的列数
# 属性:记录查询结果的行数
public $rows = 0;
# 读操作
public function read_all($sql){
# 执行SQL错误检查
$res = $this->check($sql);
# 判定结果,进行加工
if(!$res) return $res;
# 记录结果数量
$this->rows = mysqli_num_rows($res);
# 根据需求解析数据
$list = [];
while($row = mysqli_fetch_assoc($res)) $list[] = $row;
# 返回结果
return $list;
}
13、用户在使用Sql类的时候,必须要进行第一步实例化、然后连接认证和实现字符集设置。这样的话用户操作比较麻烦,应该是用户实例化Sql类就可以直接进行相应的业务处理:所以可以将连接认证、字符集设置在初始化方法中实现(构造方法)
public function __construct(array $info = array()){
# 初始化:确保用户传入了数据,否则使用默认值
$this->host = $info['host'] ?? 'localhost';
$this->port = $info['port'] ?? '3306';
$this->user = $info['user'] ?? 'root';
$this->pass = $info['pass'] ?? 'root';
$this->dbname = $info['dbname'] ?? 'test';
$this->charset = $info['charset'] ?? 'utf8';
# 调用初始化和字符集设置
if(!$this->connect()) return;
$this->charset();
}
14、确定类的控制:不需要外部访问的私有,需要外部访问的公有,重要的数据私有并增加公有操作方法进行安全控制
# 数据库初始化资源私有:不需要外部访问
private $host;
private $port;
private $user;
private $pass;
private $dbname;
private $charset;
# 连接资源仅限内部使用
private $link;
# 连接认证和字符集设置已经内部调用,不需要外部使用
private function connect(){}
private function charset(){}
# SQL检查属于内部调用,不需要公有
private function check($sql){}
14、测试:利用数据库类实现数据库的写操作和读操作
小结
1、类的封装是以功能驱动为前提,相关操作存放到一个类中
2、一个类通常是一个独立的文件,文件名与类名相同(方便后期维护和自动加载)
3、类中如果有数据需要管理,设定属性
4、类中如果有功能需要实现(数据加工),设定方法
5、一个功能通常使用一个方法实现,方法的颗粒度应该尽可能小(方便复用)
6、应该尽可能增加类对成员的控制:即能私有尽可能私有
7、类中需要实现的功能应该由具体的业务来实现支撑
- 实用类:只考虑当前业务,不考虑全面性(代码少,应用范围小)
- 工具类:全面综合考虑,尽可能多的封装可能存在的业务(代码多,应用范围广)
封装代码如下:
<?php
//数据库操作类
class Sql
{
private $host;
private $port;
private $user;
private $pass;
private $dbname;
private $charset;
// 实现数据的初始化:灵活性(允许外部修改)和通用性(给定默认值)
public function __construct(array $arr = array())
{
// 完成初始化
$this->host = $arr['host'] ?? '127.0.0.1';
$this->port = $arr['port'] ?? '3306';
$this->user = $arr['user'] ?? 'root';
$this->pass = $arr['pass'] ?? 'root';
$this->dbname = $arr['dbname'] ?? 'play';
$this->charset = $arr['charset'] ?? 'utf8';
// 对象调用构造方法,永远不需要它的返回值
// 实现初始化数据库操作
if(!$this->connect()) return; # 为了中断执行,初始化连接.加载时,连接数据库
$this->charset(); //可以初始化字符集,当对象加载时,附上默认字符集
}
# 对外提供属性,记录错误数据(外部如何处理,不需要我们管)
public $errno;
public $error;
# mysqli的连接资源对象是任何mysqli扩展操作的基础,因此需要该连接对象能够在其他方法中使用:属性处理
private $link;
# 在上述类中增加一个方法:实现连接认证功能
private function connect()
{
# 利用属性可以跨方法访问:5个参数分别为:主机、用户名、密码、数据库、端口
# 利用错误抑制符抑制可能出现的错误
$this->link = @mysqli_connect($this->host, $this->user, $this->pass, $this->dbname, $this->port);
if (!$this->link) {
// 记录错误信息 返回false
$this->errno = mysqli_errno();
$this->error = iconv('utf-8', 'gbk', mysqli_error()); #第一个参数原来的,第二个要转成的字符集
}
#正确返回
return true;
}
// 字符集设置
private function charset()
{
// 利用mysqli实现字符集设置
// 这个结果,不需要给外部使用,所以使用局部变量$res
$res = @mysqli_set_charset($this->link, $this->charset);
// 判定结果
if (!$res) {
$this->errno = mysqli_errno($this->link);
$this->error = mysqli_error($this->link);
return false;
}
// 正确操作
return true;
}
// SQL执行检查
private function check($sql)
{
#mysqli_query执行
$res = mysqli_query($this->link, $sql);
// 判断错误
if (!$res) {
$this->errno = mysqli_errno($this->link);
$this->error = mysqli_error($this->link);
return false;
}
// 成功返回结果
return $res;
}
// 写操作
public function write($sql)
{
// 调用SQL检查方法,检查和执行
$res = $this->check($sql);
// 根据结果判定: 如果$res是true,说明执行成功,应该获取受影响的行数,如果为false就返回false
return $res ? mysqli_affected_rows($this->link) : false;
}
// 获取自增长ID的方法
public function insert_id()
{
return mysqli_insert_id($this->link);
}
// 读取操作,一条记录
// 定义属性,记录读取的列数
public $columns = 0;
public function read_one($sql)
{
// 执行检查
$res = $this->check($sql);
// 判定结果
if ($res) {
// 有结果
$this->columns = @mysqli_field_count($this->link);
return mysqli_fetch_assoc($res);
}
// 没有结果
return false;
}
// 读取多条记录
// 定义属性,记录行数
public $rows;
public function read_all($sql)
{
// 执行检查
$res = $this->check($sql);
// 错误检查
if (!$res) return false;
// 结果正确
// 记录行
$this->rows = @mysqli_num_rows($res);
// 记录列
$this->columns = @mysqli_field_count($this->link);
// 获取所有记录,形成二维数组
$list = [];
while ($row = mysqli_fetch_assoc($res)) $list[] = $row;
// 返回结果
return $list;
}
}
$s1 = new Sql();
$res = $s1->read_all("select * from info");
//$res = $s1->write("insert into info values('null','潘长江','489360698','489360698@qq.com')");
var_dump($res);
echo $s1->insert_id();
echo $s1->errno, $s1->error;
对象操作mysqli数据库方法
<?php
$host='127.0.0.1';
$user = 'root';
$pwd = 'root';
$db = new mysqli();
//创建连接
$db->connect($host,$user,$pwd);
//选择数据库
$db->select_db('plus');
//设置编码
$db->set_charset('utf8');
$sql = "select * from news";
$res = $db->query($sql);
while($row = $res->fetch_assoc()) {
echo "<pre>";
print_r($row);
echo "</pre>";
}