2008-05-30 11:40:08 +00:00
< ? php
/* SVN FILE: $Id$ */
/**
* Command - line database management utility to automate programmer chores .
*
* Schema is CakePHP ' s database management utility . This helps you maintain versions of
* of your database .
*
* PHP versions 4 and 5
*
2008-10-30 17:30:26 +00:00
* CakePHP ( tm ) : Rapid Development Framework ( http :// www . cakephp . org )
* Copyright 2005 - 2008 , Cake Software Foundation , Inc . ( http :// www . cakefoundation . org )
2008-05-30 11:40:08 +00:00
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice .
*
* @ filesource
2008-10-30 17:30:26 +00:00
* @ copyright Copyright 2005 - 2008 , Cake Software Foundation , Inc . ( http :// www . cakefoundation . org )
* @ link http :// www . cakefoundation . org / projects / info / cakephp CakePHP ( tm ) Project
* @ package cake
* @ subpackage cake . cake . console . libs
* @ since CakePHP ( tm ) v 1.2 . 0.5550
* @ version $Revision $
* @ modifiedby $LastChangedBy $
* @ lastmodified $Date $
* @ license http :// www . opensource . org / licenses / mit - license . php The MIT License
2008-05-30 11:40:08 +00:00
*/
App :: import ( 'File' );
App :: import ( 'Model' , 'Schema' );
/**
* Schema is a command - line database management utility for automating programmer chores .
*
2008-10-30 17:30:26 +00:00
* @ package cake
* @ subpackage cake . cake . console . libs
2008-10-31 19:47:43 +00:00
* @ link http :// book . cakephp . org / view / 734 / Schema - management - and - migrations
2008-05-30 11:40:08 +00:00
*/
class SchemaShell extends Shell {
/**
* is this a dry run ?
*
* @ var boolean
* @ access private
*/
var $__dry = null ;
/**
* Override initialize
*
* @ access public
*/
function initialize () {
2008-06-03 05:11:04 +00:00
$this -> _welcome ();
2008-05-30 11:40:08 +00:00
$this -> out ( 'Cake Schema Shell' );
$this -> hr ();
}
/**
* Override startup
*
* @ access public
*/
function startup () {
$name = null ;
if ( ! empty ( $this -> params [ 'name' ])) {
2008-06-20 20:17:23 +00:00
$name = $this -> params [ 'name' ];
2008-08-04 19:46:44 +00:00
$this -> params [ 'file' ] = Inflector :: underscore ( $name );
2008-05-30 11:40:08 +00:00
}
2008-08-26 18:39:18 +00:00
2008-05-30 11:40:08 +00:00
$path = null ;
if ( ! empty ( $this -> params [ 'path' ])) {
2008-06-20 20:17:23 +00:00
$path = $this -> params [ 'path' ];
2008-05-30 11:40:08 +00:00
}
2008-08-26 18:39:18 +00:00
2008-05-30 11:40:08 +00:00
$file = null ;
2008-08-04 19:46:44 +00:00
if ( empty ( $this -> params [ 'file' ])) {
$this -> params [ 'file' ] = 'schema.php' ;
2008-05-30 11:40:08 +00:00
}
2008-08-04 19:46:44 +00:00
if ( strpos ( $this -> params [ 'file' ], '.php' ) === false ) {
$this -> params [ 'file' ] .= '.php' ;
2008-08-26 18:39:18 +00:00
}
2008-08-04 19:46:44 +00:00
$file = $this -> params [ 'file' ];
2008-08-26 18:39:18 +00:00
2008-05-30 11:40:08 +00:00
$connection = null ;
if ( ! empty ( $this -> params [ 'connection' ])) {
2008-06-20 20:17:23 +00:00
$connection = $this -> params [ 'connection' ];
2008-05-30 11:40:08 +00:00
}
2008-08-26 18:39:18 +00:00
2008-05-30 11:40:08 +00:00
$this -> Schema =& new CakeSchema ( compact ( 'name' , 'path' , 'file' , 'connection' ));
}
/**
* Override main
*
* @ access public
*/
function main () {
$this -> help ();
}
/**
2008-08-26 18:39:18 +00:00
* Read and output contents of schema object
2008-05-30 11:40:08 +00:00
* path to read as second arg
*
* @ access public
*/
function view () {
2008-08-04 19:46:44 +00:00
$File = new File ( $this -> Schema -> path . DS . $this -> params [ 'file' ]);
2008-05-30 11:40:08 +00:00
if ( $File -> exists ()) {
$this -> out ( $File -> read ());
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
} else {
$this -> err ( __ ( 'Schema could not be found' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
}
/**
* Read database and Write schema object
* accepts a connection as first arg or path to save as second arg
*
* @ access public
*/
function generate () {
$this -> out ( 'Generating Schema...' );
$options = array ();
if ( isset ( $this -> params [ 'f' ])) {
$options = array ( 'models' => false );
}
$snapshot = false ;
if ( isset ( $this -> args [ 0 ]) && $this -> args [ 0 ] === 'snapshot' ) {
$snapshot = true ;
}
2008-08-04 19:46:44 +00:00
if ( ! $snapshot && file_exists ( $this -> Schema -> path . DS . $this -> params [ 'file' ])) {
2008-05-30 11:40:08 +00:00
$snapshot = true ;
$result = $this -> in ( " Schema file exists. \n [O]verwrite \n [S]napshot \n [Q]uit \n Would you like to do? " , array ( 'o' , 's' , 'q' ), 's' );
if ( $result === 'q' ) {
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
if ( $result === 'o' ) {
$snapshot = false ;
}
}
$content = $this -> Schema -> read ( $options );
2008-08-04 19:46:44 +00:00
$content [ 'file' ] = $this -> params [ 'file' ];
2008-05-30 11:40:08 +00:00
if ( $snapshot === true ) {
$Folder =& new Folder ( $this -> Schema -> path );
$result = $Folder -> read ();
2008-08-04 16:53:39 +00:00
$numToUse = false ;
if ( isset ( $this -> params [ 's' ])) {
$numToUse = $this -> params [ 's' ];
}
2008-05-30 11:40:08 +00:00
$count = 1 ;
if ( ! empty ( $result [ 1 ])) {
foreach ( $result [ 1 ] as $file ) {
2008-12-05 17:01:20 +00:00
if ( preg_match ( '/schema(?:[_\d]*)?\.php$/' , $file )) {
2008-05-30 11:40:08 +00:00
$count ++ ;
}
}
}
2008-08-04 16:53:39 +00:00
if ( $numToUse !== false ) {
if ( $numToUse > $count ) {
$count = $numToUse ;
}
}
2008-08-05 02:53:56 +00:00
$fileName = rtrim ( $this -> params [ 'file' ], '.php' );
2008-08-04 19:46:44 +00:00
$content [ 'file' ] = $fileName . '_' . $count . '.php' ;
2008-05-30 11:40:08 +00:00
}
if ( $this -> Schema -> write ( $content )) {
$this -> out ( sprintf ( __ ( 'Schema file: %s generated' , true ), $content [ 'file' ]));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
} else {
$this -> err ( __ ( 'Schema file: %s generated' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
}
/**
* Dump Schema object to sql file
* if first arg == write , file will be written to sql file
* or it will output sql
*
* @ access public
*/
function dump () {
$write = false ;
$Schema = $this -> Schema -> load ();
if ( ! $Schema ) {
$this -> err ( __ ( 'Schema could not be loaded' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
if ( ! empty ( $this -> args [ 0 ])) {
2008-08-04 19:46:44 +00:00
if ( $this -> args [ 0 ] == 'write' ) {
2008-05-30 11:40:08 +00:00
$write = Inflector :: underscore ( $this -> Schema -> name );
} else {
$write = $this -> args [ 0 ];
}
}
$db =& ConnectionManager :: getDataSource ( $this -> Schema -> connection );
2009-07-14 02:30:51 +00:00
$contents = " # " . $Schema -> name . " sql generated on: " . date ( 'Y-m-d H:i:s' ) . " : " . time () . " \n \n " ;
2008-05-30 11:40:08 +00:00
$contents .= $db -> dropSchema ( $Schema ) . " \n \n " . $db -> createSchema ( $Schema );
if ( $write ) {
if ( strpos ( $write , '.sql' ) === false ) {
$write .= '.sql' ;
}
$File = new File ( $this -> Schema -> path . DS . $write , true );
if ( $File -> write ( $contents )) {
$this -> out ( sprintf ( __ ( 'SQL dump file created in %s' , true ), $File -> pwd ()));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
} else {
$this -> err ( __ ( 'SQL dump could not be created' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
}
$this -> out ( $contents );
return $contents ;
}
/**
* Run database commands : create , update
*
* @ access public
*/
function run () {
if ( ! isset ( $this -> args [ 0 ])) {
$this -> err ( 'command not found' );
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
$command = $this -> args [ 0 ];
$this -> Dispatch -> shiftArgs ();
$name = null ;
if ( isset ( $this -> args [ 0 ])) {
$name = $this -> args [ 0 ];
}
2008-08-04 19:46:44 +00:00
if ( isset ( $this -> params [ 'name' ])) {
$name = $this -> params [ 'name' ];
}
2008-08-26 18:39:18 +00:00
2008-05-30 11:40:08 +00:00
if ( isset ( $this -> params [ 'dry' ])) {
$this -> __dry = true ;
$this -> out ( __ ( 'Performing a dry run.' , true ));
}
2008-08-04 19:46:44 +00:00
$options = array ( 'name' => $name );
2008-06-20 20:17:23 +00:00
if ( isset ( $this -> params [ 's' ])) {
2008-09-24 13:49:37 +00:00
$fileName = rtrim ( $this -> Schema -> file , '.php' );
2008-08-04 19:46:44 +00:00
$options [ 'file' ] = $fileName . '_' . $this -> params [ 's' ] . '.php' ;
2008-05-30 11:40:08 +00:00
}
$Schema = $this -> Schema -> load ( $options );
if ( ! $Schema ) {
$this -> err ( sprintf ( __ ( '%s could not be loaded' , true ), $this -> Schema -> file ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
$table = null ;
if ( isset ( $this -> args [ 1 ])) {
$table = $this -> args [ 1 ];
}
2008-10-23 00:10:44 +00:00
switch ( $command ) {
2008-05-30 11:40:08 +00:00
case 'create' :
$this -> __create ( $Schema , $table );
break ;
case 'update' :
$this -> __update ( $Schema , $table );
break ;
default :
$this -> err ( __ ( 'command not found' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
}
/**
* Create database from Schema object
* Should be called via the run method
*
* @ access private
*/
function __create ( $Schema , $table = null ) {
$db =& ConnectionManager :: getDataSource ( $this -> Schema -> connection );
$drop = $create = array ();
if ( ! $table ) {
foreach ( $Schema -> tables as $table => $fields ) {
$drop [ $table ] = $db -> dropSchema ( $Schema , $table );
$create [ $table ] = $db -> createSchema ( $Schema , $table );
}
} elseif ( isset ( $Schema -> tables [ $table ])) {
$drop [ $table ] = $db -> dropSchema ( $Schema , $table );
$create [ $table ] = $db -> createSchema ( $Schema , $table );
}
if ( empty ( $drop ) || empty ( $create )) {
$this -> out ( __ ( 'Schema is up to date.' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
2008-08-04 16:53:39 +00:00
$this -> out ( " \n " . __ ( 'The following table(s) will be dropped.' , true ));
2008-05-30 11:40:08 +00:00
$this -> out ( array_keys ( $drop ));
2008-08-04 16:53:39 +00:00
if ( 'y' == $this -> in ( __ ( 'Are you sure you want to drop the table(s)?' , true ), array ( 'y' , 'n' ), 'n' )) {
$this -> out ( 'Dropping table(s).' );
2008-12-23 20:30:20 +00:00
$this -> __run ( $drop , 'drop' , $Schema );
2008-05-30 11:40:08 +00:00
}
2008-08-04 16:53:39 +00:00
$this -> out ( " \n " . __ ( 'The following table(s) will be created.' , true ));
2008-05-30 11:40:08 +00:00
$this -> out ( array_keys ( $create ));
2008-08-04 16:53:39 +00:00
if ( 'y' == $this -> in ( __ ( 'Are you sure you want to create the table(s)?' , true ), array ( 'y' , 'n' ), 'y' )) {
$this -> out ( 'Creating table(s).' );
2008-12-23 20:30:20 +00:00
$this -> __run ( $create , 'create' , $Schema );
2008-05-30 11:40:08 +00:00
}
$this -> out ( __ ( 'End create.' , true ));
}
/**
* Update database with Schema object
* Should be called via the run method
*
* @ access private
*/
function __update ( $Schema , $table = null ) {
$db =& ConnectionManager :: getDataSource ( $this -> Schema -> connection );
$this -> out ( 'Comparing Database to Schema...' );
$Old = $this -> Schema -> read ();
$compare = $this -> Schema -> compare ( $Old , $Schema );
$contents = array ();
if ( empty ( $table )) {
foreach ( $compare as $table => $changes ) {
$contents [ $table ] = $db -> alterSchema ( array ( $table => $changes ), $table );
}
} elseif ( isset ( $compare [ $table ])) {
$contents [ $table ] = $db -> alterSchema ( array ( $table => $compare [ $table ]), $table );
}
if ( empty ( $contents )) {
$this -> out ( __ ( 'Schema is up to date.' , true ));
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
$this -> out ( " \n " . __ ( 'The following statements will run.' , true ));
$this -> out ( array_map ( 'trim' , $contents ));
if ( 'y' == $this -> in ( __ ( 'Are you sure you want to alter the tables?' , true ), array ( 'y' , 'n' ), 'n' )) {
$this -> out ( '' );
$this -> out ( __ ( 'Updating Database...' , true ));
2008-12-23 20:30:20 +00:00
$this -> __run ( $contents , 'update' , $Schema );
2008-05-30 11:40:08 +00:00
}
$this -> out ( __ ( 'End update.' , true ));
}
/**
2008-08-26 18:39:18 +00:00
* Runs sql from __create () or __update ()
2008-05-30 11:40:08 +00:00
*
* @ access private
*/
2008-12-23 20:30:20 +00:00
function __run ( $contents , $event , $Schema ) {
2008-05-30 11:40:08 +00:00
if ( empty ( $contents )) {
$this -> err ( __ ( 'Sql could not be run' , true ));
return ;
}
Configure :: write ( 'debug' , 2 );
$db =& ConnectionManager :: getDataSource ( $this -> Schema -> connection );
$db -> fullDebug = true ;
$errors = array ();
2008-10-23 00:10:44 +00:00
foreach ( $contents as $table => $sql ) {
2008-05-30 11:40:08 +00:00
if ( empty ( $sql )) {
$this -> out ( sprintf ( __ ( '%s is up to date.' , true ), $table ));
} else {
if ( $this -> __dry === true ) {
$this -> out ( sprintf ( __ ( 'Dry run for %s :' , true ), $table ));
$this -> out ( $sql );
} else {
2008-12-23 20:30:20 +00:00
if ( ! $Schema -> before ( array ( $event => $table ))) {
2008-05-30 11:40:08 +00:00
return false ;
}
if ( ! $db -> _execute ( $sql )) {
2008-06-22 19:53:30 +00:00
$error = $table . ': ' . $db -> lastError ();
2008-05-30 11:40:08 +00:00
}
2008-12-23 20:30:20 +00:00
$Schema -> after ( array ( $event => $table , 'errors' => $errors ));
2008-05-30 11:40:08 +00:00
if ( isset ( $error )) {
2008-06-22 19:53:30 +00:00
$this -> out ( $error );
2008-05-30 11:40:08 +00:00
} elseif ( $this -> __dry !== true ) {
$this -> out ( sprintf ( __ ( '%s updated.' , true ), $table ));
}
}
}
}
}
/**
* Displays help contents
*
* @ access public
*/
function help () {
$this -> out ( " The Schema Shell generates a schema object from \n \t \t the database and updates the database from the schema. " );
$this -> hr ();
$this -> out ( " Usage: cake schema <command> <arg1> <arg2>... " );
$this -> hr ();
$this -> out ( 'Params:' );
$this -> out ( " \n \t -connection <config> \n \t \t set db config <config>. uses 'default' if none is specified " );
$this -> out ( " \n \t -path <dir> \n \t \t path <dir> to read and write schema.php. \n \t \t default path: " . $this -> Schema -> path );
2008-08-04 19:46:44 +00:00
$this -> out ( " \n \t -name <name> \n \t \t classname to use. " );
2008-05-30 11:40:08 +00:00
$this -> out ( " \n \t -file <name> \n \t \t file <name> to read and write. \n \t \t default file: " . $this -> Schema -> file );
$this -> out ( " \n \t -s <number> \n \t \t snapshot <number> to use for run. " );
$this -> out ( " \n \t -dry \n \t \t Perform a dry run on 'run' commands. \n \t \t Queries will be output to window instead of executed. " );
$this -> out ( " \n \t -f \n \t \t force 'generate' to create a new schema. " );
$this -> out ( 'Commands:' );
$this -> out ( " \n \t schema help \n \t \t shows this help message. " );
$this -> out ( " \n \t schema view \n \t \t read and output contents of schema file " );
2008-09-25 03:50:45 +00:00
$this -> out ( " \n \t schema generate \n \t \t reads from 'connection' writes to 'path' \n \t \t To force generation of all tables into the schema, use the -f param. \n \t \t Use 'schema generate snapshot <number>' to generate snapshots \n \t \t which you can use with the -s parameter in the other operations. " );
$this -> out ( " \n \t schema dump <filename> \n \t \t Dump database sql based on schema file to <filename>. \n \t \t If <filename> is write, schema dump will be written to a file \n \t \t that has the same name as the app directory. " );
$this -> out ( " \n \t schema run create <schema> <table> \n \t \t Drop and create tables based on schema file \n \t \t optional <schema> arg for selecting schema name \n \t \t optional <table> arg for creating only one table \n \t \t pass the -s param with a number to use a snapshot \n \t \t To see the changes, perform a dry run with the -dry param " );
2008-05-30 11:40:08 +00:00
$this -> out ( " \n \t schema run update <schema> <table> \n \t \t alter tables based on schema file \n \t \t optional <schema> arg for selecting schema name. \n \t \t optional <table> arg for altering only one table. \n \t \t To use a snapshot, pass the -s param with the snapshot number \n \t \t To see the changes, perform a dry run with the -dry param " );
$this -> out ( " " );
2008-06-04 19:04:58 +00:00
$this -> _stop ();
2008-05-30 11:40:08 +00:00
}
}
2008-08-04 19:46:44 +00:00
?>