思考:现在掌握了很多面向对象相关的内容,但是什么时候封装类?怎么封装?如果使用类成员?
引入:其实类的封装没有多么复杂,但是需要一个熟练的过程来确定哪些东西可以放到类里,该用什么样的形式等。我们通过封装一个数据库的操作来综合练习下
封装数据库类 掌握
定义:封装数据库操作类,即根据数据库的操作需求,来确认数据库操作类该有什么样的功能,以及这些功能该如何实现。
1.一个类通常就是一个文件,所以要先确定文件的名字:通常类文件命名规范有两种。
- 文件名字与类名一样,如Sql.php
- 为了区分普通php文件,增加中间类描述,如Sql_class.php
- 现在php几乎都是面向对象变成,所以通常采用第一种方式:因此当前命名规范数据类的文件为:Sql_php
2.确定类文件名字后其实也就是确定了类名字,因此可以创建一个Sql类。
<?php //数据库操作类 class Sql{ } ?>
3.类的创建分两种:一是特定使用,即类里面的所有内容只为某次使用;二是通用,即工具类,以后很多地方可以用;
特定使用,功能可以不同太灵活
通用工具,功能应该大众化,数据的变化会比较多
数据库类以后凡是要操作数据库的地方都可以用得到,很多项目都会用到,所以应该是个通用类工具,因此要考虑其到处可用的特性,让其能够灵活。
4.数据库的错操最基本的特性不会改变,即需要连接认证 而连接认证的信息是灵活的,所以可以通过设定属性来控制,这些信息也都是不同使用这不同的
应该可以改变,所以可以通过__construct来实现数据传入
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //构造方法初始化数据,数据较多,应该使用数组来传递数据,关联数组,而绝大部分的开发者本意是用来测试, //所以基本都是本地,因此可以给默认数据 public function __construct(array $info=array()) { //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b $this->host=$info['host']??'localhost'; $this->user=$info['user']??'root'; $this->pwd=$info['pwd']??'root'; $this->port=$info['port']??'3306'; $this->dbname=$info['dbname']??'t1'; $this->charset=$info['charset']??'utf8'; } } $info =array(0=>'sdfsdf',1=>'sdf',2=>'sdfsdf'); print_r($info); ?>
注意:方法设定的原则是一个方法只实现一个简单的功能,不要多个功能堆积到一个方法中。
5.数据库属性会在实例化sql对象的时候自动初始化
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //初始化构造方法 public function __construct(array $info=array()) { //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; } } $s1=new Sql(); //使用默认数据库信息 $db=array( 'host'=>'localhost', 'user'=>'root', 'pwd'=>'root', 'dbname'=>'t1' ); $s2=new Sql($db); var_dump($s1); var_dump($s2) ?>
6.数据库要操作的第一件事情就是要验证,所以需要一个连接认证的功能,这里可以使用mysqli面向对象的方法,但是需要建立一个方法来实现连接认证
连接是否成功?
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //初始化构造方法 public function __construct(array $info=array()){ //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; } //认证数据库连接 public function sql_connet(){ //利用mysqli属性可以跨方法访问:5个参数分别为:主机,用户,密码,数据库,端口 $link=new mysqli($this->host,$this->user,$this->pwd,$this->dbname,$this->port); if($link->connect_errno){ die( $link->connect_error); } } } $s=new Sql(); $s->sql_connet(); //正常 没有错误
7.用户调用sql类的目的一定是为了操作数据库,那么用户在实例化之后就需要调用连接认证方法,为了方便用户操作,可以帮助用户省去调用这一步骤:在构造方法中调用该方法
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //初始化构造方法 public function __construct(array $info=array()){ //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; $this->sql_connet(); } //认证数据库连接 public function sql_connet(){ //利用mysqli属性可以跨方法访问:5个参数分别为:主机,用户,密码,数据库,端口 $link=new mysqli($this->host,$this->user,$this->pwd,$this->dbname,$this->port); if($link->connect_errno){ die( $link->connect_error); } } } $s=new Sql(); // $s->sql_connet(); //正常 没有错误 ?>
8.至此,一旦实例化sql类对象,就可以实现数据库的连接,但是此时还存在一个细节问题,字符集,为了保证数据库连接正常操作,需要新增一个方法设定字符集
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //增加一个属性来保存mysqli返回的连接(对象),需要跨方法使用 public $link; //初始化构造方法 public function __construct(array $info=array()){ //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; //调用连接认证方法 $this->sql_connet(); //调用字符集方法 $this->sql_charset(); } //认证数据库连接 public function sql_connet(){ //利用mysqli属性可以跨方法访问:5个参数分别为:主机,用户,密码,数据库,端口 $this->link=new mysqli($this->host,$this->user,$this->pwd,$this->dbname,$this->port); if($this->link->connect_errno){ die( $this->link->connect_error); } } //设置字符集 public function sql_charset(){ $sql="set names {$this->charset}"; //mysqli::query(); $res=$this->link->query($sql); //有外部调用就会有错误,判断 if(!$res){ die('charset error'.$this->link->error); } } } $s=new Sql(array('charset'=>'utf-8')); //会报错 应该是utf8 var_dump($s);
9.至此:数据库的初始化操作已经完成,此时要考虑的事情使用户调用数据库类是为了干什么?为了执行sql指令,也就是增删改查,在mysqli中所有的sql执行都是通过mysqli::query()方法执行
但是我们可以根据需求封装两个函数,写方法和查方法(包含一条和多条查询)。
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //增加一个属性来保存mysqli返回的连接(对象),需要跨方法使用 public $link; //初始化构造方法 public function __construct(array $info=array()){ //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; //调用连接认证方法 $this->sql_connet(); //调用字符集方法 $this->link->set_charset($this->charset); // 调用字符集方法 // $this->sql_charset(); //也是可以的 } //认证数据库连接 public function sql_connet(){ //利用mysqli属性可以跨方法访问:5个参数分别为:主机,用户,密码,数据库,端口 $this->link=new mysqli($this->host,$this->user,$this->pwd,$this->dbname,$this->port); if($this->link->connect_errno){ die( $this->link->connect_error); } } // //设置字符集,这么写有点麻烦 // public function sql_charset(){ // $sql="set names {$this->charset}"; // //mysqli::query(); // $res=$this->link->query($sql); // //有外部调用就会有错误,判断 // if(!$res){ // die('charset error'.$this->link->error); // } // } //写操作 public function sql_insert($sql){ $result=$this->link->query($sql); return $result; } } $s=new Sql(); $sql="insert into b1 (name,age,sex) values ('杜家铭',9,'男')"; $s->sql_insert($sql);
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //增加一个属性来保存mysqli返回的连接(对象),需要跨方法使用 public $link; //初始化构造方法 public function __construct(array $info=array()){ //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; //调用连接认证方法 $this->sql_connet(); //调用字符集方法 $this->link->set_charset($this->charset); // 调用字符集方法 // $this->sql_charset(); //也是可以的 } //认证数据库连接 public function sql_connet(){ //利用mysqli属性可以跨方法访问:5个参数分别为:主机,用户,密码,数据库,端口 $this->link=new mysqli($this->host,$this->user,$this->pwd,$this->dbname,$this->port); if($this->link->connect_errno){ die( $this->link->connect_error); } } // //设置字符集,这么写有点麻烦 // public function sql_charset(){ // $sql="set names {$this->charset}"; // //mysqli::query(); // $res=$this->link->query($sql); // //有外部调用就会有错误,判断 // if(!$res){ // die('charset error'.$this->link->error); // } // } //写操作 public function sql_insert($sql){ $result=$this->link->query($sql); return $result; } // 读操作sql_select() 第一个参数为sql语句,第二个参数表示返回单条还是多条默认单条 public function sql_select($sql,$all=false){ $result=$this->link->query($sql); if(!$all){ // 获取一个数据 return $result->fetch_assoc(); }else{ //获取所有数据 return $result->fetch_all(MYSQLI_ASSOC); } } } $s=new Sql(); $sql="select * from b1"; print_r($s->sql_select($sql,true));
10.上述已经完成了数据库类要实现的基本功能,实现sql指令的执行和结果返回,但是从功能细节的角度触发还需要进行完善,插入操作后,要获取自增长id,更新和删除操作受影响的行数,查询操作中
记录数量。这种使可以通过设置方式来实现获取(自增长id),也可以通过增加属性来实现(属性简单)
增加属性:受影响的行数,自增长id ,查询记录数
<?php //数据库操作类 class Sql{ //设置属性 public $host; public $user; public $pwd; public $port; public $dbname; public $charset; //增加一个属性来保存mysqli返回的连接(对象),需要跨方法使用 public $link; //初始化构造方法 public function __construct(array $info=array()){ //这里的??表示为 $a=$c??$b 等同于 $a=isset($c)?$c:$b; 如果变量c存在就等于c否则等于b //以上功能只有php 版本7.0 以上可以用 $this->host=$info['host'] ?? 'localhost'; $this->user=$info['user'] ?? 'root'; $this->pwd=$info['pwd'] ?? 'root'; $this->port=$info['port'] ?? '3306'; $this->dbname=$info['dbname'] ?? 't1'; $this->charset=$info['charset'] ?? 'utf8'; //调用连接认证方法 $this->sql_connet(); //调用字符集方法 $this->link->set_charset($this->charset); // 调用字符集方法 // $this->sql_charset(); //也是可以的 } //认证数据库连接 public function sql_connet(){ //利用mysqli属性可以跨方法访问:5个参数分别为:主机,用户,密码,数据库,端口 $this->link=new mysqli($this->host,$this->user,$this->pwd,$this->dbname,$this->port); if($this->link->connect_errno){ die( $this->link->connect_error); } } // //设置字符集,这么写有点麻烦 // public function sql_charset(){ // $sql="set names {$this->charset}"; // //mysqli::query(); // $res=$this->link->query($sql); // //有外部调用就会有错误,判断 // if(!$res){ // die('charset error'.$this->link->error); // } // } //写操作 //自增id public $auto_id; public function sql_insert($sql){ $result=$this->link->query($sql); //赋值受影响的自增列id $this->auto_id=$this->link->insert_id; return $result; } // 读操作sql_select() 第一个参数为sql语句,第二个参数表示返回单条还是多条默认单条 //受影响行数 public $rows; public function sql_select($sql,$all=false){ $result=$this->link->query($sql); // 赋值受影响的行数 $this->rows=$result->num_rows; if(!$all){ // 获取一个数据 return $result->fetch_assoc(); //赋值受影响行数 }else{ //获取所有数据 return $result->fetch_all(MYSQLI_ASSOC); //赋值受影响行数 } } } // $s=new Sql(); // $sql="insert into b1 (name,age,sex) values ('杜建',33,'女')"; // $s->sql_insert($sql); // echo $s->auto_id; //返回7 $s=new Sql(); $sql="select * from b1 where id=7"; print_r($s->sql_select($sql)); echo '<hr>'; echo $s->rows; ?>
至此:数据库类的功能已经实现,接下里要考虑的定义规范:类对成员的控制性,
属性如果不需要给外部访问 私有
方法如果只是内部调用 私有
总结:
类的封装是以功能驱动为前提,相关操作存放到一个类中
一个类通常是一个独立的文件,文件名与类名相同(方便后期维护和自动加载)
类中如果有数据需要管理,设定属性(固定可以使用类常量)
类中如果有功能需要实现,(数据加工) 设定方法
一个功能通常使用一个方法实现,方法的颗粒度应该尽可能小 方便复用