schema.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: schema.php 8260 2009-07-28 20:01:42Z DarkAngelBGE $ */
00003 /**
00004  * Command-line database management utility to automate programmer chores.
00005  *
00006  * Schema is CakePHP's database management utility. This helps you maintain versions of
00007  * of your database.
00008  *
00009  * PHP versions 4 and 5
00010  *
00011  * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
00012  * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00013  *
00014  * Licensed under The MIT License
00015  * Redistributions of files must retain the above copyright notice.
00016  *
00017  * @filesource
00018  * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00019  * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00020  * @package       cake
00021  * @subpackage    cake.cake.console.libs
00022  * @since         CakePHP(tm) v 1.2.0.5550
00023  * @version       $Revision: 8260 $
00024  * @modifiedby    $LastChangedBy: DarkAngelBGE $
00025  * @lastmodified  $Date: 2009-07-28 16:01:42 -0400 (Tue, 28 Jul 2009) $
00026  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
00027  */
00028 App::import('File');
00029 App::import('Model', 'Schema');
00030 /**
00031  * Schema is a command-line database management utility for automating programmer chores.
00032  *
00033  * @package       cake
00034  * @subpackage    cake.cake.console.libs
00035  * @link          http://book.cakephp.org/view/734/Schema-management-and-migrations
00036  */
00037 class SchemaShell extends Shell {
00038 /**
00039  * is this a dry run?
00040  *
00041  * @var boolean
00042  * @access private
00043  */
00044     var $__dry = null;
00045 /**
00046  * Override initialize
00047  *
00048  * @access public
00049  */
00050     function initialize() {
00051         $this->_welcome();
00052         $this->out('Cake Schema Shell');
00053         $this->hr();
00054     }
00055 /**
00056  * Override startup
00057  *
00058  * @access public
00059  */
00060     function startup() {
00061         $name = null;
00062         if (!empty($this->params['name'])) {
00063             $name = $this->params['name'];
00064             $this->params['file'] = Inflector::underscore($name);
00065         }
00066 
00067         $path = null;
00068         if (!empty($this->params['path'])) {
00069             $path = $this->params['path'];
00070         }
00071 
00072         $file = null;
00073         if (empty($this->params['file'])) {
00074             $this->params['file'] = 'schema.php';
00075         }
00076         if (strpos($this->params['file'], '.php') === false) {
00077             $this->params['file'] .= '.php';
00078         }
00079         $file = $this->params['file'];
00080 
00081         $connection = null;
00082         if (!empty($this->params['connection'])) {
00083             $connection = $this->params['connection'];
00084         }
00085 
00086         $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection'));
00087     }
00088 /**
00089  * Override main
00090  *
00091  * @access public
00092  */
00093     function main() {
00094         $this->help();
00095     }
00096 /**
00097  * Read and output contents of schema object
00098  * path to read as second arg
00099  *
00100  * @access public
00101  */
00102     function view() {
00103         $File = new File($this->Schema->path . DS . $this->params['file']);
00104         if ($File->exists()) {
00105             $this->out($File->read());
00106             $this->_stop();
00107         } else {
00108             $this->err(__('Schema could not be found', true));
00109             $this->_stop();
00110         }
00111     }
00112 /**
00113  * Read database and Write schema object
00114  * accepts a connection as first arg or path to save as second arg
00115  *
00116  * @access public
00117  */
00118     function generate() {
00119         $this->out('Generating Schema...');
00120         $options = array();
00121         if (isset($this->params['f'])) {
00122             $options = array('models' => false);
00123         }
00124 
00125         $snapshot = false;
00126         if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
00127             $snapshot = true;
00128         }
00129 
00130         if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
00131             $snapshot = true;
00132             $result = $this->in("Schema file exists.\n [O]verwrite\n [S]napshot\n [Q]uit\nWould you like to do?", array('o', 's', 'q'), 's');
00133             if ($result === 'q') {
00134                 $this->_stop();
00135             }
00136             if ($result === 'o') {
00137                 $snapshot = false;
00138             }
00139         }
00140 
00141         $content = $this->Schema->read($options);
00142         $content['file'] = $this->params['file'];
00143 
00144         if ($snapshot === true) {
00145             $Folder =& new Folder($this->Schema->path);
00146             $result = $Folder->read();
00147 
00148             $numToUse = false;
00149             if (isset($this->params['s'])) {
00150                 $numToUse = $this->params['s'];
00151             }
00152 
00153             $count = 1;
00154             if (!empty($result[1])) {
00155                 foreach ($result[1] as $file) {
00156                     if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) {
00157                         $count++;
00158                     }
00159                 }
00160             }
00161 
00162             if ($numToUse !== false) {
00163                 if ($numToUse > $count) {
00164                     $count = $numToUse;
00165                 }
00166             }
00167 
00168             $fileName = rtrim($this->params['file'], '.php');
00169             $content['file'] = $fileName . '_' . $count . '.php';
00170         }
00171 
00172         if ($this->Schema->write($content)) {
00173             $this->out(sprintf(__('Schema file: %s generated', true), $content['file']));
00174             $this->_stop();
00175         } else {
00176             $this->err(__('Schema file: %s generated', true));
00177             $this->_stop();
00178         }
00179     }
00180 /**
00181  * Dump Schema object to sql file
00182  * if first arg == write, file will be written to sql file
00183  * or it will output sql
00184  *
00185  * @access public
00186  */
00187     function dump() {
00188         $write = false;
00189         $Schema = $this->Schema->load();
00190         if (!$Schema) {
00191             $this->err(__('Schema could not be loaded', true));
00192             $this->_stop();
00193         }
00194         if (!empty($this->args[0])) {
00195             if ($this->args[0] == 'write') {
00196                 $write = Inflector::underscore($this->Schema->name);
00197             } else {
00198                 $write = $this->args[0];
00199             }
00200         }
00201         $db =& ConnectionManager::getDataSource($this->Schema->connection);
00202         $contents = "#" . $Schema->name . " sql generated on: " . date('Y-m-d H:i:s') . " : " . time() . "\n\n";
00203         $contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema);
00204         if ($write) {
00205             if (strpos($write, '.sql') === false) {
00206                 $write .= '.sql';
00207             }
00208             $File = new File($this->Schema->path . DS . $write, true);
00209             if ($File->write($contents)) {
00210                 $this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
00211                 $this->_stop();
00212             } else {
00213                 $this->err(__('SQL dump could not be created', true));
00214                 $this->_stop();
00215             }
00216         }
00217         $this->out($contents);
00218         return $contents;
00219     }
00220 /**
00221  * Run database commands: create, update
00222  *
00223  * @access public
00224  */
00225     function run() {
00226         if (!isset($this->args[0])) {
00227             $this->err('command not found');
00228             $this->_stop();
00229         }
00230 
00231         $command = $this->args[0];
00232 
00233         $this->Dispatch->shiftArgs();
00234 
00235         $name = null;
00236         if (isset($this->args[0])) {
00237             $name = $this->args[0];
00238         }
00239         if (isset($this->params['name'])) {
00240             $name = $this->params['name'];
00241         }
00242 
00243         if (isset($this->params['dry'])) {
00244             $this->__dry = true;
00245             $this->out(__('Performing a dry run.', true));
00246         }
00247 
00248         $options = array('name' => $name);
00249         if (isset($this->params['s'])) {
00250             $fileName = rtrim($this->Schema->file, '.php');
00251             $options['file'] = $fileName . '_' . $this->params['s'] . '.php';
00252         }
00253 
00254         $Schema = $this->Schema->load($options);
00255 
00256         if (!$Schema) {
00257             $this->err(sprintf(__('%s could not be loaded', true), $this->Schema->file));
00258             $this->_stop();
00259         }
00260 
00261         $table = null;
00262         if (isset($this->args[1])) {
00263             $table = $this->args[1];
00264         }
00265 
00266         switch ($command) {
00267             case 'create':
00268                 $this->__create($Schema, $table);
00269             break;
00270             case 'update':
00271                 $this->__update($Schema, $table);
00272             break;
00273             default:
00274                 $this->err(__('command not found', true));
00275             $this->_stop();
00276         }
00277     }
00278 /**
00279  * Create database from Schema object
00280  * Should be called via the run method
00281  *
00282  * @access private
00283  */
00284     function __create($Schema, $table = null) {
00285         $db =& ConnectionManager::getDataSource($this->Schema->connection);
00286 
00287         $drop = $create = array();
00288 
00289         if (!$table) {
00290             foreach ($Schema->tables as $table => $fields) {
00291                 $drop[$table] = $db->dropSchema($Schema, $table);
00292                 $create[$table] = $db->createSchema($Schema, $table);
00293             }
00294         } elseif (isset($Schema->tables[$table])) {
00295             $drop[$table] = $db->dropSchema($Schema, $table);
00296             $create[$table] = $db->createSchema($Schema, $table);
00297         }
00298         if (empty($drop) || empty($create)) {
00299             $this->out(__('Schema is up to date.', true));
00300             $this->_stop();
00301         }
00302 
00303         $this->out("\n" . __('The following table(s) will be dropped.', true));
00304         $this->out(array_keys($drop));
00305 
00306         if ('y' == $this->in(__('Are you sure you want to drop the table(s)?', true), array('y', 'n'), 'n')) {
00307             $this->out('Dropping table(s).');
00308             $this->__run($drop, 'drop', $Schema);
00309         }
00310 
00311         $this->out("\n" . __('The following table(s) will be created.', true));
00312         $this->out(array_keys($create));
00313 
00314         if ('y' == $this->in(__('Are you sure you want to create the table(s)?', true), array('y', 'n'), 'y')) {
00315             $this->out('Creating table(s).');
00316             $this->__run($create, 'create', $Schema);
00317         }
00318 
00319         $this->out(__('End create.', true));
00320     }
00321 /**
00322  * Update database with Schema object
00323  * Should be called via the run method
00324  *
00325  * @access private
00326  */
00327     function __update($Schema, $table = null) {
00328         $db =& ConnectionManager::getDataSource($this->Schema->connection);
00329 
00330         $this->out('Comparing Database to Schema...');
00331         $Old = $this->Schema->read();
00332         $compare = $this->Schema->compare($Old, $Schema);
00333 
00334         $contents = array();
00335 
00336         if (empty($table)) {
00337             foreach ($compare as $table => $changes) {
00338                 $contents[$table] = $db->alterSchema(array($table => $changes), $table);
00339             }
00340         } elseif (isset($compare[$table])) {
00341             $contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
00342         }
00343 
00344         if (empty($contents)) {
00345             $this->out(__('Schema is up to date.', true));
00346             $this->_stop();
00347         }
00348 
00349         $this->out("\n" . __('The following statements will run.', true));
00350         $this->out(array_map('trim', $contents));
00351         if ('y' == $this->in(__('Are you sure you want to alter the tables?', true), array('y', 'n'), 'n')) {
00352             $this->out('');
00353             $this->out(__('Updating Database...', true));
00354             $this->__run($contents, 'update', $Schema);
00355         }
00356 
00357         $this->out(__('End update.', true));
00358     }
00359 /**
00360  * Runs sql from __create() or __update()
00361  *
00362  * @access private
00363  */
00364     function __run($contents, $event, $Schema) {
00365         if (empty($contents)) {
00366             $this->err(__('Sql could not be run', true));
00367             return;
00368         }
00369         Configure::write('debug', 2);
00370         $db =& ConnectionManager::getDataSource($this->Schema->connection);
00371         $db->fullDebug = true;
00372 
00373         $errors = array();
00374         foreach ($contents as $table => $sql) {
00375             if (empty($sql)) {
00376                 $this->out(sprintf(__('%s is up to date.', true), $table));
00377             } else {
00378                 if ($this->__dry === true) {
00379                     $this->out(sprintf(__('Dry run for %s :', true), $table));
00380                     $this->out($sql);
00381                 } else {
00382                     if (!$Schema->before(array($event => $table))) {
00383                         return false;
00384                     }
00385                     if (!$db->_execute($sql)) {
00386                         $error = $table . ': '  . $db->lastError();
00387                     }
00388 
00389                     $Schema->after(array($event => $table, 'errors'=> $errors));
00390 
00391                     if (isset($error)) {
00392                         $this->out($error);
00393                     } elseif ($this->__dry !== true) {
00394                         $this->out(sprintf(__('%s updated.', true), $table));
00395                     }
00396                 }
00397             }
00398         }
00399     }
00400 /**
00401  * Displays help contents
00402  *
00403  * @access public
00404  */
00405     function help() {
00406         $this->out("The Schema Shell generates a schema object from \n\t\tthe database and updates the database from the schema.");
00407         $this->hr();
00408         $this->out("Usage: cake schema <command> <arg1> <arg2>...");
00409         $this->hr();
00410         $this->out('Params:');
00411         $this->out("\n\t-connection <config>\n\t\tset db config <config>. uses 'default' if none is specified");
00412         $this->out("\n\t-path <dir>\n\t\tpath <dir> to read and write schema.php.\n\t\tdefault path: ". $this->Schema->path);
00413         $this->out("\n\t-name <name>\n\t\tclassname to use.");
00414         $this->out("\n\t-file <name>\n\t\tfile <name> to read and write.\n\t\tdefault file: ". $this->Schema->file);
00415         $this->out("\n\t-s <number>\n\t\tsnapshot <number> to use for run.");
00416         $this->out("\n\t-dry\n\t\tPerform a dry run on 'run' commands.\n\t\tQueries will be output to window instead of executed.");
00417         $this->out("\n\t-f\n\t\tforce 'generate' to create a new schema.");
00418         $this->out('Commands:');
00419         $this->out("\n\tschema help\n\t\tshows this help message.");
00420         $this->out("\n\tschema view\n\t\tread and output contents of schema file");
00421         $this->out("\n\tschema generate\n\t\treads from 'connection' writes to 'path'\n\t\tTo force generation of all tables into the schema, use the -f param.\n\t\tUse 'schema generate snapshot <number>' to generate snapshots\n\t\twhich you can use with the -s parameter in the other operations.");
00422         $this->out("\n\tschema dump <filename>\n\t\tDump database sql based on schema file to <filename>. \n\t\tIf <filename> is write, schema dump will be written to a file\n\t\tthat has the same name as the app directory.");
00423         $this->out("\n\tschema run create <schema> <table>\n\t\tDrop and create tables based on schema file\n\t\toptional <schema> arg for selecting schema name\n\t\toptional <table> arg for creating only one table\n\t\tpass the -s param with a number to use a snapshot\n\t\tTo see the changes, perform a dry run with the -dry param");
00424         $this->out("\n\tschema run update <schema> <table>\n\t\talter tables based on schema file\n\t\toptional <schema> arg for selecting schema name.\n\t\toptional <table> arg for altering only one table.\n\t\tTo use a snapshot, pass the -s param with the snapshot number\n\t\tTo see the changes, perform a dry run with the -dry param");
00425         $this->out("");
00426         $this->_stop();
00427     }
00428 }
00429 ?>

Generated on Sun Nov 22 00:30:51 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7