Completed
Push — main ( aad5b0...185e4c )
by Aspirant
17s queued 12s
created

Revision::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
nc 1
nop 3
dl 0
loc 9
rs 10
c 1
b 0
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
        return Db::name('revision')->insertGetId($data);
130
    }
131
132
    public function add(string $title)
133
    {
134
        // TODO: catch exception
135
        $this->setTableData();
136
        $revisionId = $this->saveRevision($title);
137
        return $revisionId;
138
    }
139
140
    private function initRevisionData()
141
    {
142
        $revision = Db::table('revision')->where('id', $this->revisionId)->find();
143
        if ($revision) {
144
            $this->mainTableData = $revision['main_data'];
145
            $this->i18nTableData = $revision['i18n_data'];
146
            $this->extraTableData = $revision['extra_data'];
147
            return [
148
                'tableName' => $revision['table_name'],
149
                'originalId' => $revision['original_id'],
150
                'title' => $revision['title'],
151
            ];
152
        }
153
        return [];
154
    }
155
156
    private function ifRevisionMatchOriginal(array $revisionData): bool
157
    {
158
        return ($revisionData['tableName'] === $this->tableName) && ($revisionData['originalId'] === $this->originalId);
159
    }
160
161
    private function updateMainTableData()
162
    {
163
        Db::name($this->tableName)->where('id', $this->originalId)->update($this->getMainTableData());
164
    }
165
166
    private function deleteOriginalI18nData()
167
    {
168
        Db::table($this->i18nTableName)->where('original_id', $this->originalId)->delete();
169
    }
170
171
    private function deleteOriginalExtraTableData()
172
    {
173
        if (!empty($this->extra)) {
174
            if (isAssocArray($this->extra)) {
175
                foreach ($this->extra as $tableName => $idFieldName) {
176
                    Db::table($tableName)->where($idFieldName, $this->originalId)->delete();
177
                }
178
            } else {
179
                $idFieldName = $this->tableName . '_id';
180
                foreach ($this->extra as $tableName) {
181
                    Db::table($tableName)->where($idFieldName, $this->originalId)->delete();
182
                }
183
            }
184
        }
185
    }
186
187
    private function insertI18nTableData()
188
    {
189
        Db::name($this->i18nTableName)->insertAll($this->getI18nTableData());
190
    }
191
192
    private function insertExtraTableData()
193
    {
194
        if (!empty($this->extra) && !empty($this->getExtraTableData())) {
195
            foreach ($this->getExtraTableData() as $tableName => $records) {
196
                Db::name($tableName)->insertAll($records);
197
            }
198
        }
199
    }
200
201
    public function restore(int $revisionId)
202
    {
203
        try {
204
            $this->revisionId = $revisionId;
205
            $revisionData = $this->initRevisionData();
206
            if (false === $this->ifRevisionMatchOriginal($revisionData)) {
207
                throw new Exception("The revision does not match the original record.");
208
            }
209
            $this->updateMainTableData();
210
            $this->deleteOriginalI18nData();
211
            $this->insertI18nTableData();
212
            $this->deleteOriginalExtraTableData();
213
            $this->insertExtraTableData();
214
        } catch (Exception $e) {
215
            throw new Exception($e->getMessage());
216
        }
217
    }
218
219
    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...
220
    {
221
        $names = array_keys($record);
222
        return array_diff($names, ['id', '_id']);
223
    }
224
225
    private function tableExists(string $tableName): bool
226
    {
227
        try {
228
            Db::query("select 1 from `$tableName` LIMIT 1");
229
        } catch (Exception $e) {
230
            return false;
231
        }
232
        return true;
233
    }
234
}
235