Revision::restore()   A
last analyzed

Complexity

Conditions 3
Paths 9

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
eloc 12
nc 9
nop 1
dl 0
loc 15
rs 9.8666
c 1
b 1
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace aspirantzhang\octopusRevision;
6
7
use think\facade\Db;
8
use think\Exception;
9
10
class Revision
11
{
12
    private $tableName;
13
    private $i18nTableName;
14
    private $originalId;
15
    private $revisionId;
16
    private $mainTableData;
17
    private $i18nTableData;
18
    private $recordUpdateTime;
19
    private $extra;
20
    private $extraTableData;
21
22
    public function __construct(string $tableName, int $originalId, array $extra = [])
23
    {
24
        $this->tableName = $tableName;
25
        $this->i18nTableName = $tableName . '_i18n';
26
        $this->originalId = (int)$originalId;
27
        $this->mainTableData = '[]';
28
        $this->i18nTableData = '[]';
29
        $this->extraTableData = '[]';
30
        $this->extra = $extra;
31
    }
32
33
    private function setMainTableData(array $data)
34
    {
35
        unset($data['id']);
36
        $this->mainTableData = json_encode($data);
37
    }
38
39
    private function getMainTableData()
40
    {
41
        return json_decode($this->mainTableData, true);
42
    }
43
44
    private function setI18nTableData(array $data)
45
    {
46
        foreach ($data as &$singleI18nRecord) {
47
            unset($singleI18nRecord['_id']);
48
        }
49
        $this->i18nTableData = json_encode($data);
50
    }
51
52
    private function setRecordUpdateTime(string $time)
53
    {
54
        $this->recordUpdateTime = $time;
55
    }
56
57
    private function getI18nTableData()
58
    {
59
        return json_decode($this->i18nTableData, true);
60
    }
61
62
    private function getExtraTableData()
63
    {
64
        return json_decode($this->extraTableData, true);
65
    }
66
67
    private function i18nTableExists(): bool
68
    {
69
        return $this->tableExists($this->i18nTableName);
70
    }
71
72
    private function setExtraTableData()
73
    {
74
        if (empty($this->extra)) {
75
            return;
76
        }
77
        $result = [];
78
        if (isAssocArray($this->extra)) {
79
            foreach ($this->extra as $tableName => $idFieldName) {
80
                if ($this->tableExists($tableName)) {
81
                    $record = Db::table($tableName)->where($idFieldName, $this->originalId)->select()->toArray();
82
                    foreach ($record as &$arr) {
83
                        unset($arr['id']);
84
                    }
85
                    $result[$tableName] = $record;
86
                }
87
            }
88
        } else {
89
            $idFieldName = $this->tableName . '_id';
90
            foreach ($this->extra as $tableName) {
91
                $record = Db::table($tableName)->where($idFieldName, $this->originalId)->select()->toArray();
92
                foreach ($record as &$arr) {
93
                    unset($arr['id']);
94
                }
95
                $result[$tableName] = $record;
96
            }
97
        }
98
        $this->extraTableData = json_encode($result);
99
    }
100
101
    private function setTableData(): void
102
    {
103
        // main table
104
        $record = Db::table($this->tableName)->where('id', $this->originalId)->find();
105
        $this->setMainTableData($record);
106
        $this->setRecordUpdateTime($record['update_time']);
107
        // i18n table
108
        if ($this->i18nTableExists()) {
109
            $i18nRecord = Db::table($this->i18nTableName)->where('original_id', $this->originalId)->select()->toArray();
110
            $this->setI18nTableData($i18nRecord);
111
        }
112
        // extra table
113
        $this->setExtraTableData();
114
    }
115
116
    private function saveRevision(string $title)
117
    {
118
        $currentTime = date('Y-m-d H:i:s');
119
        $data = [
120
            'table_name' => $this->tableName,
121
            'original_id' => $this->originalId,
122
            'title' => $title,
123
            'main_data' => $this->mainTableData,
124
            'i18n_data' => $this->i18nTableData,
125
            'extra_data' => $this->extraTableData,
126
            'create_time' => $this->recordUpdateTime,
127
            'update_time' => $currentTime
128
        ];
129
        try {
130
            return Db::name('revision')->insertGetId($data);
131
        } catch (Exception $e) {
132
            throw new Exception(__('write revision data failed'));
133
        }
134
    }
135
136
    public function save(string $title)
137
    {
138
        try {
139
            $this->setTableData();
140
            $revisionId = $this->saveRevision($title);
141
            return $revisionId;
142
        } catch (Exception $e) {
143
            throw new Exception($e->getMessage());
144
        }
145
    }
146
147
    private function initRevisionData()
148
    {
149
        $revision = Db::table('revision')->where('id', $this->revisionId)->find();
150
        if ($revision) {
151
            $this->mainTableData = $revision['main_data'];
152
            $this->i18nTableData = $revision['i18n_data'];
153
            $this->extraTableData = $revision['extra_data'];
154
            return [
155
                'tableName' => $revision['table_name'],
156
                'originalId' => $revision['original_id'],
157
                'title' => $revision['title'],
158
            ];
159
        }
160
        return [];
161
    }
162
163
    private function ifRevisionMatchOriginal(array $revisionData): bool
164
    {
165
        return ($revisionData['tableName'] === $this->tableName) && ($revisionData['originalId'] === $this->originalId);
166
    }
167
168
    private function updateMainTableData()
169
    {
170
        Db::name($this->tableName)->where('id', $this->originalId)->update($this->getMainTableData());
171
    }
172
173
    private function deleteOriginalI18nData()
174
    {
175
        Db::table($this->i18nTableName)->where('original_id', $this->originalId)->delete();
176
    }
177
178
    private function deleteOriginalExtraTableData()
179
    {
180
        if (!empty($this->extra)) {
181
            if (isAssocArray($this->extra)) {
182
                foreach ($this->extra as $tableName => $idFieldName) {
183
                    Db::table($tableName)->where($idFieldName, $this->originalId)->delete();
184
                }
185
            } else {
186
                $idFieldName = $this->tableName . '_id';
187
                foreach ($this->extra as $tableName) {
188
                    Db::table($tableName)->where($idFieldName, $this->originalId)->delete();
189
                }
190
            }
191
        }
192
    }
193
194
    private function insertI18nTableData()
195
    {
196
        Db::name($this->i18nTableName)->insertAll($this->getI18nTableData());
197
    }
198
199
    private function insertExtraTableData()
200
    {
201
        if (!empty($this->extra) && !empty($this->getExtraTableData())) {
202
            foreach ($this->getExtraTableData() as $tableName => $records) {
203
                Db::name($tableName)->insertAll($records);
204
            }
205
        }
206
    }
207
208
    public function restore(int $revisionId)
209
    {
210
        try {
211
            $this->revisionId = $revisionId;
212
            $revisionData = $this->initRevisionData();
213
            if (false === $this->ifRevisionMatchOriginal($revisionData)) {
214
                throw new Exception("The revision does not match the original record.");
215
            }
216
            $this->updateMainTableData();
217
            $this->deleteOriginalI18nData();
218
            $this->insertI18nTableData();
219
            $this->deleteOriginalExtraTableData();
220
            $this->insertExtraTableData();
221
        } catch (Exception $e) {
222
            throw new Exception($e->getMessage());
223
        }
224
    }
225
226
    private function getAllColumnNamesWithoutId(array $record): array
0 ignored issues
show
Unused Code introduced by
The method getAllColumnNamesWithoutId() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
227
    {
228
        $names = array_keys($record);
229
        return array_diff($names, ['id', '_id']);
230
    }
231
232
    private function tableExists(string $tableName): bool
233
    {
234
        try {
235
            Db::query("select 1 from `$tableName` LIMIT 1");
236
        } catch (Exception $e) {
237
            return false;
238
        }
239
        return true;
240
    }
241
}
242