什么是软删除
软删除也叫逻辑删除,指在数据库中使用标记字段表示数据的删除状态,而不是正真删除数据。
为什么要用软删除
项目在生产环境中产生的数据,即使在业务上删除的数据,也有存在的意义,彻底的抹去这些已删除数据可能会对我们进行数据分析产生影响。
使用软删除保证了数据的完整性,另外在定位程序问题上也有一定的帮助。
实现思路
我的习惯是,在需要软删除的业务表中,加入以下4个字段:
1
2
3
4
5
6
-- MySQL 举例 --
`status` tinyint(3) NOT NULL DEFAULT '1' COMMENT '状态(1正常,2更新,3删除)',
`created_at` bigint(13) NOT NULL COMMENT '创建时间',
`updated_at` bigint(13) NOT NULL COMMENT '最后更新时间',
`deleted_at` bigint(13) NOT NULL COMMENT '删除时间',
-- 也可以使用created、updated、deleted,字段类型可以根据业务来定,可以使用datetime --
然后在框架中,修改原来的对应数据库操作逻辑:
- add:增加当前时间戳(created_at/created)
- update:更新当前时间戳(updated_at/updated),更新status=2
- delete:更新当前时间戳(deleted_at/deleted),更新status=3
- 查询:增加查询条件
status<3
在PHP的phalapi框架下改造软删除的示例
原框架中没有内置软删除功能,我们需要修改框架的 DataModel 类中的方法来实现。
我们不要去直接修改 DataModel 类,而是创建一个 Model 类继承自 DataModel。
1
class Model extends DataModel
然后重写以下的方法,让项目中的 业务表Model 继承于我们创建的这个 Model类。
- 插入 insert
- 自动插入创建时间 created
- 更新 updateAll 和 update
- 自动更新更新时间 updated
- 自动更新数据状态 status = 2(已更新)
- 删除 deleteAll 和 deleteIds
- 原删除 delete 操作改为 更新 update 操作
- 自动更新删除时间 deleted
- 自动更新数据状态 status = 3(已删除)
- 查询 getList、getDataMoreBy、count
- 查询条件增加软删除状态过滤,status < 3 (已删除)
当然,修改完 Model 之后,别忘了每张数据表也都应该修改,必须包含status、created、updated、deleted
四个字段。
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/** ---------------- 插入操作 ---------------- **/
public function insert($data, $id = NULL)
{
// 创建时间 xtongs
$data['created'] = date('Y-m-d H:i:s');
$id = parent::insert($data, $id);
return $id !== FALSE ? intval($id) : $id;
}
/** ---------------- 更新操作 ---------------- **/
/**
* 更新全部数据
* @param string|array $where 查询条件,例如:id = 1,或数组形式array('id' => 1)
* @param array $updateData
* @return int|boolean 返回更新的条数
*/
public function updateAll($where, array $updateData) {
// 更新时间 xtongs
$updateData['updated'] = date('Y-m-d H:i:s');
$updateData['status'] = Constant::STATUS_UPDATE;
return $this->getORM()->where($where)->update($updateData);
}
public function update($id, $data)
{
// 更新时间 xtongs
$updateData['updated'] = date('Y-m-d H:i:s');
$updateData['status'] = Constant::STATUS_UPDATE;
return parent::update($id, $data); // TODO: Change the autogenerated stub
}
/** ---------------- 删除操作 ---------------- **/
/**
* 删除全部
* @param string|array $where 查询条件,例如:id = 1,或数组形式array('id' => 1)
* @return int|boolean 返回删除的条数
*/
public function deleteAll($where) {
// 软删除,删除时间 xtongs
$updateData['deleted'] = date('Y-m-d H:i:s');
$updateData['status'] = Constant::STATUS_DELETE;
return $this->getORM()->where($where)->update($updateData);
}
/**
* 根据多个ID删除
* @param string|array $ids
* @return int|boolean 返回删除的条数
*/
public function deleteIds($ids) {
// 软删除,删除时间 xtongs
$updateData['deleted'] = date('Y-m-d H:i:s');
$updateData['status'] = Constant::STATUS_DELETE;
return $this->getORM()->where('id', $ids)->update($updateData);
}
/** ---------------- 查询操作 ---------------- **/
/**
* 根据条件,取列表数组
* @param string|array $where 查询条件
* @param array $whereParams 更复杂查询条件时的动态参数
* @param string|array $select 需要获取的字段
* @param string $order 排序
* @param int $page 第几页
* @param int $perpage 分页数量
* @return array 没有时返回空数组
*/
public function getList($where = NULL, $whereParams = array(), $select = '*', $order = NULL, $page = 1, $perpage = 0) {
$page = intval($page);
$perpage = intval($perpage);
$orm = $this->getORM();
// 软删除标记 xtongs
$where['status < ?'] = Constant::STATUS_DELETE;
// 条件
if (!empty($where) && !empty($whereParams)) {
$orm->where($where, $whereParams);
} else if (!empty($where)) {
$orm->where($where);
}
// 字段选择
$select = is_array($select) ? implode(',', $select) : $select;
$orm->select($select);
// 排序
$order = is_array($order) ? implode(', ', $order) : $order;
if (!empty($order)) {
$orm->order($order);
}
// 分页
if ($perpage) {
return $orm->page($page, $perpage)->fetchAll();
}
return $orm->fetchAll();
}
/**
* 获取多条纪录
* @param string $field 需要查询的字段名,通常为主键或带有唯一索引的字段
* @param string|int|float|NULL $value 查询的值
* @param int $limit 需要获取的数量,为0时无限制,顺序获取
* @param string|array $select 需要获取的字段
* @return array 没有时返回空数组
*/
public function getDataMoreBy($field, $value, $limit = 0, $select = '*') {
$orm = $this->getORM()
->select(is_array($select) ? implode(',', $select) : $select)
->where($field, $value)
// 软删除 xtongs
->where('status < ?', Constant::STATUS_DELETE);
$limit = intval($limit);
if ($limit > 0) {
$orm->limit(0, $limit);
}
return $orm->fetchAll();
}
/**
* 获取总数
* @param string|array|NULL $where 统计条件
* @param string $countBy 需要统计的字段名
* @return int 总数
*/
public function count($where = NULL, $countBy = '*') {
$orm = $this->getORM();
// 软删除标记 xtongs
$where['status < ?'] = Constant::STATUS_DELETE;
// 条件
if (!empty($where)) {
$orm->where($where);
}
$total = $orm->count($countBy);
return intval($total);
}