~tsep-dev/tsep/0.9-beta

« back to all changes in this revision

Viewing changes to branches/symfony/cake/console/libs/schema.php

  • Committer: geoffreyfishing
  • Date: 2011-01-11 23:46:12 UTC
  • Revision ID: svn-v4:ae0de26e-ed09-4cbe-9a20-e40b4c60ac6c::125
Created a symfony branch for future migration to symfony

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Command-line database management utility to automate programmer chores.
 
4
 *
 
5
 * Schema is CakePHP's database management utility. This helps you maintain versions of
 
6
 * of your database.
 
7
 *
 
8
 * PHP versions 4 and 5
 
9
 *
 
10
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 
11
 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
 
12
 *
 
13
 * Licensed under The MIT License
 
14
 * Redistributions of files must retain the above copyright notice.
 
15
 *
 
16
 * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
 
17
 * @link          http://cakephp.org CakePHP(tm) Project
 
18
 * @package       cake
 
19
 * @subpackage    cake.cake.console.libs
 
20
 * @since         CakePHP(tm) v 1.2.0.5550
 
21
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 
22
 */
 
23
App::import('Core', 'File', false);
 
24
App::import('Model', 'CakeSchema', false);
 
25
 
 
26
/**
 
27
 * Schema is a command-line database management utility for automating programmer chores.
 
28
 *
 
29
 * @package       cake
 
30
 * @subpackage    cake.cake.console.libs
 
31
 * @link          http://book.cakephp.org/view/1523/Schema-management-and-migrations
 
32
 */
 
33
class SchemaShell extends Shell {
 
34
 
 
35
/**
 
36
 * is this a dry run?
 
37
 *
 
38
 * @var boolean
 
39
 * @access private
 
40
 */
 
41
        var $__dry = null;
 
42
 
 
43
/**
 
44
 * Override initialize
 
45
 *
 
46
 * @access public
 
47
 */
 
48
        function initialize() {
 
49
                $this->_welcome();
 
50
                $this->out('Cake Schema Shell');
 
51
                $this->hr();
 
52
        }
 
53
 
 
54
/**
 
55
 * Override startup
 
56
 *
 
57
 * @access public
 
58
 */
 
59
        function startup() {
 
60
                $name = $file = $path = $connection = $plugin = null;
 
61
                if (!empty($this->params['name'])) {
 
62
                        $name = $this->params['name'];
 
63
                } elseif (!empty($this->args[0])) {
 
64
                        $name = $this->params['name'] = $this->args[0];
 
65
                }
 
66
 
 
67
                if (strpos($name, '.')) {
 
68
                        list($this->params['plugin'], $splitName) = pluginSplit($name);
 
69
                        $name = $this->params['name'] = $splitName;
 
70
                }
 
71
 
 
72
                if ($name) {
 
73
                        $this->params['file'] = Inflector::underscore($name);
 
74
                }
 
75
 
 
76
                if (empty($this->params['file'])) {
 
77
                        $this->params['file'] = 'schema.php';
 
78
                }
 
79
                if (strpos($this->params['file'], '.php') === false) {
 
80
                        $this->params['file'] .= '.php';
 
81
                }
 
82
                $file = $this->params['file'];
 
83
 
 
84
                if (!empty($this->params['path'])) {
 
85
                        $path = $this->params['path'];
 
86
                }
 
87
 
 
88
                if (!empty($this->params['connection'])) {
 
89
                        $connection = $this->params['connection'];
 
90
                }
 
91
                if (!empty($this->params['plugin'])) {
 
92
                        $plugin = $this->params['plugin'];
 
93
                        if (empty($name)) {
 
94
                                $name = $plugin;
 
95
                        }
 
96
                }
 
97
                $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin'));
 
98
        }
 
99
 
 
100
/**
 
101
 * Override main
 
102
 *
 
103
 * @access public
 
104
 */
 
105
        function main() {
 
106
                $this->help();
 
107
        }
 
108
 
 
109
/**
 
110
 * Read and output contents of schema object
 
111
 * path to read as second arg
 
112
 *
 
113
 * @access public
 
114
 */
 
115
        function view() {
 
116
                $File = new File($this->Schema->path . DS . $this->params['file']);
 
117
                if ($File->exists()) {
 
118
                        $this->out($File->read());
 
119
                        $this->_stop();
 
120
                } else {
 
121
                        $file = $this->Schema->path . DS . $this->params['file'];
 
122
                        $this->err(sprintf(__('Schema file (%s) could not be found.', true), $file));
 
123
                        $this->_stop();
 
124
                }
 
125
        }
 
126
 
 
127
/**
 
128
 * Read database and Write schema object
 
129
 * accepts a connection as first arg or path to save as second arg
 
130
 *
 
131
 * @access public
 
132
 */
 
133
        function generate() {
 
134
                $this->out(__('Generating Schema...', true));
 
135
                $options = array();
 
136
                if (isset($this->params['f'])) {
 
137
                        $options = array('models' => false);
 
138
                }
 
139
 
 
140
                $snapshot = false;
 
141
                if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
 
142
                        $snapshot = true;
 
143
                }
 
144
 
 
145
                if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
 
146
                        $snapshot = true;
 
147
                        $result = strtolower($this->in("Schema file exists.\n [O]verwrite\n [S]napshot\n [Q]uit\nWould you like to do?", array('o', 's', 'q'), 's'));
 
148
                        if ($result === 'q') {
 
149
                                return $this->_stop();
 
150
                        }
 
151
                        if ($result === 'o') {
 
152
                                $snapshot = false;
 
153
                        }
 
154
                }
 
