PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/cake/console/libs/schema.php

https://github.com/hardsshah/bookmarks
PHP | 429 lines | 291 code | 38 blank | 100 comment | 64 complexity | b3640a4e749226ea018df3bcf92efc8a MD5 | raw file
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * Command-line database management utility to automate programmer chores.
  5. *
  6. * Schema is CakePHP's database management utility. This helps you maintain versions of
  7. * of your database.
  8. *
  9. * PHP versions 4 and 5
  10. *
  11. * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
  12. * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
  13. *
  14. * Licensed under The MIT License
  15. * Redistributions of files must retain the above copyright notice.
  16. *
  17. * @filesource
  18. * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
  19. * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
  20. * @package cake
  21. * @subpackage cake.cake.console.libs
  22. * @since CakePHP(tm) v 1.2.0.5550
  23. * @version $Revision$
  24. * @modifiedby $LastChangedBy$
  25. * @lastmodified $Date$
  26. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  27. */
  28. App::import('File');
  29. App::import('Model', 'Schema');
  30. /**
  31. * Schema is a command-line database management utility for automating programmer chores.
  32. *
  33. * @package cake
  34. * @subpackage cake.cake.console.libs
  35. * @link http://book.cakephp.org/view/734/Schema-management-and-migrations
  36. */
  37. class SchemaShell extends Shell {
  38. /**
  39. * is this a dry run?
  40. *
  41. * @var boolean
  42. * @access private
  43. */
  44. var $__dry = null;
  45. /**
  46. * Override initialize
  47. *
  48. * @access public
  49. */
  50. function initialize() {
  51. $this->_welcome();
  52. $this->out('Cake Schema Shell');
  53. $this->hr();
  54. }
  55. /**
  56. * Override startup
  57. *
  58. * @access public
  59. */
  60. function startup() {
  61. $name = null;
  62. if (!empty($this->params['name'])) {
  63. $name = $this->params['name'];
  64. $this->params['file'] = Inflector::underscore($name);
  65. }
  66. $path = null;
  67. if (!empty($this->params['path'])) {
  68. $path = $this->params['path'];
  69. }
  70. $file = null;
  71. if (empty($this->params['file'])) {
  72. $this->params['file'] = 'schema.php';
  73. }
  74. if (strpos($this->params['file'], '.php') === false) {
  75. $this->params['file'] .= '.php';
  76. }
  77. $file = $this->params['file'];
  78. $connection = null;
  79. if (!empty($this->params['connection'])) {
  80. $connection = $this->params['connection'];
  81. }
  82. $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection'));
  83. }
  84. /**
  85. * Override main
  86. *
  87. * @access public
  88. */
  89. function main() {
  90. $this->help();
  91. }
  92. /**
  93. * Read and output contents of schema object
  94. * path to read as second arg
  95. *
  96. * @access public
  97. */
  98. function view() {
  99. $File = new File($this->Schema->path . DS . $this->params['file']);
  100. if ($File->exists()) {
  101. $this->out($File->read());
  102. $this->_stop();
  103. } else {
  104. $this->err(__('Schema could not be found', true));
  105. $this->_stop();
  106. }
  107. }
  108. /**
  109. * Read database and Write schema object
  110. * accepts a connection as first arg or path to save as second arg
  111. *
  112. * @access public
  113. */
  114. function generate() {
  115. $this->out('Generating Schema...');
  116. $options = array();
  117. if (isset($this->params['f'])) {
  118. $options = array('models' => false);
  119. }
  120. $snapshot = false;
  121. if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
  122. $snapshot = true;
  123. }
  124. if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
  125. $snapshot = true;
  126. $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');
  127. if ($result === 'q') {
  128. $this->_stop();
  129. }
  130. if ($result === 'o') {
  131. $snapshot = false;
  132. }
  133. }
  134. $content = $this->Schema->read($options);
  135. $content['file'] = $this->params['file'];
  136. if ($snapshot === true) {
  137. $Folder =& new Folder($this->Schema->path);
  138. $result = $Folder->read();
  139. $numToUse = false;
  140. if (isset($this->params['s'])) {
  141. $numToUse = $this->params['s'];
  142. }
  143. $count = 1;
  144. if (!empty($result[1])) {
  145. foreach ($result[1] as $file) {
  146. if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) {
  147. $count++;
  148. }
  149. }
  150. }
  151. if ($numToUse !== false) {
  152. if ($numToUse > $count) {
  153. $count = $numToUse;
  154. }
  155. }
  156. $fileName = rtrim($this->params['file'], '.php');
  157. $content['file'] = $fileName . '_' . $count . '.php';
  158. }
  159. if ($this->Schema->write($content)) {
  160. $this->out(sprintf(__('Schema file: %s generated', true), $content['file']));
  161. $this->_stop();
  162. } else {
  163. $this->err(__('Schema file: %s generated', true));
  164. $this->_stop();
  165. }
  166. }
  167. /**
  168. * Dump Schema object to sql file
  169. * if first arg == write, file will be written to sql file
  170. * or it will output sql
  171. *
  172. * @access public
  173. */
  174. function dump() {
  175. $write = false;
  176. $Schema = $this->Schema->load();
  177. if (!$Schema) {
  178. $this->err(__('Schema could not be loaded', true));
  179. $this->_stop();
  180. }
  181. if (!empty($this->args[0])) {
  182. if ($this->args[0] == 'write') {
  183. $write = Inflector::underscore($this->Schema->name);
  184. } else {
  185. $write = $this->args[0];
  186. }
  187. }
  188. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  189. $contents = "#". $Schema->name ." sql generated on: " . date('Y-m-d H:m:s') . " : ". time()."\n\n";
  190. $contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema);
  191. if ($write) {
  192. if (strpos($write, '.sql') === false) {
  193. $write .= '.sql';
  194. }
  195. $File = new File($this->Schema->path . DS . $write, true);
  196. if ($File->write($contents)) {
  197. $this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
  198. $this->_stop();
  199. } else {
  200. $this->err(__('SQL dump could not be created', true));
  201. $this->_stop();
  202. }
  203. }
  204. $this->out($contents);
  205. return $contents;
  206. }
  207. /**
  208. * Run database commands: create, update
  209. *
  210. * @access public
  211. */
  212. function run() {
  213. if (!isset($this->args[0])) {
  214. $this->err('command not found');
  215. $this->_stop();
  216. }
  217. $command = $this->args[0];
  218. $this->Dispatch->shiftArgs();
  219. $name = null;
  220. if (isset($this->args[0])) {
  221. $name = $this->args[0];
  222. }
  223. if (isset($this->params['name'])) {
  224. $name = $this->params['name'];
  225. }
  226. if (isset($this->params['dry'])) {
  227. $this->__dry = true;
  228. $this->out(__('Performing a dry run.', true));
  229. }
  230. $options = array('name' => $name);
  231. if (isset($this->params['s'])) {
  232. $fileName = rtrim($this->Schema->file, '.php');
  233. $options['file'] = $fileName . '_' . $this->params['s'] . '.php';
  234. }
  235. $Schema = $this->Schema->load($options);
  236. if (!$Schema) {
  237. $this->err(sprintf(__('%s could not be loaded', true), $this->Schema->file));
  238. $this->_stop();
  239. }
  240. $table = null;
  241. if (isset($this->args[1])) {
  242. $table = $this->args[1];
  243. }
  244. switch ($command) {
  245. case 'create':
  246. $this->__create($Schema, $table);
  247. break;
  248. case 'update':
  249. $this->__update($Schema, $table);
  250. break;
  251. default:
  252. $this->err(__('command not found', true));
  253. $this->_stop();
  254. }
  255. }
  256. /**
  257. * Create database from Schema object
  258. * Should be called via the run method
  259. *
  260. * @access private
  261. */
  262. function __create($Schema, $table = null) {
  263. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  264. $drop = $create = array();
  265. if (!$table) {
  266. foreach ($Schema->tables as $table => $fields) {
  267. $drop[$table] = $db->dropSchema($Schema, $table);
  268. $create[$table] = $db->createSchema($Schema, $table);
  269. }
  270. } elseif (isset($Schema->tables[$table])) {
  271. $drop[$table] = $db->dropSchema($Schema, $table);
  272. $create[$table] = $db->createSchema($Schema, $table);
  273. }
  274. if (empty($drop) || empty($create)) {
  275. $this->out(__('Schema is up to date.', true));
  276. $this->_stop();
  277. }
  278. $this->out("\n" . __('The following table(s) will be dropped.', true));
  279. $this->out(array_keys($drop));
  280. if ('y' == $this->in(__('Are you sure you want to drop the table(s)?', true), array('y', 'n'), 'n')) {
  281. $this->out('Dropping table(s).');
  282. $this->__run($drop, 'drop', $Schema);
  283. }
  284. $this->out("\n" . __('The following table(s) will be created.', true));
  285. $this->out(array_keys($create));
  286. if ('y' == $this->in(__('Are you sure you want to create the table(s)?', true), array('y', 'n'), 'y')) {
  287. $this->out('Creating table(s).');
  288. $this->__run($create, 'create', $Schema);
  289. }
  290. $this->out(__('End create.', true));
  291. }
  292. /**
  293. * Update database with Schema object
  294. * Should be called via the run method
  295. *
  296. * @access private
  297. */
  298. function __update($Schema, $table = null) {
  299. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  300. $this->out('Comparing Database to Schema...');
  301. $Old = $this->Schema->read();
  302. $compare = $this->Schema->compare($Old, $Schema);
  303. $contents = array();
  304. if (empty($table)) {
  305. foreach ($compare as $table => $changes) {
  306. $contents[$table] = $db->alterSchema(array($table => $changes), $table);
  307. }
  308. } elseif (isset($compare[$table])) {
  309. $contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
  310. }
  311. if (empty($contents)) {
  312. $this->out(__('Schema is up to date.', true));
  313. $this->_stop();
  314. }
  315. $this->out("\n" . __('The following statements will run.', true));
  316. $this->out(array_map('trim', $contents));
  317. if ('y' == $this->in(__('Are you sure you want to alter the tables?', true), array('y', 'n'), 'n')) {
  318. $this->out('');
  319. $this->out(__('Updating Database...', true));
  320. $this->__run($contents, 'update', $Schema);
  321. }
  322. $this->out(__('End update.', true));
  323. }
  324. /**
  325. * Runs sql from __create() or __update()
  326. *
  327. * @access private
  328. */
  329. function __run($contents, $event, $Schema) {
  330. if (empty($contents)) {
  331. $this->err(__('Sql could not be run', true));
  332. return;
  333. }
  334. Configure::write('debug', 2);
  335. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  336. $db->fullDebug = true;
  337. $errors = array();
  338. foreach ($contents as $table => $sql) {
  339. if (empty($sql)) {
  340. $this->out(sprintf(__('%s is up to date.', true), $table));
  341. } else {
  342. if ($this->__dry === true) {
  343. $this->out(sprintf(__('Dry run for %s :', true), $table));
  344. $this->out($sql);
  345. } else {
  346. if (!$Schema->before(array($event => $table))) {
  347. return false;
  348. }
  349. if (!$db->_execute($sql)) {
  350. $error = $table . ': ' . $db->lastError();
  351. }
  352. $Schema->after(array($event => $table, 'errors'=> $errors));
  353. if (isset($error)) {
  354. $this->out($error);
  355. } elseif ($this->__dry !== true) {
  356. $this->out(sprintf(__('%s updated.', true), $table));
  357. }
  358. }
  359. }
  360. }
  361. }
  362. /**
  363. * Displays help contents
  364. *
  365. * @access public
  366. */
  367. function help() {
  368. $this->out("The Schema Shell generates a schema object from \n\t\tthe database and updates the database from the schema.");
  369. $this->hr();
  370. $this->out("Usage: cake schema <command> <arg1> <arg2>...");
  371. $this->hr();
  372. $this->out('Params:');
  373. $this->out("\n\t-connection <config>\n\t\tset db config <config>. uses 'default' if none is specified");
  374. $this->out("\n\t-path <dir>\n\t\tpath <dir> to read and write schema.php.\n\t\tdefault path: ". $this->Schema->path);
  375. $this->out("\n\t-name <name>\n\t\tclassname to use.");
  376. $this->out("\n\t-file <name>\n\t\tfile <name> to read and write.\n\t\tdefault file: ". $this->Schema->file);
  377. $this->out("\n\t-s <number>\n\t\tsnapshot <number> to use for run.");
  378. $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.");
  379. $this->out("\n\t-f\n\t\tforce 'generate' to create a new schema.");
  380. $this->out('Commands:');
  381. $this->out("\n\tschema help\n\t\tshows this help message.");
  382. $this->out("\n\tschema view\n\t\tread and output contents of schema file");
  383. $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.");
  384. $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.");
  385. $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");
  386. $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");
  387. $this->out("");
  388. $this->_stop();
  389. }
  390. }
  391. ?>