PHP封装数据库操作类

  • 封装数据库操作类
  • 因为目前所使用的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>";
}
posted @ 2019-12-17 00:22  江湖艺人  阅读(947)  评论(0编辑  收藏  举报