155
 
 
156
                $content = $this->Schema->read($options);
 
157
                $content['file'] = $this->params['file'];
 
158
 
 
159
                if ($snapshot === true) {
 
160
                        $Folder =& new Folder($this->Schema->path);
 
161
                        $result = $Folder->read();
 
162
 
 
163
                        $numToUse = false;
 
164
                        if (isset($this->params['s'])) {
 
165
                                $numToUse = $this->params['s'];
 
166
                        }
 
167
 
 
168
                        $count = 1;
 
169
                        if (!empty($result[1])) {
 
170
                                foreach ($result[1] as $file) {
 
171
                                        if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) {
 
172
                                                $count++;
 
173
                                        }
 
174
                                }
 
175
                        }
 
176
 
 
177
                        if ($numToUse !== false) {
 
178
                                if ($numToUse > $count) {
 
179
                                        $count = $numToUse;
 
180
                                }
 
181
                        }
 
182
 
 
183
                        $fileName = rtrim($this->params['file'], '.php');
 
184
                        $content['file'] = $fileName . '_' . $count . '.php';
 
185
                }
 
186
 
 
187
                if ($this->Schema->write($content)) {
 
188
                        $this->out(sprintf(__('Schema file: %s generated', true), $content['file']));
 
189
                        $this->_stop();
 
190
                } else {
 
191
                        $this->err(__('Schema file: %s generated', true));
 
192
                        $this->_stop();
 
193
                }
 
194
        }
 
195
 
 
196
/**
 
197
 * Dump Schema object to sql file
 
198
 * Use the `write` param to enable and control SQL file output location.
 
199
 * Simply using -write will write the sql file to the same dir as the schema file.
 
200
 * If -write contains a full path name the file will be saved there. If -write only
 
201
 * contains no DS, that will be used as the file name, in the same dir as the schema file.
 
202
 *
 
203
 * @access public
 
204
 */
 
205
        function dump() {
 
206
                $write = false;
 
207
                $Schema = $this->Schema->load();
 
208
                if (!$Schema) {
 
209
                        $this->err(__('Schema could not be loaded', true));
 
210
                        $this->_stop();
 
211
                }
 
212
                if (isset($this->params['write'])) {
 
213
                        if ($this->params['write'] == 1) {
 
214
                                $write = Inflector::underscore($this->Schema->name);
 
215
                        } else {
 
216
                                $write = $this->params['write'];
 
217
                        }
 
218
                }
 
219
                $db =& ConnectionManager::getDataSource($this->Schema->connection);
 
220
                $contents = "#" . $Schema->name . " sql generated on: " . date('Y-m-d H:i:s') . " : " . time() . "\n\n";
 
221
                $contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema);
 
222
 
 
223
                if ($write) {
 
224
                        if (strpos($write, '.sql') === false) {
 
225
                                $write .= '.sql';
 
226
                        }
 
227
                        if (strpos($write, DS) !== false) {
 
228
                                $File =& new File($write, true);
 
229
                        } else {
 
230
                                $File =& new File($this->Schema->path . DS . $write, true);
 
231
                        }
 
232
 
 
233
                        if ($File->write($contents)) {
 
234
                                $this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
 
235
                                $this->_stop();
 
236
                        } else {
 
237
                                $this->err(__('SQL dump could not be created', true));
 
238
                                $this->_stop();
 
239
                        }
 
240
                }
 
241
                $this->out($contents);
 
242
                return $contents;
 
243
        }
 
244
 
 
245
/**
 
246
 * Run database create commands.  Alias for run create.
 
247
 *
 
248
 * @return void
 
249
 */
 
250
        function create() {
 
251
                list($Schema, $table) = $this->_loadSchema();
 
252
                $this->__create($Schema, $table);
 
253
        }
 
254
 
 
255
/**
 
256
 * Run database create commands.  Alias for run create.
 
257
 *
 
258
 * @return void
 
259
 */
 
260
        function update() {
 
261
                list($Schema, $table) = $this->_loadSchema();
 
262
                $this->__update($Schema, $table);
 
263
        }
 
