Complex classes like MigrationController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MigrationController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 21 | class MigrationController extends AbstractActionController |
||
| 22 | { |
||
| 23 | const UPGRADE_KEY = 'up'; |
||
| 24 | const DOWNGRADE_KEY = 'down'; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Destination to folder with migration files |
||
| 28 | * |
||
| 29 | * @var string |
||
| 30 | */ |
||
| 31 | protected $migrationFolder = null; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * Execute one migration |
||
| 35 | * |
||
| 36 | */ |
||
| 37 | public function executeAction() |
||
| 38 | { |
||
| 39 | /* @var $console Console */ |
||
| 40 | $console = $this->getServiceLocator()->get('console'); |
||
| 41 | $request = $this->getRequest(); |
||
| 42 | $migration = $request->getParam('number'); |
||
| 43 | $percona = $request->getParam('percona'); |
||
| 44 | $port = $request->getParam('port'); |
||
| 45 | |||
| 46 | $migrationPath = $this->getMigrationFolder(); |
||
| 47 | $filePath = $migrationPath . $migration . '.php'; |
||
| 48 | |||
| 49 | if (!file_exists($filePath)) { |
||
| 50 | $console->writeLine('Migration does not exists: ' . $filePath, Color::RED); |
||
| 51 | } else { |
||
| 52 | if (!$this->isApplySchema($console)) { |
||
| 53 | false; |
||
| 54 | } |
||
| 55 | |||
| 56 | $migrationArray = $this->includeMigration($filePath); |
||
| 57 | |||
| 58 | if ($request->getParam(self::UPGRADE_KEY)) { |
||
| 59 | $this->applyMigration(self::UPGRADE_KEY, $migration, $migrationArray, $percona, $port); |
||
| 60 | } elseif ($request->getParam(self::DOWNGRADE_KEY)) { |
||
| 61 | $this->applyMigration(self::DOWNGRADE_KEY, $migration, $migrationArray, $percona, $port); |
||
| 62 | } |
||
| 63 | } |
||
| 64 | } |
||
| 65 | |||
| 66 | /** |
||
| 67 | * Show confirm for migration |
||
| 68 | * |
||
| 69 | * @param string $action |
||
| 70 | * @param string $migration |
||
| 71 | * @param array $migrationArray |
||
| 72 | * @return bool |
||
| 73 | */ |
||
| 74 | protected function applyMigration($action, $migration, $migrationArray, $percona, $port) |
||
| 75 | { |
||
| 76 | /* @var $adapter \Zend\Db\Adapter\Adapter */ |
||
| 77 | /* @var $console Console */ |
||
| 78 | $adapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); |
||
| 79 | $console = $this->getServiceLocator()->get('console'); |
||
| 80 | $model = new Migration($adapter, $this->getServiceLocator(), $percona, $port); |
||
|
|
|||
| 81 | $methodName = $action == self::UPGRADE_KEY ? 'upgrade' : 'downgrade'; |
||
| 82 | |||
| 83 | $console->writeLine(); |
||
| 84 | $console->write('Current migration: '); |
||
| 85 | $console->writeLine($this->getMigrationFolder() . $migration . '.php', Color::YELLOW); |
||
| 86 | $console->writeLine($migrationArray[$action], Color::BLUE); |
||
| 87 | $exist = $model->get(array('migration' => $migration)); |
||
| 88 | $doNotSaveAsExecuted = false; |
||
| 89 | if (!empty($exist) && empty($exist['ignored'])) { |
||
| 90 | $console->writeLine('This migration was already executed', Color::YELLOW); |
||
| 91 | $doNotSaveAsExecuted = true; |
||
| 92 | } elseif (!empty($exist) && !empty($exist['ignored'])) { |
||
| 93 | $console->writeLine('This migration was already pseudo-executed (ignored)', Color::LIGHT_CYAN); |
||
| 94 | } |
||
| 95 | $answer = Char::prompt('Apply this migration (Yes / no / ignore forever)? [y/n/i]', 'yni'); |
||
| 96 | switch ($answer) { |
||
| 97 | case 'y': |
||
| 98 | if ($action == self::UPGRADE_KEY) { |
||
| 99 | $model->$methodName($migration, $migrationArray, $ignore = false, $doNotSaveAsExecuted); |
||
| 100 | $console->writeLine('This migration successful upgraded', Color::GREEN); |
||
| 101 | } else { |
||
| 102 | $model->$methodName($migration, $migrationArray, $ignore = false); |
||
| 103 | $console->writeLine('This migration successful downgraded', Color::GREEN); |
||
| 104 | } |
||
| 105 | break; |
||
| 106 | case 'i': |
||
| 107 | $model->$methodName($migration, $migrationArray, $ignore = true); |
||
| 108 | if ($action == self::UPGRADE_KEY) { |
||
| 109 | $console->writeLine('This migration pseudo-upgraded', Color::LIGHT_CYAN); |
||
| 110 | } else { |
||
| 111 | $console->writeLine('This migration pseudo-downgraded', Color::LIGHT_CYAN); |
||
| 112 | } |
||
| 113 | break; |
||
| 114 | case 'n': |
||
| 115 | default: |
||
| 116 | $console->writeLine('This migration discarded', Color::RED); |
||
| 117 | return false; |
||
| 118 | break; |
||
| 119 | } |
||
| 120 | |||
| 121 | return true; |
||
| 122 | } |
||
| 123 | |||
| 124 | /** |
||
| 125 | * Create new migration file |
||
| 126 | * |
||
| 127 | * @throws RuntimeException |
||
| 128 | */ |
||
| 129 | public function createAction() |
||
| 130 | { |
||
| 131 | $console = $this->getServiceLocator()->get('console'); |
||
| 132 | |||
| 133 | if (!$console instanceof Console) { |
||
| 134 | throw new RuntimeException('Cannot obtain console adapter. Are we running in a console?'); |
||
| 135 | } |
||
| 136 | $request = $this->getRequest(); |
||
| 137 | $short_name = $request->getParam('short_name', ''); |
||
| 138 | |||
| 139 | $config = $this->getServiceLocator()->get('Config'); |
||
| 140 | if (isset($config['console-tools']['migration_template'])) { |
||
| 141 | $dateTemplate = $config['console-tools']['migration_template']; |
||
| 142 | } else { |
||
| 143 | $dateTemplate = 'YmdHis'; |
||
| 144 | } |
||
| 145 | |||
| 146 | $migrationPath = $this->getMigrationFolder(); |
||
| 147 | if (!is_dir($migrationPath)) { |
||
| 148 | mkdir($migrationPath, 0777); |
||
| 149 | } |
||
| 150 | |||
| 151 | $date = new \DateTime(); |
||
| 152 | if ($short_name) { |
||
| 153 | $short_name = '_' . $short_name; |
||
| 154 | } |
||
| 155 | |||
| 156 | $migrationName = $date->format($dateTemplate) . $short_name . '.php'; |
||
| 157 | |||
| 158 | $migrationContent = <<<EOD |
||
| 159 | <?php |
||
| 160 | |||
| 161 | return [ |
||
| 162 | 'up' => "", |
||
| 163 | 'down' => "" |
||
| 164 | ]; |
||
| 165 | |||
| 166 | EOD; |
||
| 167 | |||
| 168 | file_put_contents($migrationPath . $migrationName, $migrationContent); |
||
| 169 | |||
| 170 | $console->writeLine('Created migration file: ' . $migrationPath . $migrationName, Color::GREEN); |
||
| 171 | } |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Upgrade/downgrade to migration |
||
| 175 | * |
||
| 176 | * @throws RuntimeException |
||
| 177 | */ |
||
| 178 | public function upgradeAction() |
||
| 179 | { |
||
| 180 | $console = $this->getServiceLocator()->get('console'); |
||
| 181 | if (!$console instanceof Console) { |
||
| 182 | throw new RuntimeException('Cannot obtain console adapter. Are we running in a console?'); |
||
| 183 | } |
||
| 184 | |||
| 185 | $adapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); |
||
| 186 | $request = $this->getRequest(); |
||
| 187 | $toMigration = $request->getParam('number', 'all'); |
||
| 188 | $percona = $request->getParam('percona'); |
||
| 189 | $port = $request->getParam('port'); |
||
| 190 | $model = new Migration($adapter, $this->getServiceLocator(), $percona, $port); |
||
| 191 | $migrationsFromBase = $model->applied(); |
||
| 192 | $migrationFolderPath = $this->getMigrationFolder(); |
||
| 193 | $files = array(); |
||
| 194 | |||
| 195 | if (!$this->isApplySchema($console, $adapter)) { |
||
| 196 | false; |
||
| 197 | } |
||
| 198 | |||
| 199 | if ($toMigration == 'all') { |
||
| 200 | $filesDirty = scandir($migrationFolderPath); |
||
| 201 | for ($i=0; $i<count($filesDirty); $i++) { |
||
| 202 | if (substr($filesDirty[$i], 0, 1) != '.') { |
||
| 203 | array_push($files, substr($filesDirty[$i], 0, -4)); |
||
| 204 | } |
||
| 205 | } |
||
| 206 | $filesFromDrive = array_diff($files, $migrationsFromBase); |
||
| 207 | asort($files, SORT_NATURAL); |
||
| 208 | $files = array_diff($filesFromDrive, $migrationsFromBase); |
||
| 209 | asort($files, SORT_NATURAL); |
||
| 210 | $upgradeAction = self::UPGRADE_KEY; |
||
| 211 | } elseif (in_array($toMigration, $migrationsFromBase)) { |
||
| 212 | $key = array_search($toMigration, $migrationsFromBase); |
||
| 213 | $files = array_slice($migrationsFromBase, $key); |
||
| 214 | rsort($files, SORT_NATURAL); |
||
| 215 | $upgradeAction = self::DOWNGRADE_KEY; |
||
| 216 | } else { |
||
| 217 | $console->writeLine('Did not apply the migration: ' . $toMigration, Color::RED); |
||
| 218 | return false; |
||
| 219 | } |
||
| 220 | |||
| 221 | if (!count($files)) { |
||
| 222 | $console->writeLine('You have last version of database', Color::GREEN); |
||
| 223 | return false; |
||
| 224 | } |
||
| 225 | |||
| 226 | foreach ($files as $migration) { |
||
| 227 | $migrationPath = $migrationFolderPath . |
||
| 228 | DIRECTORY_SEPARATOR . $migration . '.php'; |
||
| 229 | |||
| 230 | $migrationArray = $this->includeMigration($migrationPath); |
||
| 231 | |||
| 232 | try { |
||
| 233 | switch ($upgradeAction) { |
||
| 234 | case self::DOWNGRADE_KEY: |
||
| 235 | //downgrade action |
||
| 236 | $this->applyMigration(self::DOWNGRADE_KEY, $migration, $migrationArray, $percona, $port); |
||
| 237 | break; |
||
| 238 | case self::UPGRADE_KEY: |
||
| 239 | //upgrade action |
||
| 240 | $this->applyMigration(self::UPGRADE_KEY, $migration, $migrationArray, $percona, $port); |
||
| 241 | break; |
||
| 242 | default: |
||
| 243 | throw new \Exception('Not set action'); |
||
| 244 | break; |
||
| 245 | } |
||
| 246 | continue; |
||
| 247 | } catch (\Exception $err) { |
||
| 248 | $console->writeLine('Current migration failed commit', Color::RED); |
||
| 249 | $console->writeLine($err->getMessage(), Color::RED); |
||
| 250 | return false; |
||
| 251 | } |
||
| 252 | } |
||
| 253 | } |
||
| 254 | |||
| 255 | /** |
||
| 256 | * Show last applied migration number |
||
| 257 | * |
||
| 258 | */ |
||
| 259 | public function lastAction() |
||
| 260 | { |
||
| 261 | $console = $this->getServiceLocator()->get('console'); |
||
| 262 | if (!$console instanceof Console) { |
||
| 263 | throw new RuntimeException('Cannot obtain console adapter. Are we running in a console?'); |
||
| 264 | } |
||
| 265 | |||
| 266 | $request = $this->getRequest(); |
||
| 267 | $adapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); |
||
| 268 | $model = new Migration($adapter, $this->getServiceLocator()); |
||
| 269 | $lastMigration = $model->last(); |
||
| 270 | |||
| 271 | $migrationName = $this->getMigrationFolder() . $lastMigration->last . '.php'; |
||
| 272 | if ($request->getParam('show')) { |
||
| 273 | $console->writeLine('Last applied the migration: ' . $migrationName, Color::GREEN); |
||
| 274 | $console->writeLine('=== Up SQL ===', Color::YELLOW); |
||
| 275 | $console->writeLine($lastMigration->up, Color::GREEN); |
||
| 276 | $console->writeLine('=== Down SQL ===', Color::YELLOW); |
||
| 277 | $console->writeLine($lastMigration->down, Color::GREEN); |
||
| 278 | } else { |
||
| 279 | $console->writeLine('Last applied the migration: ' . $migrationName, Color::GREEN); |
||
| 280 | } |
||
| 281 | } |
||
| 282 | |||
| 283 | /** |
||
| 284 | * @param Console|null $console |
||
| 285 | * @param Adapter|null $adapter |
||
| 286 | * @return bool |
||
| 287 | */ |
||
| 288 | protected function isApplySchema(Console $console = null, Adapter $adapter = null) |
||
| 289 | { |
||
| 290 | if ($console === null) { |
||
| 291 | $console = $this->getServiceLocator()->get('console'); |
||
| 292 | } |
||
| 293 | if ($adapter === null) { |
||
| 294 | $adapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); |
||
| 295 | } |
||
| 296 | |||
| 297 | $console->writeLine('Current DSN: ' . $adapter->getDriver()->getConnection()->getDsn(), Color::GREEN); |
||
| 298 | $answer = Char::prompt('The schema is correct (Yes/No)? [y/n]', 'yn'); |
||
| 299 | if ($answer == 'n') { |
||
| 300 | return false; |
||
| 301 | } |
||
| 302 | return true; |
||
| 303 | } |
||
| 304 | |||
| 305 | /** |
||
| 306 | * Get migration folder from config file |
||
| 307 | * |
||
| 308 | * @return string |
||
| 309 | */ |
||
| 310 | protected function getMigrationFolder() |
||
| 319 | |||
| 320 | /** |
||
| 321 | * @param $migrationPath |
||
| 322 | * @return mixed |
||
| 323 | */ |
||
| 324 | protected function includeMigration($migrationPath) |
||
| 325 | { |
||
| 330 | } |
||
| 331 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: