PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/vendors/migration.php

http://github.com/jrbasso/migrations
PHP | 444 lines | 248 code | 29 blank | 167 comment | 50 complexity | 4165fde37930ab77daee5c4a71f2e4b6 MD5 | raw file
  1. <?php
  2. /**
  3. * Base migration class
  4. *
  5. * @link http://github.com/jrbasso/migrations
  6. * @package migrations
  7. * @subpackage migrations.vendors
  8. * @since v 0.1
  9. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  10. */
  11. /**
  12. * Migration
  13. */
  14. class Migration {
  15. /**
  16. * Uses models
  17. *
  18. * @var array
  19. * @access public
  20. */
  21. var $uses = array();
  22. /**
  23. * Stop up/down on error
  24. *
  25. * @var boolean
  26. * @access public
  27. */
  28. var $stopOnError = true;
  29. /**
  30. * DataSource link
  31. *
  32. * @var object
  33. * @access protected
  34. */
  35. var $_db = null;
  36. /**
  37. * Schell that called this class
  38. *
  39. * @var object
  40. * @access protected
  41. */
  42. var $_shell;
  43. /**
  44. * Fake CakeSchema
  45. *
  46. * @var object
  47. * @access private
  48. */
  49. var $__fakeSchema = null;
  50. /**
  51. * Error
  52. *
  53. * @var boolean
  54. * @access private
  55. */
  56. var $__error = false;
  57. /**
  58. * Constructor
  59. */
  60. function __construct(&$shell = null) {
  61. $this->_shell =& $shell;
  62. $this->_db =& $shell->db;
  63. $this->__fakeSchema = new CakeSchema();
  64. $uses = $this->_getUses();
  65. foreach ($uses as $use) {
  66. $varName = Inflector::camelize($use);
  67. if (!PHP5) {
  68. $this->{$varName} =& ClassRegistry::init(array('class' => $use, 'alias' => $use, 'ds' => $shell->connection));
  69. } else {
  70. $this->{$varName} = ClassRegistry::init(array('class' => $use, 'alias' => $use, 'ds' => $shell->connection));
  71. }
  72. if (!$this->{$varName}) {
  73. $this->_shell->err(String::insert(
  74. __d('migrations', 'Model ":model" not exists.', true),
  75. array('model' => $use)
  76. ));
  77. $this->_shell->_stop();
  78. }
  79. }
  80. }
  81. /**
  82. * Get uses from parent classes
  83. *
  84. * @return array
  85. * @access protected
  86. */
  87. function _getUses(){
  88. // Uses
  89. $uses = get_class_vars('AppMigration');
  90. $uses = $uses['uses'];
  91. if (!is_array($uses)) {
  92. $uses = (array)$uses;
  93. }
  94. if (!is_array($this->uses)) {
  95. $this->uses = (array)$this->uses;
  96. }
  97. return array_unique(array_merge($uses, $this->uses));
  98. }
  99. /**
  100. * Get a model from a table name
  101. *
  102. * @param string $tableName
  103. * @return object Model
  104. * @access public
  105. */
  106. function getModel($tableName) {
  107. if (!in_array($tableName, $this->_db->listSources())) {
  108. return null;
  109. }
  110. return new AppModel(array('name' => Inflector::camelize(Inflector::singularize($tableName)), 'table' => $tableName, 'ds' => $this->_db->configKeyName));
  111. }
  112. /**
  113. * Create a table
  114. *
  115. * @param string $tableName
  116. * @param array $columns
  117. * @param array $indexes
  118. * @param array $options
  119. * @return boolean
  120. * @access public
  121. */
  122. function createTable($tableName, $columns, $indexes = array(), $options = array()) {
  123. if ($this->stopOnError && $this->__error) {
  124. return false;
  125. }
  126. $_defaults = array(
  127. 'insertId' => true,
  128. 'insertCreated' => true,
  129. 'insertModified' => true,
  130. 'insertUpdated' => false
  131. );
  132. $options = array_merge($_defaults, $options);
  133. if (!empty($options['insertId']) && !isset($columns['id'])) {
  134. $idColumn = array(
  135. 'id' => array(
  136. 'type' => 'integer',
  137. 'null' => false,
  138. 'default' => null,
  139. 'key' => 'primary'
  140. )
  141. );
  142. $columns = $idColumn + $columns; // To insert on the begin
  143. }
  144. $datesColumn = array(
  145. 'type' => 'datetime',
  146. 'null' => false,
  147. 'default' => null
  148. );
  149. foreach (array('created', 'modified', 'updated') as $field) {
  150. if ($options['insert' . Inflector::camelize($field)] && !isset($columns[$field])) {
  151. $columns[$field] = $datesColumn;
  152. }
  153. }
  154. $this->out('> ' . String::insert(
  155. __d('migrations', 'Creating table ":table"... ', true),
  156. array('table' => $tableName)
  157. ), false);
  158. $this->__fakeSchema->tables = array($tableName => $columns);
  159. if (is_array($indexes) && !empty($indexes)) {
  160. $this->__fakeSchema->tables['indexes'] = $indexes;
  161. }
  162. if ($this->_db->execute($this->_db->createSchema($this->__fakeSchema))) {
  163. $this->out('ok');
  164. return true;
  165. }
  166. $this->out('nok');
  167. $this->__error = true;
  168. return false;
  169. }
  170. /**
  171. * Change some itens from table
  172. *
  173. * @todo Need implements. Cake core dont support this
  174. * @return void
  175. * @access public
  176. */
  177. function changeTable() {
  178. }
  179. /**
  180. * Drop a table
  181. *
  182. * @param string $tableName Name of table
  183. * @return boolean
  184. * @access public
  185. */
  186. function dropTable($tableName) {
  187. if ($this->stopOnError && $this->__error) {
  188. return false;
  189. }
  190. if (is_array($tableName)) {
  191. foreach ($tableName as $table) {
  192. if (!$this->dropTable($table) && $this->stopOnError && $this->__error) {
  193. return false;
  194. }
  195. }
  196. return true;
  197. }
  198. $this->out('> ' . String::insert(__d('migrations', 'Dropping table ":table"... ', true), array('table' => $tableName)), false);
  199. $this->__fakeSchema->tables = array($tableName => '');
  200. if ($this->_db->execute($this->_db->dropSchema($this->__fakeSchema, $tableName))) {
  201. $this->out('ok');
  202. return true;
  203. }
  204. $this->out('nok');
  205. $this->__error = true;
  206. return false;
  207. }
  208. /**
  209. * Include new column
  210. *
  211. * @param string $tableName
  212. * @param string $columnName
  213. * @param array $columnConfig
  214. * @return boolean
  215. * @access public
  216. */
  217. function addColumn($tableName, $columnName, $columnConfig = array()) {
  218. if ($this->stopOnError && $this->__error) {
  219. return false;
  220. }
  221. $columnConfig = array_merge(array('type' => 'integer'), $columnConfig);
  222. $this->out('> ' . String::insert(__d('migrations', 'Creating column ":column"... ', true), array('column' => $columnName)), false);
  223. if ($this->_db->execute($this->_db->alterSchema(array(
  224. $tableName => array(
  225. 'add' => array(
  226. $columnName => $columnConfig
  227. )
  228. )
  229. ), $tableName))) {
  230. $this->out('ok');
  231. return true;
  232. }
  233. $this->out('nok');
  234. $this->__error = true;
  235. return false;
  236. }
  237. /**
  238. * Remove a column
  239. *
  240. * @param string $tableName
  241. * @param string $columnName
  242. * @return boolean
  243. * @access public
  244. */
  245. function removeColumn($tableName, $columnName) {
  246. if ($this->stopOnError && $this->__error) {
  247. return false;
  248. }
  249. $this->out('> ' . String::insert(__d('migrations', 'Removing column ":column"... ', true), array('column' => $columnName)), false);
  250. if ($this->_db->execute($this->_db->alterSchema(array(
  251. $tableName => array(
  252. 'drop' => array(
  253. $columnName => array()
  254. )
  255. )
  256. ), $tableName))) {
  257. $this->out('ok');
  258. return true;
  259. }
  260. $this->out('nok');
  261. $this->__error = true;
  262. return false;
  263. }
  264. /**
  265. * Change column
  266. *
  267. * @param string $tableName
  268. * @param string $columnName
  269. * @param array $newColumnConfig
  270. * @param boolean $verbose
  271. * @return boolean
  272. * @access public
  273. */
  274. function changeColumn($tableName, $columnName, $newColumnConfig = array(), $verbose = true) {
  275. if ($this->stopOnError && $this->__error) {
  276. return false;
  277. }
  278. $verbose && $this->out('> ' . String::insert(__d('migrations', 'Changing column ":column"... ', true), array('column' => $columnName)), false);
  279. if ($this->_db->isInterfaceSupported('describe')) {
  280. $describe = $this->_db->describe($tableName, true);
  281. if (!isset($describe[$columnName])) {
  282. $verbose && $this->out(__d('migrations', 'column not found.', true));
  283. $this->__error = true;
  284. return false;
  285. }
  286. $newColumnConfig = array_merge($describe[$columnName], $newColumnConfig);
  287. }
  288. if ($this->_db->execute($this->_db->alterSchema(array(
  289. $tableName => array(
  290. 'change' => array(
  291. $columnName => $newColumnConfig
  292. )
  293. )
  294. ), $tableName))) {
  295. $verbose && $this->out('ok');
  296. return true;
  297. }
  298. $verbose && $this->out('nok');
  299. $this->__error = true;
  300. return false;
  301. }
  302. /**
  303. * Rename a column
  304. *
  305. * @param string $tableName
  306. * @param string $oldColumnName
  307. * @param string $newColumnName
  308. * @return boolean
  309. * @access public
  310. */
  311. function renameColumn($tableName, $oldColumnName, $newColumnName) {
  312. if ($this->stopOnError && $this->__error) {
  313. return false;
  314. }
  315. $this->out('> ' . String::insert(__d('migrations', 'Renaming column ":old" to ":new"...', true), array('old' => $oldColumnName, 'new' => $newColumnName)), false);
  316. if ($this->changeColumn($tableName, $oldColumnName, array('name' => $newColumnName), false)) {
  317. $this->out('ok');
  318. return true;
  319. }
  320. $this->out('nok');
  321. $this->__error = true;
  322. return false;
  323. }
  324. /**
  325. * Add new index
  326. *
  327. * @todo Need implement.
  328. * @return void
  329. * @access public
  330. */
  331. function addIndex() {
  332. }
  333. /**
  334. * Remove a index
  335. *
  336. * @todo Need implement.
  337. * @return void
  338. * @access public
  339. */
  340. function removeIndex() {
  341. }
  342. /**
  343. * Output a message to console
  344. *
  345. * @param string $message
  346. * @param boolean $newLine
  347. * @return void
  348. * @access public
  349. */
  350. function out($message, $newLine = true) {
  351. if ($this->_shell) {
  352. $this->_shell->out($message, $newLine);
  353. }
  354. }
  355. /**
  356. * Install revision
  357. *
  358. * @return boolean
  359. * @access public
  360. */
  361. function install() {
  362. return $this->_exec('up', 'Install');
  363. }
  364. /**
  365. * Uninstall revision
  366. *
  367. * @return boolean
  368. * @access public
  369. */
  370. function uninstall() {
  371. return $this->_exec('down', 'Uninstall');
  372. }
  373. /**
  374. * Execute Install and Uninstall methods
  375. *
  376. * @param string $command Can be 'up' or 'down'
  377. * @param string $callback Name of callback function
  378. * @return boolean
  379. * @access protected
  380. */
  381. function _exec($command, $callback) {
  382. $this->__error = false;
  383. if (!method_exists($this, $command)) {
  384. $this->out(String::insert(__d('migrations', '> Method ":method" not implemented. Skipping...', true), array('method' => $command)));
  385. return true;
  386. }
  387. $method = 'before' . $callback;
  388. if (method_exists($this, $method)) {
  389. if (!$this->$method()) {
  390. return false;
  391. }
  392. }
  393. $ok = $this->_db->begin($this->__fakeSchema);
  394. $this->$command();
  395. if ($this->stopOnError) {
  396. if ($this->__error) {
  397. $ok = false;
  398. }
  399. }
  400. if ($ok) {
  401. $this->_db->commit($this->__fakeSchema);
  402. } else {
  403. $this->_db->rollback($this->__fakeSchema);
  404. }
  405. $method = 'after' . $callback;
  406. if (method_exists($this, $method)) {
  407. $this->$method($ok);
  408. }
  409. return $ok;
  410. }
  411. }
  412. if (!class_exists('CakeSchema')) {
  413. class CakeSchema {}
  414. }
  415. ?>