思考:现在掌握了很多面向对象相关的内容,但是什么时候封装类?怎么封装?如果使用类成员?
引入:其实类的封装没有多么复杂,但是需要一个熟练的过程来确定哪些东西可以放到类里,该用什么样的形式等。我们通过封装一个数据库的操作来综合练习下
封装数据库类 掌握
定义:封装数据库操作类,即根据数据库的操作需求,来确认数据库操作类该有什么样的功能,以及这些功能该如何实现。
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;
?>
至此:数据库类的功能已经实现,接下里要考虑的定义规范:类对成员的控制性,
属性如果不需要给外部访问 私有
方法如果只是内部调用 私有
总结:
类的封装是以功能驱动为前提,相关操作存放到一个类中
一个类通常是一个独立的文件,文件名与类名相同(方便后期维护和自动加载)
类中如果有数据需要管理,设定属性(固定可以使用类常量)
类中如果有功能需要实现,(数据加工) 设定方法
一个功能通常使用一个方法实现,方法的颗粒度应该尽可能小 方便复用