1
+ <?php
2
+
3
+ /*
4
+ * Based on:
5
+ * http://bakery.cakephp.org/articles/erma/2010/01/05/cakephp-sql-shell-simple-and-powerful
6
+ /
7
+ */
8
+
9
+ App::uses ('Folder ' , 'Utility ' );
10
+
11
+ App::uses ('ConnectionManager ' , 'Model ' );
12
+ App::uses ('SchemaVersion ' , 'SqlMigration.Model ' );
13
+
14
+ class SqlMigrationShell extends Shell {
15
+
16
+ /**
17
+ * Connection used
18
+ *
19
+ * @var string
20
+ */
21
+ private $ connection = 'default ' ;
22
+
23
+ /**
24
+ * This plugin's name
25
+ *
26
+ * @var string
27
+ */
28
+ private $ myName = 'SqlMigration ' ;
29
+
30
+ /**
31
+ * Table holding schema version
32
+ *
33
+ * @var string
34
+ */
35
+ private $ tableName = 'schema_versions ' ;
36
+
37
+
38
+ /**
39
+ * Sucess status
40
+ *
41
+ */
42
+ private $ successStatus = 'STATUS ' ;
43
+
44
+ /**
45
+ * Skipped status
46
+ *
47
+ */
48
+ private $ skippedStatus = 'SKIPPED ' ;
49
+
50
+ /**
51
+ * Data model
52
+ *
53
+ */
54
+ private $ schemaVersionModel ;
55
+
56
+ /*
57
+ * Overridding this method will prevent welcome message
58
+ */
59
+ public function _welcome () {
60
+ $ this ->out ('SQL Migration plugin ' );
61
+ }
62
+
63
+
64
+ /*
65
+ * Function called if no other command is psecified
66
+ */
67
+ public function main () {
68
+
69
+ $ this ->out ('SqlMigration shell ' );
70
+
71
+ $ this ->schemaVersionModel = new SchemaVersion ();
72
+
73
+ //$this->checkMigrationShema();
74
+ $ this ->out ('Executing migration ' );
75
+ //$this->executeMigration();
76
+ //$v = $this->getVersion();
77
+ $ this ->update ();
78
+
79
+ } // End function main()
80
+
81
+ /**
82
+ * Get latest version
83
+ * @return int Latest version number
84
+ */
85
+ private function getVersion () {
86
+ $ latest = $ this ->schemaVersionModel ->find ('first ' , array (
87
+ 'order ' => array ('created DESC ' ),
88
+ 'limit ' => 1
89
+ ));
90
+ if ( $ latest && is_numeric ($ latest ['SchemaVersion ' ]['version ' ]) ) {
91
+ return (int )$ latest ['SchemaVersion ' ]['version ' ];
92
+ }
93
+ else {
94
+ $ this ->out ('No version found. Assuming 0. ' );
95
+ return 0 ;
96
+ }
97
+ } // End function getVersion()
98
+
99
+ /**
100
+ * Get all version history
101
+ * @return int Latest version number
102
+ */
103
+ private function getAllVersions () {
104
+ $ all = $ this ->schemaVersionModel ->find ('all ' , array (
105
+ 'order ' => array ('created ASC ' )
106
+ ));
107
+ return $ all ;
108
+ } // End function getAllVersions()
109
+
110
+ /**
111
+ * Set version
112
+ * Create new verision if doesn't exists, update if existing.
113
+ * @param int $version Version
114
+ * @param String $status Status of upgrade to version
115
+ */
116
+ private function setVersion ($ version , $ status ) {
117
+ $ existingVersion = $ this ->schemaVersionModel ->findByVersion ($ version );
118
+ if ( $ existingVersion ) {
119
+ $ this ->schemaVersionModel ->id = $ existingVersion ['SchemaVersion ' ]['id ' ];
120
+ $ this ->schemaVersionModel ->saveField ('status ' , $ status );
121
+ }
122
+ else {
123
+ $ data = array ('SchemaVersion ' => array (
124
+ 'version ' => $ version ,
125
+ 'status ' => $ status ));
126
+ $ this ->schemaVersionModel ->create ();
127
+ $ saved = $ this ->schemaVersionModel ->save ($ data );
128
+ if ( !$ saved ) {
129
+ $ this ->out ('Unable to set version ' );
130
+ $ this ->_stop ();
131
+ }
132
+ }
133
+ } // End function setVersion()
134
+
135
+
136
+ /**
137
+ * Get SQL to run (from file) for a given version
138
+ * @param int $verion Version number
139
+ * @return String SQL to run
140
+ */
141
+ private function getSql ($ version ) {
142
+ if (($ text = file_get_contents ($ filename = APP .'Config/Sql/upgrade- ' .$ version .'.sql ' )) !== false ) {
143
+ return $ text ;
144
+ } else {
145
+ $ this ->out ("Couldn't load contents of file {$ filename }, unable to uograde/downgrade " );
146
+ $ this ->_stop ();
147
+ }
148
+ } // End function getSql()
149
+
150
+
151
+ /**
152
+ * Run the update.
153
+ * This will try to run all upgrade SQL file in order of version.
154
+ * It wil also try to run./re-run any version that might have been
155
+ * skipped previously
156
+ */
157
+ private function update () {
158
+ $ sqlFolder = new Folder (APP .'Config/Sql ' );
159
+ list ($ dirs , $ files ) = $ sqlFolder ->read ();
160
+ $ upgrades = array ();
161
+ foreach ($ files as $ i => $ file ) {
162
+ if (preg_match ( '/upgrade-(\d+)\.sql$/ ' , $ file , $ matches )) {
163
+ //unset($files[$i]);
164
+ // $this->out($matches[1]);
165
+ $ upgrades [(int )$ matches [1 ]] = $ file ;
166
+ }
167
+ }
168
+ ksort ($ upgrades );
169
+ $ version = max (array_keys ($ upgrades ));
170
+ $ this ->out ('Upgrading up to version : ' .$ version );
171
+
172
+ $ allVersions = $ this ->getAllVersions ();
173
+ // $this->out(print_r($allVersions));
174
+
175
+ // Try to run missing/skipped versions
176
+ $ this ->out ('Looking for missing versions ' );
177
+ foreach ($ allVersions as $ v ) {
178
+ if ( $ v ['SchemaVersion ' ]['status ' ] === $ this ->skippedStatus && isset ($ upgrades [$ v ['SchemaVersion ' ]['version ' ]]) ) {
179
+ $ this ->out ('Running skipped version: ' . $ upgrades [$ v ['SchemaVersion ' ]['version ' ]]);
180
+ if ( !$ this ->executeSql ($ v ['SchemaVersion ' ]['version ' ]) ) {
181
+ break ;
182
+ }
183
+ }
184
+ }
185
+ // Run upgrades up to the highest/latest verion of the upgrade files found
186
+ for ($ currentVersion = $ this ->getVersion (); $ currentVersion < $ version ; $ currentVersion = $ this ->getVersion ()) {
187
+ $ this ->out ('Currently at Version ' .$ currentVersion );
188
+ $ this ->out ('Updating to Version ' .($ currentVersion +1 ));
189
+ if ( !isset ($ upgrades [$ currentVersion +1 ]) ) {
190
+ $ this ->out ('No upgrade file for version ' .($ currentVersion +1 ).'. Skipping ' );
191
+ $ this ->setVersion ((int )($ currentVersion +1 ), $ this ->skippedStatus );
192
+ continue ;
193
+ }
194
+ if ( !$ this ->executeSql ($ currentVersion +1 ) ) {
195
+ break ;
196
+ }
197
+ }
198
+ $ this ->out ('Now at version ' .$ this ->getVersion ());
199
+ } // End function update
200
+
201
+ /**
202
+ * Execute SQL file for a given version
203
+ * @param int $version Version to execute
204
+ * @return boolean False if user choose to not run the SQL. 'Skip' will return true
205
+ */
206
+ private function executeSql ($ version ) {
207
+ $ this ->out ('Executing sql: ' );
208
+ $ this ->hr ();
209
+ $ this ->out ($ sql = $ this ->getSql ($ version ));
210
+ $ this ->hr ();
211
+ $ a = $ this ->in ('Execute SQL? [y/n/s] ' );
212
+ if ( $ a === 'n ' ) {
213
+ return false ;
214
+ }
215
+ else if ( $ a === 's ' ) {
216
+ return true ;
217
+ } else {
218
+ $ this ->out ('Launching MySQL to execute SQL ' );
219
+ $ database = ConnectionManager::getDataSource ('default ' )->config ;
220
+ $ sql_file = APP .'Config/Sql/upgrade- ' .$ version .'.sql ' ;
221
+ exec ("mysql --host= $ {database['host ' ]} --user= $ {database['login ' ]} --password= $ {database['password ' ]} --database= $ {database['database ' ]} < $ {sql_file}" );
222
+ $ this ->setVersion ((int )($ version ), $ this ->successStatus );
223
+ }
224
+ return true ;
225
+ } // End function executeSql()
226
+
227
+
228
+ /**
229
+ * Check if the appropriate database exists for the plugin
230
+ * @return [type] [description]
231
+ */
232
+ public function setup () {
233
+
234
+ $ ds = ConnectionManager::getDataSource ($ this ->connection );
235
+ $ result = $ ds ->execute ("SHOW TABLES LIKE ' " .$ this ->tableName ."' " )->fetch ();
236
+ if ( empty ($ result ) ) {
237
+ $ this ->out ('Looks like this plugin was never used. Creating table needed ' );
238
+ $ this ->createMigrationSchema ();
239
+ }
240
+ else {
241
+ $ this ->out ('Updating database table needed by plugin ' );
242
+ $ this ->updateMigrationSchema ();
243
+ }
244
+
245
+ } // End function checkMigrationSchema()
246
+
247
+
248
+ /**
249
+ * Update the database table for the plugin
250
+ */
251
+ private function updateMigrationSchema () {
252
+
253
+ // Command to run
254
+ $ command = 'schema update --quiet --plugin ' .$ this ->myName .' --connection ' . $ this ->connection ;
255
+ // Dispatch to shell
256
+ $ this ->dispatchShell ($ command );
257
+ $ this ->out ('Updated ' );
258
+
259
+ } // End function updateMigrationchema()
260
+
261
+
262
+ /**
263
+ * Create database table needed by plugin
264
+ */
265
+ private function createMigrationSchema () {
266
+
267
+ // Command to run
268
+ $ command = 'schema create --quiet --plugin ' .$ this ->myName .' --connection ' . $ this ->connection ;
269
+ // Dispatch to shell
270
+ $ this ->dispatchShell ($ command );
271
+ $ this ->out ('Created ' );
272
+
273
+ } // End function createMigrationchema()
274
+
275
+ }
276
+ ?>
0 commit comments