You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
6.2 KiB

  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. use yii\base\InvalidConfigException;
  8. use yii\rbac\DbManager;
  9. /**
  10. * Initializes RBAC tables.
  11. *
  12. * @author Alexander Kochetov <creocoder@gmail.com>
  13. * @since 2.0
  14. */
  15. class m140506_102106_rbac_init extends \yii\db\Migration
  16. {
  17. /**
  18. * @throws yii\base\InvalidConfigException
  19. * @return DbManager
  20. */
  21. protected function getAuthManager()
  22. {
  23. $authManager = Yii::$app->getAuthManager();
  24. if (!$authManager instanceof DbManager) {
  25. throw new InvalidConfigException('You should configure "authManager" component to use database before executing this migration.');
  26. }
  27. return $authManager;
  28. }
  29. /**
  30. * @return bool
  31. */
  32. protected function isMSSQL()
  33. {
  34. return $this->db->driverName === 'mssql' || $this->db->driverName === 'sqlsrv' || $this->db->driverName === 'dblib';
  35. }
  36. protected function isOracle()
  37. {
  38. return $this->db->driverName === 'oci';
  39. }
  40. /**
  41. * {@inheritdoc}
  42. */
  43. public function up()
  44. {
  45. $authManager = $this->getAuthManager();
  46. $this->db = $authManager->db;
  47. $schema = $this->db->getSchema()->defaultSchema;
  48. $tableOptions = null;
  49. if ($this->db->driverName === 'mysql') {
  50. // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
  51. $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
  52. }
  53. $this->createTable($authManager->ruleTable, [
  54. 'name' => $this->string(64)->notNull(),
  55. 'data' => $this->binary(),
  56. 'created_at' => $this->integer(),
  57. 'updated_at' => $this->integer(),
  58. 'PRIMARY KEY ([[name]])',
  59. ], $tableOptions);
  60. $this->createTable($authManager->itemTable, [
  61. 'name' => $this->string(64)->notNull(),
  62. 'type' => $this->smallInteger()->notNull(),
  63. 'description' => $this->text(),
  64. 'rule_name' => $this->string(64),
  65. 'data' => $this->binary(),
  66. 'created_at' => $this->integer(),
  67. 'updated_at' => $this->integer(),
  68. 'PRIMARY KEY ([[name]])',
  69. 'FOREIGN KEY ([[rule_name]]) REFERENCES ' . $authManager->ruleTable . ' ([[name]])' .
  70. $this->buildFkClause('ON DELETE SET NULL', 'ON UPDATE CASCADE'),
  71. ], $tableOptions);
  72. $this->createIndex('idx-auth_item-type', $authManager->itemTable, 'type');
  73. $this->createTable($authManager->itemChildTable, [
  74. 'parent' => $this->string(64)->notNull(),
  75. 'child' => $this->string(64)->notNull(),
  76. 'PRIMARY KEY ([[parent]], [[child]])',
  77. 'FOREIGN KEY ([[parent]]) REFERENCES ' . $authManager->itemTable . ' ([[name]])' .
  78. $this->buildFkClause('ON DELETE CASCADE', 'ON UPDATE CASCADE'),
  79. 'FOREIGN KEY ([[child]]) REFERENCES ' . $authManager->itemTable . ' ([[name]])' .
  80. $this->buildFkClause('ON DELETE CASCADE', 'ON UPDATE CASCADE'),
  81. ], $tableOptions);
  82. $this->createTable($authManager->assignmentTable, [
  83. 'item_name' => $this->string(64)->notNull(),
  84. 'user_id' => $this->string(64)->notNull(),
  85. 'created_at' => $this->integer(),
  86. 'PRIMARY KEY ([[item_name]], [[user_id]])',
  87. 'FOREIGN KEY ([[item_name]]) REFERENCES ' . $authManager->itemTable . ' ([[name]])' .
  88. $this->buildFkClause('ON DELETE CASCADE', 'ON UPDATE CASCADE'),
  89. ], $tableOptions);
  90. if ($this->isMSSQL()) {
  91. $this->execute("CREATE TRIGGER {$schema}.trigger_auth_item_child
  92. ON {$schema}.{$authManager->itemTable}
  93. INSTEAD OF DELETE, UPDATE
  94. AS
  95. DECLARE @old_name VARCHAR (64) = (SELECT name FROM deleted)
  96. DECLARE @new_name VARCHAR (64) = (SELECT name FROM inserted)
  97. BEGIN
  98. IF COLUMNS_UPDATED() > 0
  99. BEGIN
  100. IF @old_name <> @new_name
  101. BEGIN
  102. ALTER TABLE {$authManager->itemChildTable} NOCHECK CONSTRAINT FK__auth_item__child;
  103. UPDATE {$authManager->itemChildTable} SET child = @new_name WHERE child = @old_name;
  104. END
  105. UPDATE {$authManager->itemTable}
  106. SET name = (SELECT name FROM inserted),
  107. type = (SELECT type FROM inserted),
  108. description = (SELECT description FROM inserted),
  109. rule_name = (SELECT rule_name FROM inserted),
  110. data = (SELECT data FROM inserted),
  111. created_at = (SELECT created_at FROM inserted),
  112. updated_at = (SELECT updated_at FROM inserted)
  113. WHERE name IN (SELECT name FROM deleted)
  114. IF @old_name <> @new_name
  115. BEGIN
  116. ALTER TABLE {$authManager->itemChildTable} CHECK CONSTRAINT FK__auth_item__child;
  117. END
  118. END
  119. ELSE
  120. BEGIN
  121. DELETE FROM {$schema}.{$authManager->itemChildTable} WHERE parent IN (SELECT name FROM deleted) OR child IN (SELECT name FROM deleted);
  122. DELETE FROM {$schema}.{$authManager->itemTable} WHERE name IN (SELECT name FROM deleted);
  123. END
  124. END;");
  125. }
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. public function down()
  131. {
  132. $authManager = $this->getAuthManager();
  133. $this->db = $authManager->db;
  134. if ($this->isMSSQL()) {
  135. $this->execute('DROP TRIGGER {$schema}.trigger_auth_item_child;');
  136. }
  137. $this->dropTable($authManager->assignmentTable);
  138. $this->dropTable($authManager->itemChildTable);
  139. $this->dropTable($authManager->itemTable);
  140. $this->dropTable($authManager->ruleTable);
  141. }
  142. protected function buildFkClause($delete = '', $update = '')
  143. {
  144. if ($this->isMSSQL()) {
  145. return '';
  146. }
  147. if ($this->isOracle()) {
  148. return ' ' . $delete;
  149. }
  150. return implode(' ', ['', $delete, $update]);
  151. }
  152. }