264
 
 
265
/**
 
266
 * Prepares the Schema objects for database operations.
 
267
 *
 
268
 * @return void
 
269
 */
 
270
        function _loadSchema() {
 
271
                $name = $plugin = null;
 
272
                if (isset($this->params['name'])) {
 
273
                        $name = $this->params['name'];
 
274
                }
 
275
                if (isset($this->params['plugin'])) {
 
276
                        $plugin = $this->params['plugin'];
 
277
                }
 
278
                
 
279
                if (isset($this->params['dry'])) {
 
280
                        $this->__dry = true;
 
281
                        $this->out(__('Performing a dry run.', true));
 
282
                }
 
283
 
 
284
                $options = array('name' => $name, 'plugin' => $plugin);
 
285
                if (isset($this->params['s'])) {
 
286
                        $fileName = rtrim($this->Schema->file, '.php');
 
287
                        $options['file'] = $fileName . '_' . $this->params['s'] . '.php';
 
288
                }
 
289
 
 
290
                $Schema =& $this->Schema->load($options);
 
291
 
 
292
                if (!$Schema) {
 
293
                        $this->err(sprintf(__('%s could not be loaded', true), $this->Schema->path . DS . $this->Schema->file));
 
294
                        $this->_stop();
 
295
                }
 
296
                $table = null;
 
297
                if (isset($this->args[1])) {
 
298
                        $table = $this->args[1];
 
299
                }
 
300
                return array(&$Schema, $table);
 
301
        }
 
302
 
 
303
/**
 
304
 * Create database from Schema object
 
305
 * Should be called via the run method
 
306
 *
 
307
 * @access private
 
308
 */
 
309
        function __create(&$Schema, $table = null) {
 
310
                $db =& ConnectionManager::getDataSource($this->Schema->connection);
 
311
 
 
312
                $drop = $create = array();
 
313
 
 
314
                if (!$table) {
 
315
                        foreach ($Schema->tables as $table => $fields) {
 
316
                                $drop[$table] = $db->dropSchema($Schema, $table);
 
317
                                $create[$table] = $db->createSchema($Schema, $table);
 
318
                        }
 
319
                } elseif (isset($Schema->tables[$table])) {
 
320
                        $drop[$table] = $db->dropSchema($Schema, $table);
 
321
                        $create[$table] = $db->createSchema($Schema, $table);
 
322
                }
 
323
                if (empty($drop) || empty($create)) {
 
324
                        $this->out(__('Schema is up to date.', true));
 
325
                        $this->_stop();
 
326
                }
 
327
 
 
328
                $this->out("\n" . __('The following table(s) will be dropped.', true));
 
329
                $this->out(array_keys($drop));
 
330
 
 
331
                if ('y' == $this->in(__('Are you sure you want to drop the table(s)?', true), array('y', 'n'), 'n')) {
 
332
                        $this->out(__('Dropping table(s).', true));
 
333
                        $this->__run($drop, 'drop', $Schema);
 
334
                }
 
335
 
 
336
                $this->out("\n" . __('The following table(s) will be created.', true));
 
337
                $this->out(array_keys($create));
 
338
 
 
339
                if ('y' == $this->in(__('Are you sure you want to create the table(s)?', true), array('y', 'n'), 'y')) {
 
340
                        $this->out(__('Creating table(s).', true));
 
341
                        $this->__run($create, 'create', $Schema);
 
342
                }
 
343
                $this->out(__('End create.', true));
 
344
        }
 
345
 
 
346
/**
 
347
 * Update database with Schema object
 
348
 * Should be called via the run method
 
349
 *
 
350
 * @access private
 
351
 */
 
352
        function __update(&$Schema, $table = null) {
 
353
                $db =& ConnectionManager::getDataSource($this->Schema->connection);
 
354
 
 
355
                $this->out(__('Comparing Database to Schema...', true));
 
356
                $options = array();
 
357
                if (isset($this->params['f'])) {
 
358
                        $options['models'] = false;
 
359
                }
 
360
                $Old = $this->Schema->read($options);
 
361
                $compare = $this->Schema->compare($Old, $Schema);
 
362
 
 
363
                $contents = array();
 
364
 
 
365
                if (empty($table)) {
 
366
                        foreach ($compare as $table => $changes) {
 
367
                                $contents[$table] = $db->alterSchema(array($table => $changes), $table);
 
368
                        }
 
369
                } elseif (isset($compare[$table])) {
 
370
                        $contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
 
371
                }
 
372
 
 
373
                if (empty($contents)) {
 
374
                        $this->out(__('Schema is up to date.', true));
 
375
                        $this->_stop();
 
376
                }
 
377
 
 
378
                $this->out("\n" . __('The following statements will run.', true));
 
379
                $this->out(array_map('trim', $contents));
 
380
                if ('y' == $this->in(__('Are you sure you want to alter the tables?', true), array('y', 'n'), 'n')) {
 
381
                        $this->out();
 
382
                        $this->out(__('Updating Database...', true));
 
383
                        $this->__run($contents, 'update', $Schema);
 
384
                }
 
385
 
 
386
                $this->out(__('End update.', true));
 
387
        }
 
