PHP设计模式之数据访问对象模式

数据访问对象模式描述了如何创建提供透明访问任何数据源的对象。
在应用程序中,必须编写一条 SQL 语句才能在数据库内创建实体。接下来,为了提供对实体任何特性的更新,还需要编写另一条 SQL 语句。创建这些 SQL 语句所涉及的重复操作就会非常的频繁。所以,我们可以使用数据库访问模式,来封装创建 SQL 调用、减少实体创建的复杂性和重复。

数据库查询第一版:普通版本

demo1.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
header('Content-Type:text/html; charset=utf-8');

class DB{
private $pdo;

public function __construct(){
try{
$drive_opt = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'];
$this->pdo = new PDO('mysql:host=localhost;dbname=db', 'root', 'password', $drive_opt);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
exit('数据库连接错误:'.$e->getMessage());
}
}

public function select($sql){
$stmt = $this->pdo->prepare($sql);
$stmt->execute();

$result = [];
while(!!$rowObj = $stmt->fetchObject()){
$result[] = $rowObj;
}
return $stmt;
}
}

$db = new DB;
print_r($db->select('SELECT user,email FROM user')); // 裸写SQL

echo '<br />';

print_r($db->select('SELECT user,email FROM user WHERE id<20'));

echo '<br />';

print_r($db->select('SELECT user,email FROM user WHERE id<20 LIMIT 0,2'));

此版本里重点看 select(sql)这个方法,通过传递 SQL 语句进行执行。但如果 SQL 语句变的很多的时候,会发生一些重复。例如 SQL 语句里的 SELECT,FROM 等字符串。

数据库查询第二版:封装 SQL

demo2.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
header('Content-Type:text/html; charset=utf-8');

class DB{
private $pdo;

public function __construct(){
try{
$drive_opt = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'];
$this->pdo = new PDO('mysql:host=localhost;dbname=db', 'root', 'password', $drive_opt);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
exit('数据库连接错误:'.$e->getMessage());
}
}

public function select($field, $table, $where = '', $limit = ''){
if($where) $where = 'WHERE '.$where;
if($limit) $limit = 'LIMIT '.$limit;
$sql = "SELECT $field FROM $table $where $limit";
$stmt = $this->pdo->prepare($sql);
$stmt->execute();

$result = [];
while(!!$rowObj = $stmt->fetchObject()){
$result[] = $rowObj;
}
return $stmt;
}
}

$db = new DB;
print_r($db->select('user,email', 'user'));

echo '<br />';

print_r($db->select('user,email', 'user', 'id<20'));

echo '<br />';

print_r($db->select('user,email', 'user', 'id<20', '0,2'));

此版本 select()方法的参数发生了改变,我们把 SQL 进行了封装,通过客户端传递参数来进行组装 SQL 语句,避免的避免一些重复。但由于这种封装可能造成一些麻烦,比如封装过度,阅读困难,书写 SQL 没有裸写灵活。

数据库查询第三版:参数改为数组传递

demo3.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
header('Content-Type:text/html; charset=utf-8');

class DB{
private $pdo;
private $table = 'user';

public function __construct(){
try{
$drive_opt = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'];
$this->pdo = new PDO('mysql:host=localhost;dbname=db', 'root', 'password', $drive_opt);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
exit('数据库连接错误:'.$e->getMessage());
}
}

public function select($field = [], $data = []){
if($data['where']) $where = 'WHERE '.$data['where'];
if($data['limit']) $limit = 'LIMIT '.$data['limit'];
$field = implode(',', $field);
$sql = "SELECT $field FROM $this->table $where $limit";
$stmt = $this->pdo->prepare($sql);
$stmt->execute();

$result = [];
while(!!$rowObj = $stmt->fetchObject()){
$result[] = $rowObj;
}
return $stmt;
}

public function selectFull($sql){
$stmt = $this->pdo->prepare($sql);
$stmt->execute();

$result = [];
while(!!$rowObj = $stmt->fetchObject()){
$result[] = $rowObj;
}
return $stmt;
}
}

$db = new DB;
print_r($db->select(['user', 'email']));

echo '<br />';

print_r($db->select(['user', 'email'], ['where'=>'id<20']));

echo '<br />';

print_r($db->select(['user', 'email'], ['where'=>'id<20', 'limit'=>'0,2']));

此版本将普通传参改成数组传值,阅读稍微提高了一点,传值错误率也减少了一点,但书写 SQL 还是不够灵活,性能上也没裸写的高,对于复杂的 SQL,经常无能为力。这是时候,建议创建一个提供裸写 SQL 的方法 selectFull()。