1: | <?php
|
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: |
|
11: |
|
12: | namespace Xmf\Database;
|
13: |
|
14: | use Xmf\Module\Helper;
|
15: | use Xmf\Yaml;
|
16: |
|
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: |
|
34: | class Migrate
|
35: | {
|
36: |
|
37: |
|
38: | protected $helper;
|
39: |
|
40: |
|
41: | protected $moduleTables;
|
42: |
|
43: |
|
44: | protected $tableHandler;
|
45: |
|
46: |
|
47: | protected $tableDefinitionFile;
|
48: |
|
49: |
|
50: | protected $targetDefinitions;
|
51: |
|
52: | |
53: | |
54: | |
55: | |
56: | |
57: | |
58: | |
59: |
|
60: | public function __construct($dirname)
|
61: | {
|
62: | $this->helper = Helper::getHelper($dirname);
|
63: | if (false === $this->helper) {
|
64: | throw new \InvalidArgumentException("Invalid module $dirname specified");
|
65: | }
|
66: | $module = $this->helper->getModule();
|
67: | $this->moduleTables = $module->getInfo('tables');
|
68: | if (empty($this->moduleTables)) {
|
69: | throw new \RuntimeException("No tables established in module");
|
70: | }
|
71: |
|
72: | $version = preg_replace_callback(
|
73: | '/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/',
|
74: | function ($match) {
|
75: | $semver = $match[1] . '_' . $match[2] . '_' .$match[3];
|
76: | if (!empty($match[4])) {
|
77: | $semver .= '_' . substr($match[4], 0, 8);
|
78: | }
|
79: | return $semver;
|
80: | },
|
81: | $module->getInfo('version'));
|
82: |
|
83: | $this->tableDefinitionFile = $this->helper->path("sql/{$dirname}_{$version}_migrate.yml");
|
84: | $this->tableHandler = new Tables();
|
85: | }
|
86: |
|
87: | |
88: | |
89: | |
90: | |
91: | |
92: | |
93: | |
94: | |
95: |
|
96: | public function saveCurrentSchema()
|
97: | {
|
98: | $this->tableHandler = new Tables();
|
99: |
|
100: | $schema = $this->getCurrentSchema();
|
101: |
|
102: | foreach ($schema as $tableName => $tableData) {
|
103: | unset($schema[$tableName]['name']);
|
104: | }
|
105: |
|
106: | return Yaml::save($schema, $this->tableDefinitionFile);
|
107: | }
|
108: |
|
109: | |
110: | |
111: | |
112: | |
113: |
|
114: | public function getCurrentSchema()
|
115: | {
|
116: | foreach ($this->moduleTables as $tableName) {
|
117: | if (false === $this->tableHandler->useTable($tableName)) {
|
118: | $this->tableHandler->addTable($tableName);
|
119: | }
|
120: | }
|
121: |
|
122: | return $this->tableHandler->dumpTables();
|
123: | }
|
124: |
|
125: | |
126: | |
127: | |
128: | |
129: | |
130: | |
131: |
|
132: | public function getTargetDefinitions()
|
133: | {
|
134: | if (!isset($this->targetDefinitions)) {
|
135: | $this->targetDefinitions = Yaml::read($this->tableDefinitionFile);
|
136: | if (null === $this->targetDefinitions) {
|
137: | throw new \RuntimeException("No schema definition " . $this->tableDefinitionFile);
|
138: | }
|
139: | }
|
140: | return $this->targetDefinitions;
|
141: | }
|
142: |
|
143: | |
144: | |
145: | |
146: | |
147: | |
148: | |
149: |
|
150: | public function synchronizeSchema($force = true)
|
151: | {
|
152: | $this->tableHandler = new Tables();
|
153: | $this->getSynchronizeDDL();
|
154: | return $this->tableHandler->executeQueue($force);
|
155: | }
|
156: |
|
157: | |
158: | |
159: | |
160: | |
161: |
|
162: | public function getSynchronizeDDL()
|
163: | {
|
164: | $this->getTargetDefinitions();
|
165: | $this->preSyncActions();
|
166: | foreach ($this->moduleTables as $tableName) {
|
167: | if ($this->tableHandler->useTable($tableName)) {
|
168: | $this->synchronizeTable($tableName);
|
169: | } else {
|
170: | $this->addMissingTable($tableName);
|
171: | }
|
172: | }
|
173: | return $this->tableHandler->dumpQueue();
|
174: | }
|
175: |
|
176: | |
177: | |
178: | |
179: | |
180: | |
181: | |
182: | |
183: | |
184: | |
185: | |
186: | |
187: | |
188: | |
189: | |
190: | |
191: | |
192: |
|
193: | protected function preSyncActions()
|
194: | {
|
195: | }
|
196: |
|
197: | |
198: | |
199: | |
200: | |
201: | |
202: | |
203: |
|
204: | protected function addMissingTable($tableName)
|
205: | {
|
206: | $this->tableHandler->addTable($tableName);
|
207: | $this->tableHandler->setTableOptions($tableName, $this->targetDefinitions[$tableName]['options']);
|
208: | foreach ($this->targetDefinitions[$tableName]['columns'] as $column) {
|
209: | $this->tableHandler->addColumn($tableName, $column['name'], $column['attributes']);
|
210: | }
|
211: | foreach ($this->targetDefinitions[$tableName]['keys'] as $key => $keyData) {
|
212: | if ($key === 'PRIMARY') {
|
213: | $this->tableHandler->addPrimaryKey($tableName, $keyData['columns']);
|
214: | } else {
|
215: | $this->tableHandler->addIndex($key, $tableName, $keyData['columns'], $keyData['unique']);
|
216: | }
|
217: | }
|
218: | }
|
219: |
|
220: | |
221: | |
222: | |
223: | |
224: | |
225: | |
226: |
|
227: | protected function synchronizeTable($tableName)
|
228: | {
|
229: | foreach ($this->targetDefinitions[$tableName]['columns'] as $column) {
|
230: | $attributes = $this->tableHandler->getColumnAttributes($tableName, $column['name']);
|
231: | if ($attributes === false) {
|
232: | $this->tableHandler->addColumn($tableName, $column['name'], $column['attributes']);
|
233: | } elseif ($column['attributes'] !== $attributes) {
|
234: | $this->tableHandler->alterColumn($tableName, $column['name'], $column['attributes']);
|
235: | }
|
236: | }
|
237: |
|
238: | $tableDef = $this->tableHandler->dumpTables();
|
239: | if (isset($tableDef[$tableName])) {
|
240: | foreach ($tableDef[$tableName]['columns'] as $columnData) {
|
241: | if (!$this->targetHasColumn($tableName, $columnData['name'])) {
|
242: | $this->tableHandler->dropColumn($tableName, $columnData['name']);
|
243: | }
|
244: | }
|
245: | }
|
246: |
|
247: | $existingIndexes = $this->tableHandler->getTableIndexes($tableName);
|
248: | if (isset($this->targetDefinitions[$tableName]['keys'])) {
|
249: | foreach ($this->targetDefinitions[$tableName]['keys'] as $key => $keyData) {
|
250: | if ($key === 'PRIMARY') {
|
251: | if (!isset($existingIndexes[$key])) {
|
252: | $this->tableHandler->addPrimaryKey($tableName, $keyData['columns']);
|
253: | } elseif ($existingIndexes[$key]['columns'] !== $keyData['columns']) {
|
254: | $this->tableHandler->dropPrimaryKey($tableName);
|
255: | $this->tableHandler->addPrimaryKey($tableName, $keyData['columns']);
|
256: | }
|
257: | } else {
|
258: | if (!isset($existingIndexes[$key])) {
|
259: | $this->tableHandler->addIndex($key, $tableName, $keyData['columns'], $keyData['unique']);
|
260: | } elseif ($existingIndexes[$key]['unique'] !== $keyData['unique']
|
261: | || $existingIndexes[$key]['columns'] !== $keyData['columns']
|
262: | ) {
|
263: | $this->tableHandler->dropIndex($key, $tableName);
|
264: | $this->tableHandler->addIndex($key, $tableName, $keyData['columns'], $keyData['unique']);
|
265: | }
|
266: | }
|
267: | }
|
268: | }
|
269: | if (false !== $existingIndexes) {
|
270: | foreach ($existingIndexes as $key => $keyData) {
|
271: | if (!isset($this->targetDefinitions[$tableName]['keys'][$key])) {
|
272: | $this->tableHandler->dropIndex($key, $tableName);
|
273: | }
|
274: | }
|
275: | }
|
276: | }
|
277: |
|
278: | |
279: | |
280: | |
281: | |
282: | |
283: | |
284: | |
285: |
|
286: | protected function targetHasColumn($tableName, $columnName)
|
287: | {
|
288: | if (isset($this->targetDefinitions[$tableName])) {
|
289: | foreach ($this->targetDefinitions[$tableName]['columns'] as $col) {
|
290: | if (strcasecmp($col['name'], $columnName) === 0) {
|
291: | return true;
|
292: | }
|
293: | }
|
294: | }
|
295: |
|
296: | return false;
|
297: | }
|
298: |
|
299: | |
300: | |
301: | |
302: | |
303: | |
304: | |
305: |
|
306: | protected function targetHasTable($tableName)
|
307: | {
|
308: | if (isset($this->targetDefinitions[$tableName])) {
|
309: | return true;
|
310: | }
|
311: | return false;
|
312: | }
|
313: |
|
314: | |
315: | |
316: | |
317: | |
318: |
|
319: | public function getLastError()
|
320: | {
|
321: | return $this->tableHandler->getLastError();
|
322: | }
|
323: |
|
324: | |
325: | |
326: | |
327: | |
328: |
|
329: | public function getLastErrNo()
|
330: | {
|
331: | return $this->tableHandler->getLastErrNo();
|
332: | }
|
333: | }
|
334: | |