388
 
 
389
/**
 
390
 * Runs sql from __create() or __update()
 
391
 *
 
392
 * @access private
 
393
 */
 
394
        function __run($contents, $event, &$Schema) {
 
395
                if (empty($contents)) {
 
396
                        $this->err(__('Sql could not be run', true));
 
397
                        return;
 
398
                }
 
399
                Configure::write('debug', 2);
 
400
                $db =& ConnectionManager::getDataSource($this->Schema->connection);
 
401
 
 
402
                foreach ($contents as $table => $sql) {
 
403
                        if (empty($sql)) {
 
404
                                $this->out(sprintf(__('%s is up to date.', true), $table));
 
405
                        } else {
 
406
                                if ($this->__dry === true) {
 
407
                                        $this->out(sprintf(__('Dry run for %s :', true), $table));
 
408
                                        $this->out($sql);
 
409
                                } else {
 
410
                                        if (!$Schema->before(array($event => $table))) {
 
411
                                                return false;
 
412
                                        }
 
413
                                        $error = null;
 
414
                                        if (!$db->execute($sql)) {
 
415
                                                $error = $table . ': '  . $db->lastError();
 
416
                                        }
 
417
 
 
418
                                        $Schema->after(array($event => $table, 'errors' => $error));
 
419
 
 
420
                                        if (!empty($error)) {
 
421
                                                $this->out($error);
 
422
                                        } else {
 
423
                                                $this->out(sprintf(__('%s updated.', true), $table));
 
424
                                        }
 
425
                                }
 
426
                        }
 
427
                }
 
428
        }
 
429
 
 
430
/**
 
431
 * Displays help contents
 
432
 *
 
433
 * @access public
 
434
 */
 
435
        function help() {
 
436
                $help = <<<TEXT
 
437
The Schema Shell generates a schema object from
 
438
the database and updates the database from the schema.
 
439
---------------------------------------------------------------
 
440
Usage: cake schema <command> <arg1> <arg2>...
 
441
---------------------------------------------------------------
 
442
Params:
 
443
        -connection <config>
 
444
                set db config <config>. uses 'default' if none is specified
 
445
 
 
446
        -path <dir>
 
447
                path <dir> to read and write schema.php.
 
448
                default path: {$this->Schema->path}
 
449
 
 
450
        -name <name>
 
451
                Classname to use. If <name> is Plugin.className, it will
 
452
                set the plugin and name params.
 
453
 
 
454
        -file <name>
 
455
                file <name> to read and write.
 
456
                default file: {$this->Schema->file}
 
457
 
 
458
        -s <number>
 
459
                snapshot <number> to use for run.
 
460
 
 
461
        -dry
 
462
                Perform a dry run on create + update commands.
 
463
                Queries will be output to window instead of executed.
 
464
 
 
465
        -f
 
466
                force 'generate' to create a new schema.
 
467
 
 
468
        -plugin
 
469
                Indicate the plugin to use.
 
470
 
 
471
Commands:
 
472
 
 
473
        schema help
 
474
                shows this help message.
 
475
 
 
476
        schema view <name>
 
477
                read and output contents of schema file.
 
478
 
 
479
        schema generate
 
480
                reads from 'connection' writes to 'path'
 
481
                To force generation of all tables into the schema, use the -f param.
 
482
                Use 'schema generate snapshot <number>' to generate snapshots
 
483
                which you can use with the -s parameter in the other operations.
 
484
 
 
485
        schema dump <name>
 
486
                Dump database sql based on schema file to stdout.
 
487
                If you use the `-write` param is used a .sql will be generated.
 
488
                If `-write` is a filename, then that file name will be generate.
 
489
                If `-write` is a full path, the schema will be written there.
 
490
 
 
491
        schema create <name> <table>
 
492
                Drop and create tables based on schema file
 
493
                optional <table> argument can be used to create only a single 
 
494
                table in the schema. Pass the -s param with a number to use a snapshot.
 
495
                Use the `-dry` param to preview the changes.
 
496
 
 
497
        schema update <name> <table>
 
498
                Alter the tables based on schema file. Optional <table>
 
499
                parameter will only update one table. 
 
500
                To use a snapshot pass the `-s` param with the snapshot number.
 
501
                To preview the changes that will be done use `-dry`.
 
502
TEXT;
 
503
                $this->out($help);
 
504
                $this->_stop();
 
505
        }
 
506
}