• 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>";
    }
    
  • 相关阅读:
    Golang的类型转换实战案例
    Golang的基础数据类型-布尔型
    Golang的运算符优先级实操案例
    Golang的单目(一元)运算符-地址操作符和接收操作符
    基于Ambari的WebUI部署kafka服务
    HBase集群优化篇
    HBase Master的高可用实战案例
    HBase 数据迁移实战案例
    HBase API实战案例
    MySQL数据源接入DBus
  • 原文地址:https://www.cnblogs.com/wjlbk/p/12633508.html
Copyright © 2020-2023  润新知