您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

437 行
20KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\db;
  8. use yii\base\Component;
  9. use yii\di\Instance;
  10. /**
  11. * Migration is the base class for representing a database migration.
  12. *
  13. * Migration is designed to be used together with the "yii migrate" command.
  14. *
  15. * Each child class of Migration represents an individual database migration which
  16. * is identified by the child class name.
  17. *
  18. * Within each migration, the [[up()]] method should be overridden to contain the logic
  19. * for "upgrading" the database; while the [[down()]] method for the "downgrading"
  20. * logic. The "yii migrate" command manages all available migrations in an application.
  21. *
  22. * If the database supports transactions, you may also override [[safeUp()]] and
  23. * [[safeDown()]] so that if anything wrong happens during the upgrading or downgrading,
  24. * the whole migration can be reverted in a whole.
  25. *
  26. * Migration provides a set of convenient methods for manipulating database data and schema.
  27. * For example, the [[insert()]] method can be used to easily insert a row of data into
  28. * a database table; the [[createTable()]] method can be used to create a database table.
  29. * Compared with the same methods in [[Command]], these methods will display extra
  30. * information showing the method parameters and execution time, which may be useful when
  31. * applying migrations.
  32. *
  33. * @author Qiang Xue <qiang.xue@gmail.com>
  34. * @since 2.0
  35. */
  36. class Migration extends Component implements MigrationInterface
  37. {
  38. /**
  39. * @var Connection|array|string the DB connection object or the application component ID of the DB connection
  40. * that this migration should work with. Starting from version 2.0.2, this can also be a configuration array
  41. * for creating the object.
  42. *
  43. * Note that when a Migration object is created by the `migrate` command, this property will be overwritten
  44. * by the command. If you do not want to use the DB connection provided by the command, you may override
  45. * the [[init()]] method like the following:
  46. *
  47. * ```php
  48. * public function init()
  49. * {
  50. * $this->db = 'db2';
  51. * parent::init();
  52. * }
  53. * ```
  54. */
  55. public $db = 'db';
  56. /**
  57. * Initializes the migration.
  58. * This method will set [[db]] to be the 'db' application component, if it is null.
  59. */
  60. public function init()
  61. {
  62. parent::init();
  63. $this->db = Instance::ensure($this->db, Connection::className());
  64. }
  65. /**
  66. * This method contains the logic to be executed when applying this migration.
  67. * Child classes may override this method to provide actual migration logic.
  68. * @return boolean return a false value to indicate the migration fails
  69. * and should not proceed further. All other return values mean the migration succeeds.
  70. */
  71. public function up()
  72. {
  73. $transaction = $this->db->beginTransaction();
  74. try {
  75. if ($this->safeUp() === false) {
  76. $transaction->rollBack();
  77. return false;
  78. }
  79. $transaction->commit();
  80. } catch (\Exception $e) {
  81. echo "Exception: " . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n";
  82. echo $e->getTraceAsString() . "\n";
  83. $transaction->rollBack();
  84. return false;
  85. }
  86. return null;
  87. }
  88. /**
  89. * This method contains the logic to be executed when removing this migration.
  90. * The default implementation throws an exception indicating the migration cannot be removed.
  91. * Child classes may override this method if the corresponding migrations can be removed.
  92. * @return boolean return a false value to indicate the migration fails
  93. * and should not proceed further. All other return values mean the migration succeeds.
  94. */
  95. public function down()
  96. {
  97. $transaction = $this->db->beginTransaction();
  98. try {
  99. if ($this->safeDown() === false) {
  100. $transaction->rollBack();
  101. return false;
  102. }
  103. $transaction->commit();
  104. } catch (\Exception $e) {
  105. echo "Exception: " . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n";
  106. echo $e->getTraceAsString() . "\n";
  107. $transaction->rollBack();
  108. return false;
  109. }
  110. return null;
  111. }
  112. /**
  113. * This method contains the logic to be executed when applying this migration.
  114. * This method differs from [[up()]] in that the DB logic implemented here will
  115. * be enclosed within a DB transaction.
  116. * Child classes may implement this method instead of [[up()]] if the DB logic
  117. * needs to be within a transaction.
  118. * @return boolean return a false value to indicate the migration fails
  119. * and should not proceed further. All other return values mean the migration succeeds.
  120. */
  121. public function safeUp()
  122. {
  123. }
  124. /**
  125. * This method contains the logic to be executed when removing this migration.
  126. * This method differs from [[down()]] in that the DB logic implemented here will
  127. * be enclosed within a DB transaction.
  128. * Child classes may implement this method instead of [[up()]] if the DB logic
  129. * needs to be within a transaction.
  130. * @return boolean return a false value to indicate the migration fails
  131. * and should not proceed further. All other return values mean the migration succeeds.
  132. */
  133. public function safeDown()
  134. {
  135. }
  136. /**
  137. * Executes a SQL statement.
  138. * This method executes the specified SQL statement using [[db]].
  139. * @param string $sql the SQL statement to be executed
  140. * @param array $params input parameters (name => value) for the SQL execution.
  141. * See [[Command::execute()]] for more details.
  142. */
  143. public function execute($sql, $params = [])
  144. {
  145. echo " > execute SQL: $sql ...";
  146. $time = microtime(true);
  147. $this->db->createCommand($sql)->bindValues($params)->execute();
  148. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  149. }
  150. /**
  151. * Creates and executes an INSERT SQL statement.
  152. * The method will properly escape the column names, and bind the values to be inserted.
  153. * @param string $table the table that new rows will be inserted into.
  154. * @param array $columns the column data (name => value) to be inserted into the table.
  155. */
  156. public function insert($table, $columns)
  157. {
  158. echo " > insert into $table ...";
  159. $time = microtime(true);
  160. $this->db->createCommand()->insert($table, $columns)->execute();
  161. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  162. }
  163. /**
  164. * Creates and executes an batch INSERT SQL statement.
  165. * The method will properly escape the column names, and bind the values to be inserted.
  166. * @param string $table the table that new rows will be inserted into.
  167. * @param array $columns the column names.
  168. * @param array $rows the rows to be batch inserted into the table
  169. */
  170. public function batchInsert($table, $columns, $rows)
  171. {
  172. echo " > insert into $table ...";
  173. $time = microtime(true);
  174. $this->db->createCommand()->batchInsert($table, $columns, $rows)->execute();
  175. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  176. }
  177. /**
  178. * Creates and executes an UPDATE SQL statement.
  179. * The method will properly escape the column names and bind the values to be updated.
  180. * @param string $table the table to be updated.
  181. * @param array $columns the column data (name => value) to be updated.
  182. * @param array|string $condition the conditions that will be put in the WHERE part. Please
  183. * refer to [[Query::where()]] on how to specify conditions.
  184. * @param array $params the parameters to be bound to the query.
  185. */
  186. public function update($table, $columns, $condition = '', $params = [])
  187. {
  188. echo " > update $table ...";
  189. $time = microtime(true);
  190. $this->db->createCommand()->update($table, $columns, $condition, $params)->execute();
  191. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  192. }
  193. /**
  194. * Creates and executes a DELETE SQL statement.
  195. * @param string $table the table where the data will be deleted from.
  196. * @param array|string $condition the conditions that will be put in the WHERE part. Please
  197. * refer to [[Query::where()]] on how to specify conditions.
  198. * @param array $params the parameters to be bound to the query.
  199. */
  200. public function delete($table, $condition = '', $params = [])
  201. {
  202. echo " > delete from $table ...";
  203. $time = microtime(true);
  204. $this->db->createCommand()->delete($table, $condition, $params)->execute();
  205. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  206. }
  207. /**
  208. * Builds and executes a SQL statement for creating a new DB table.
  209. *
  210. * The columns in the new table should be specified as name-definition pairs (e.g. 'name' => 'string'),
  211. * where name stands for a column name which will be properly quoted by the method, and definition
  212. * stands for the column type which can contain an abstract DB type.
  213. *
  214. * The [[QueryBuilder::getColumnType()]] method will be invoked to convert any abstract type into a physical one.
  215. *
  216. * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly
  217. * put into the generated SQL.
  218. *
  219. * @param string $table the name of the table to be created. The name will be properly quoted by the method.
  220. * @param array $columns the columns (name => definition) in the new table.
  221. * @param string $options additional SQL fragment that will be appended to the generated SQL.
  222. */
  223. public function createTable($table, $columns, $options = null)
  224. {
  225. echo " > create table $table ...";
  226. $time = microtime(true);
  227. $this->db->createCommand()->createTable($table, $columns, $options)->execute();
  228. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  229. }
  230. /**
  231. * Builds and executes a SQL statement for renaming a DB table.
  232. * @param string $table the table to be renamed. The name will be properly quoted by the method.
  233. * @param string $newName the new table name. The name will be properly quoted by the method.
  234. */
  235. public function renameTable($table, $newName)
  236. {
  237. echo " > rename table $table to $newName ...";
  238. $time = microtime(true);
  239. $this->db->createCommand()->renameTable($table, $newName)->execute();
  240. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  241. }
  242. /**
  243. * Builds and executes a SQL statement for dropping a DB table.
  244. * @param string $table the table to be dropped. The name will be properly quoted by the method.
  245. */
  246. public function dropTable($table)
  247. {
  248. echo " > drop table $table ...";
  249. $time = microtime(true);
  250. $this->db->createCommand()->dropTable($table)->execute();
  251. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  252. }
  253. /**
  254. * Builds and executes a SQL statement for truncating a DB table.
  255. * @param string $table the table to be truncated. The name will be properly quoted by the method.
  256. */
  257. public function truncateTable($table)
  258. {
  259. echo " > truncate table $table ...";
  260. $time = microtime(true);
  261. $this->db->createCommand()->truncateTable($table)->execute();
  262. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  263. }
  264. /**
  265. * Builds and executes a SQL statement for adding a new DB column.
  266. * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.
  267. * @param string $column the name of the new column. The name will be properly quoted by the method.
  268. * @param string $type the column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)
  269. * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
  270. * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
  271. */
  272. public function addColumn($table, $column, $type)
  273. {
  274. echo " > add column $column $type to table $table ...";
  275. $time = microtime(true);
  276. $this->db->createCommand()->addColumn($table, $column, $type)->execute();
  277. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  278. }
  279. /**
  280. * Builds and executes a SQL statement for dropping a DB column.
  281. * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
  282. * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
  283. */
  284. public function dropColumn($table, $column)
  285. {
  286. echo " > drop column $column from table $table ...";
  287. $time = microtime(true);
  288. $this->db->createCommand()->dropColumn($table, $column)->execute();
  289. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  290. }
  291. /**
  292. * Builds and executes a SQL statement for renaming a column.
  293. * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
  294. * @param string $name the old name of the column. The name will be properly quoted by the method.
  295. * @param string $newName the new name of the column. The name will be properly quoted by the method.
  296. */
  297. public function renameColumn($table, $name, $newName)
  298. {
  299. echo " > rename column $name in table $table to $newName ...";
  300. $time = microtime(true);
  301. $this->db->createCommand()->renameColumn($table, $name, $newName)->execute();
  302. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  303. }
  304. /**
  305. * Builds and executes a SQL statement for changing the definition of a column.
  306. * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
  307. * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
  308. * @param string $type the new column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)
  309. * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
  310. * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
  311. */
  312. public function alterColumn($table, $column, $type)
  313. {
  314. echo " > alter column $column in table $table to $type ...";
  315. $time = microtime(true);
  316. $this->db->createCommand()->alterColumn($table, $column, $type)->execute();
  317. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  318. }
  319. /**
  320. * Builds and executes a SQL statement for creating a primary key.
  321. * The method will properly quote the table and column names.
  322. * @param string $name the name of the primary key constraint.
  323. * @param string $table the table that the primary key constraint will be added to.
  324. * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
  325. */
  326. public function addPrimaryKey($name, $table, $columns)
  327. {
  328. echo " > add primary key $name on $table (" . (is_array($columns) ? implode(',', $columns) : $columns).") ...";
  329. $time = microtime(true);
  330. $this->db->createCommand()->addPrimaryKey($name, $table, $columns)->execute();
  331. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  332. }
  333. /**
  334. * Builds and executes a SQL statement for dropping a primary key.
  335. * @param string $name the name of the primary key constraint to be removed.
  336. * @param string $table the table that the primary key constraint will be removed from.
  337. */
  338. public function dropPrimaryKey($name, $table)
  339. {
  340. echo " > drop primary key $name ...";
  341. $time = microtime(true);
  342. $this->db->createCommand()->dropPrimaryKey($name, $table)->execute();
  343. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  344. }
  345. /**
  346. * Builds a SQL statement for adding a foreign key constraint to an existing table.
  347. * The method will properly quote the table and column names.
  348. * @param string $name the name of the foreign key constraint.
  349. * @param string $table the table that the foreign key constraint will be added to.
  350. * @param string|array $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas or use an array.
  351. * @param string $refTable the table that the foreign key references to.
  352. * @param string|array $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas or use an array.
  353. * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
  354. * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
  355. */
  356. public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
  357. {
  358. echo " > add foreign key $name: $table (" . implode(',', (array) $columns) . ") references $refTable (" . implode(',', (array) $refColumns) . ") ...";
  359. $time = microtime(true);
  360. $this->db->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update)->execute();
  361. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  362. }
  363. /**
  364. * Builds a SQL statement for dropping a foreign key constraint.
  365. * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
  366. * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
  367. */
  368. public function dropForeignKey($name, $table)
  369. {
  370. echo " > drop foreign key $name from table $table ...";
  371. $time = microtime(true);
  372. $this->db->createCommand()->dropForeignKey($name, $table)->execute();
  373. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  374. }
  375. /**
  376. * Builds and executes a SQL statement for creating a new index.
  377. * @param string $name the name of the index. The name will be properly quoted by the method.
  378. * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
  379. * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns, please separate them
  380. * by commas or use an array. The column names will be properly quoted by the method.
  381. * @param boolean $unique whether to add UNIQUE constraint on the created index.
  382. */
  383. public function createIndex($name, $table, $columns, $unique = false)
  384. {
  385. echo " > create" . ($unique ? ' unique' : '') . " index $name on $table (" . implode(',', (array) $columns) . ") ...";
  386. $time = microtime(true);
  387. $this->db->createCommand()->createIndex($name, $table, $columns, $unique)->execute();
  388. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  389. }
  390. /**
  391. * Builds and executes a SQL statement for dropping an index.
  392. * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
  393. * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
  394. */
  395. public function dropIndex($name, $table)
  396. {
  397. echo " > drop index $name ...";
  398. $time = microtime(true);
  399. $this->db->createCommand()->dropIndex($name, $table)->execute();
  400. echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
  401. }
  402. }