3
* Command-line database management utility to automate programmer chores.
5
* Schema is CakePHP's database management utility. This helps you maintain versions of
10
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
11
* Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
13
* Licensed under The MIT License
14
* Redistributions of files must retain the above copyright notice.
16
* @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
17
* @link http://cakephp.org CakePHP(tm) Project
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)
23
App::import('Core', 'File', false);
24
App::import('Model', 'CakeSchema', false);
27
* Schema is a command-line database management utility for automating programmer chores.
30
* @subpackage cake.cake.console.libs
31
* @link http://book.cakephp.org/view/1523/Schema-management-and-migrations
33
class SchemaShell extends Shell {
48
function initialize() {
50
$this->out('Cake Schema Shell');
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];
67
if (strpos($name, '.')) {
68
list($this->params['plugin'], $splitName) = pluginSplit($name);
69
$name = $this->params['name'] = $splitName;
73
$this->params['file'] = Inflector::underscore($name);
76
if (empty($this->params['file'])) {
77
$this->params['file'] = 'schema.php';
79
if (strpos($this->params['file'], '.php') === false) {
80
$this->params['file'] .= '.php';
82
$file = $this->params['file'];
84
if (!empty($this->params['path'])) {
85
$path = $this->params['path'];
88
if (!empty($this->params['connection'])) {
89
$connection = $this->params['connection'];
91
if (!empty($this->params['plugin'])) {
92
$plugin = $this->params['plugin'];
97
$this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin'));
110
* Read and output contents of schema object
111
* path to read as second arg
116
$File = new File($this->Schema->path . DS . $this->params['file']);
117
if ($File->exists()) {
118
$this->out($File->read());
121
$file = $this->Schema->path . DS . $this->params['file'];
122
$this->err(sprintf(__('Schema file (%s) could not be found.', true), $file));
128
* Read database and Write schema object
129
* accepts a connection as first arg or path to save as second arg
133
function generate() {
134
$this->out(__('Generating Schema...', true));
136
if (isset($this->params['f'])) {
137
$options = array('models' => false);
141
if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
145
if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
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();
151
if ($result === 'o') {
156
$content = $this->Schema->read($options);
157
$content['file'] = $this->params['file'];
159
if ($snapshot === true) {
160
$Folder =& new Folder($this->Schema->path);
161
$result = $Folder->read();
164
if (isset($this->params['s'])) {
165
$numToUse = $this->params['s'];
169
if (!empty($result[1])) {
170
foreach ($result[1] as $file) {
171
if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) {
177
if ($numToUse !== false) {
178
if ($numToUse > $count) {
183
$fileName = rtrim($this->params['file'], '.php');
184
$content['file'] = $fileName . '_' . $count . '.php';
187
if ($this->Schema->write($content)) {
188
$this->out(sprintf(__('Schema file: %s generated', true), $content['file']));
191
$this->err(__('Schema file: %s generated', true));
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.
207
$Schema = $this->Schema->load();
209
$this->err(__('Schema could not be loaded', true));
212
if (isset($this->params['write'])) {
213
if ($this->params['write'] == 1) {
214
$write = Inflector::underscore($this->Schema->name);
216
$write = $this->params['write'];
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);
224
if (strpos($write, '.sql') === false) {
227
if (strpos($write, DS) !== false) {
228
$File =& new File($write, true);
230
$File =& new File($this->Schema->path . DS . $write, true);
233
if ($File->write($contents)) {
234
$this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
237
$this->err(__('SQL dump could not be created', true));
241
$this->out($contents);
246
* Run database create commands. Alias for run create.
251
list($Schema, $table) = $this->_loadSchema();
252
$this->__create($Schema, $table);
256
* Run database create commands. Alias for run create.
261
list($Schema, $table) = $this->_loadSchema();
262
$this->__update($Schema, $table);
266
* Prepares the Schema objects for database operations.
270
function _loadSchema() {
271
$name = $plugin = null;
272
if (isset($this->params['name'])) {
273
$name = $this->params['name'];
275
if (isset($this->params['plugin'])) {
276
$plugin = $this->params['plugin'];
279
if (isset($this->params['dry'])) {
281
$this->out(__('Performing a dry run.', true));
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';
290
$Schema =& $this->Schema->load($options);
293
$this->err(sprintf(__('%s could not be loaded', true), $this->Schema->path . DS . $this->Schema->file));
297
if (isset($this->args[1])) {
298
$table = $this->args[1];
300
return array(&$Schema, $table);
304
* Create database from Schema object
305
* Should be called via the run method
309
function __create(&$Schema, $table = null) {
310
$db =& ConnectionManager::getDataSource($this->Schema->connection);
312
$drop = $create = array();
315
foreach ($Schema->tables as $table => $fields) {
316
$drop[$table] = $db->dropSchema($Schema, $table);
317
$create[$table] = $db->createSchema($Schema, $table);
319
} elseif (isset($Schema->tables[$table])) {
320
$drop[$table] = $db->dropSchema($Schema, $table);
321
$create[$table] = $db->createSchema($Schema, $table);
323
if (empty($drop) || empty($create)) {
324
$this->out(__('Schema is up to date.', true));
328
$this->out("\n" . __('The following table(s) will be dropped.', true));
329
$this->out(array_keys($drop));
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);
336
$this->out("\n" . __('The following table(s) will be created.', true));
337
$this->out(array_keys($create));
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);
343
$this->out(__('End create.', true));
347
* Update database with Schema object
348
* Should be called via the run method
352
function __update(&$Schema, $table = null) {
353
$db =& ConnectionManager::getDataSource($this->Schema->connection);
355
$this->out(__('Comparing Database to Schema...', true));
357
if (isset($this->params['f'])) {
358
$options['models'] = false;
360
$Old = $this->Schema->read($options);
361
$compare = $this->Schema->compare($Old, $Schema);
366
foreach ($compare as $table => $changes) {
367
$contents[$table] = $db->alterSchema(array($table => $changes), $table);
369
} elseif (isset($compare[$table])) {
370
$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
373
if (empty($contents)) {
374
$this->out(__('Schema is up to date.', true));
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')) {
382
$this->out(__('Updating Database...', true));
383
$this->__run($contents, 'update', $Schema);
386
$this->out(__('End update.', true));
390
* Runs sql from __create() or __update()
394
function __run($contents, $event, &$Schema) {
395
if (empty($contents)) {
396
$this->err(__('Sql could not be run', true));
399
Configure::write('debug', 2);
400
$db =& ConnectionManager::getDataSource($this->Schema->connection);
402
foreach ($contents as $table => $sql) {
404
$this->out(sprintf(__('%s is up to date.', true), $table));
406
if ($this->__dry === true) {
407
$this->out(sprintf(__('Dry run for %s :', true), $table));
410
if (!$Schema->before(array($event => $table))) {
414
if (!$db->execute($sql)) {
415
$error = $table . ': ' . $db->lastError();
418
$Schema->after(array($event => $table, 'errors' => $error));
420
if (!empty($error)) {
423
$this->out(sprintf(__('%s updated.', true), $table));
431
* Displays help contents
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
---------------------------------------------------------------
444
set db config <config>. uses 'default' if none is specified
447
path <dir> to read and write schema.php.
448
default path: {$this->Schema->path}
451
Classname to use. If <name> is Plugin.className, it will
452
set the plugin and name params.
455
file <name> to read and write.
456
default file: {$this->Schema->file}
459
snapshot <number> to use for run.
462
Perform a dry run on create + update commands.
463
Queries will be output to window instead of executed.
466
force 'generate' to create a new schema.
469
Indicate the plugin to use.
474
shows this help message.
477
read and output contents of schema file.
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.
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.
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.
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`.