Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
106 / 106 |
|
98.63% |
72 / 73 |
|
3.36% |
32 / 953 |
|
93.33% |
14 / 15 |
CRAP | |
0.00% |
0 / 1 |
| Migrations | |
100.00% |
106 / 106 |
|
98.63% |
72 / 73 |
|
3.36% |
32 / 953 |
|
100.00% |
15 / 15 |
1634.20 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| __invoke | |
100.00% |
41 / 41 |
|
100.00% |
33 / 33 |
|
1.19% |
11 / 926 |
|
100.00% |
1 / 1 |
295.82 | |||
| migrate | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| confirmTestRunForPending | |
100.00% |
9 / 9 |
|
100.00% |
6 / 6 |
|
50.00% |
2 / 4 |
|
100.00% |
1 / 1 |
4.12 | |||
| migrationsForNamespace | |
100.00% |
20 / 20 |
|
93.33% |
14 / 15 |
|
85.71% |
6 / 7 |
|
100.00% |
1 / 1 |
7.14 | |||
| migrationIdsAreUnique | |
100.00% |
6 / 6 |
|
100.00% |
7 / 7 |
|
40.00% |
2 / 5 |
|
100.00% |
1 / 1 |
4.94 | |||
| createMigrationsTable | |
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| supportsTransactions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| driverPolicy | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| planner | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| phpLoader | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| log | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| plan | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| executor | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| runner | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celema\Quma\Commands; |
| 6 | |
| 7 | use Celema\Console\Args; |
| 8 | use Celema\Console\Command; |
| 9 | use Celema\Console\Io; |
| 10 | use Celema\Console\Opt; |
| 11 | use Celema\Quma\Connection; |
| 12 | use Celema\Quma\Contract; |
| 13 | use Celema\Quma\Environment; |
| 14 | use Celema\Quma\Migrations\DriverPolicy; |
| 15 | use Celema\Quma\Migrations\Executor; |
| 16 | use Celema\Quma\Migrations\Log; |
| 17 | use Celema\Quma\Migrations\MetadataTable; |
| 18 | use Celema\Quma\Migrations\PhpLoader; |
| 19 | use Celema\Quma\Migrations\Plan; |
| 20 | use Celema\Quma\Migrations\Planner; |
| 21 | use Celema\Quma\Migrations\Runner; |
| 22 | use Celema\Quma\Migrations\RunOptions; |
| 23 | use Celema\Quma\Migrations\TestRunConfirmation; |
| 24 | |
| 25 | #[Command('db:migrations', 'Apply missing database migrations', group: 'Database')] |
| 26 | #[Opt('--apply', 'Apply the pending migrations instead of only planning them')] |
| 27 | #[Opt( |
| 28 | '--test-run', |
| 29 | 'Run pending migrations inside a transaction and roll back (sqlite/pgsql only)', |
| 30 | )] |
| 31 | #[Opt('--namespace', 'Migration namespace to run', value: 'name')] |
| 32 | #[Opt('--conn', 'Connection to use', value: 'name')] |
| 33 | #[Opt('--stacktrace', 'Show stack traces for failing migrations')] |
| 34 | #[Opt('--yes', 'Skip the test-run confirmation prompt')] |
| 35 | final class Migrations |
| 36 | { |
| 37 | protected readonly Environment $env; |
| 38 | protected readonly ?Contract\MigrationFactory $migrationFactory; |
| 39 | |
| 40 | /** @psalm-suppress PropertyNotSetInConstructor Assigned first thing in __invoke() */ |
| 41 | protected Io $io; |
| 42 | |
| 43 | /** @param array<non-empty-string, Connection>|Connection $conn */ |
| 44 | public function __construct( |
| 45 | array|Connection $conn, |
| 46 | array $options = [], |
| 47 | ?Contract\MigrationFactory $migrationFactory = null, |
| 48 | ) { |
| 49 | $this->env = new Environment($conn, $options); |
| 50 | $this->migrationFactory = $migrationFactory; |
| 51 | } |
| 52 | |
| 53 | public function __invoke(Args $args, Io $io): int |
| 54 | { |
| 55 | $this->io = $io; |
| 56 | $env = $this->env; |
| 57 | $namespace = $args->opt('--namespace', ''); |
| 58 | $showStacktrace = $args->has('--stacktrace'); |
| 59 | $apply = $args->has('--apply'); |
| 60 | $testRun = $args->has('--test-run'); |
| 61 | $yes = $args->has('--yes'); |
| 62 | $driverSupported = $this->driverPolicy()->isKnown(); |
| 63 | |
| 64 | if ($apply && $testRun) { |
| 65 | $io->echolnErr( |
| 66 | '<bright-red>Error</bright-red>: Options --apply and --test-run cannot be used together.', |
| 67 | ); |
| 68 | |
| 69 | return 1; |
| 70 | } |
| 71 | |
| 72 | if ($testRun && (!$driverSupported || !$this->supportsTransactions())) { |
| 73 | $io->echolnErr( |
| 74 | '<bright-red>Error</bright-red>: Test runs are only supported for transactional drivers: sqlite and pgsql.', |
| 75 | ); |
| 76 | $io->echolnErr( |
| 77 | 'MySQL migrations are plan-only without --apply because DDL statements can cause implicit commits.', |
| 78 | ); |
| 79 | |
| 80 | return 1; |
| 81 | } |
| 82 | |
| 83 | $migrations = $this->migrationsForNamespace($namespace); |
| 84 | |
| 85 | if ($migrations === false) { |
| 86 | return 1; |
| 87 | } |
| 88 | |
| 89 | $migrationNamespace = $namespace !== '' ? $namespace : 'default'; |
| 90 | $tableExists = $driverSupported && $env->checkIfMigrationsTableExists($env->db); |
| 91 | |
| 92 | if (!$apply && !$testRun) { |
| 93 | return $this->plan()->show($migrationNamespace, $migrations, $tableExists); |
| 94 | } |
| 95 | |
| 96 | if ( |
| 97 | !$apply && !$this->confirmTestRunForPending($migrationNamespace, $migrations, $tableExists, $yes) |
| 98 | ) { |
| 99 | return 1; |
| 100 | } |
| 101 | |
| 102 | if ($driverSupported && !$tableExists && !$this->supportsTransactions()) { |
| 103 | $result = $this->createMigrationsTable(); |
| 104 | |
| 105 | if ($result !== 0) { |
| 106 | // Requires simulating failing metadata table creation. |
| 107 | return $result; // @codeCoverageIgnore |
| 108 | } |
| 109 | |
| 110 | $tableExists = true; |
| 111 | } |
| 112 | |
| 113 | return $this->migrate( |
| 114 | $migrationNamespace, |
| 115 | $migrations, |
| 116 | $showStacktrace, |
| 117 | $apply, |
| 118 | $tableExists, |
| 119 | ); |
| 120 | } |
| 121 | |
| 122 | /** @param list<string> $migrations */ |
| 123 | protected function migrate( |
| 124 | string $namespace, |
| 125 | array $migrations, |
| 126 | bool $showStacktrace, |
| 127 | bool $apply, |
| 128 | bool $tableExists, |
| 129 | ): int { |
| 130 | return $this->runner()->run( |
| 131 | $namespace, |
| 132 | $migrations, |
| 133 | new RunOptions( |
| 134 | $showStacktrace, |
| 135 | $apply, |
| 136 | $tableExists, |
| 137 | $this->createMigrationsTable(...), |
| 138 | ), |
| 139 | ); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * @param list<string> $migrations |
| 144 | */ |
| 145 | protected function confirmTestRunForPending( |
| 146 | string $namespace, |
| 147 | array $migrations, |
| 148 | bool $tableExists, |
| 149 | bool $yes, |
| 150 | ): bool { |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 152 | $pendingMigrations = $this->planner()->pendingMigrations( |
| 153 | $namespace, |
| 154 | $migrations, |
| 155 | $appliedMigrations, |
| 156 | ); |
| 157 | |
| 158 | if (count($pendingMigrations) === 0) { |
| 159 | return true; |
| 160 | } |
| 161 | |
| 162 | return new TestRunConfirmation()->confirm($this->io, $yes); |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * @return list<string>|false |
| 167 | */ |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 173 | $this->io->error('No migration directories defined in configuration'); |
| 174 | |
| 175 | return false; |
| 176 | } |
| 177 | |
| 178 | if ($namespace) { |
| 179 | if (!array_key_exists($namespace, $migrationNamespaces)) { |
| 180 | $this->io->error("Migration namespace '{$namespace}' does not exist"); |
| 181 | |
| 182 | return false; |
| 183 | } |
| 184 | |
| 185 | $migrations = $migrationNamespaces[$namespace]; |
| 186 | |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 188 | } |
| 189 | |
| 190 | if (!array_key_exists('default', $migrationNamespaces)) { |
| 191 | $this->io->error("Migration namespace 'default' does not exist"); |
| 192 | $this->io->info( |
| 193 | 'If you have defined namespaced migrations, you must either provide a namespace using the ' |
| 194 | . "`--namespace` flag when running this command, or define a namespace named 'default' which " |
| 195 | . 'will be used when no namespace is provided.', |
| 196 | ); |
| 197 | |
| 198 | return false; |
| 199 | } |
| 200 | |
| 201 | $migrations = $migrationNamespaces['default']; |
| 202 | |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 204 | } |
| 205 | |
| 206 | /** @param list<string> $migrations */ |
| 207 | protected function migrationIdsAreUnique(string $namespace, array $migrations): bool |
| 208 | { |
| 209 | $duplicates = $this->planner()->duplicateMigrationIds($namespace, $migrations); |
| 210 | |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return count($duplicates) === 0; |
| 220 | } |
| 221 | |
| 222 | protected function createMigrationsTable(): int |
| 223 | { |
| 224 | $result = new MetadataTable($this->env, $this->io)->create($this->env->db); |
| 225 | |
| 226 | if ($result === 0) { |
| 227 | return 0; |
| 228 | } |
| 229 | |
| 230 | // Would require simulating failing metadata table creation. |
| 231 | // @codeCoverageIgnoreStart |
| 232 | $this->io->error('Migration table could not be created.'); |
| 233 | |
| 234 | return $result; |
| 235 | |
| 236 | // @codeCoverageIgnoreEnd |
| 237 | } |
| 238 | |
| 239 | protected function supportsTransactions(): bool |
| 240 | { |
| 241 | return $this->driverPolicy()->supportsTransactions(); |
| 242 | } |
| 243 | |
| 244 | private function driverPolicy(): DriverPolicy |
| 245 | { |
| 246 | return new DriverPolicy($this->env->driver); |
| 247 | } |
| 248 | |
| 249 | private function planner(): Planner |
| 250 | { |
| 251 | return new Planner($this->driverPolicy()); |
| 252 | } |
| 253 | |
| 254 | private function phpLoader(): PhpLoader |
| 255 | { |
| 256 | return new PhpLoader($this->env, $this->migrationFactory); |
| 257 | } |
| 258 | |
| 259 | private function log(): Log |
| 260 | { |
| 261 | return new Log($this->env, $this->planner()); |
| 262 | } |
| 263 | |
| 264 | private function plan(): Plan |
| 265 | { |
| 266 | return new Plan($this->env, $this->planner(), $this->log(), $this->io); |
| 267 | } |
| 268 | |
| 269 | private function executor(): Executor |
| 270 | { |
| 271 | return new Executor($this->env, $this->log(), $this->phpLoader(), $this->io); |
| 272 | } |
| 273 | |
| 274 | private function runner(): Runner |
| 275 | { |
| 276 | return new Runner( |
| 277 | $this->env, |
| 278 | $this->driverPolicy(), |
| 279 | $this->planner(), |
| 280 | $this->log(), |
| 281 | $this->executor(), |
| 282 | $this->io, |
| 283 | ); |
| 284 | } |
| 285 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 45 | array|Connection $conn, |
| 46 | array $options = [], |
| 47 | ?Contract\MigrationFactory $migrationFactory = null, |
| 48 | ) { |
| 49 | $this->env = new Environment($conn, $options); |
| 50 | $this->migrationFactory = $migrationFactory; |
| 51 | } |
| 146 | string $namespace, |
| 147 | array $migrations, |
| 148 | bool $tableExists, |
| 149 | bool $yes, |
| 150 | ): bool { |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 152 | $pendingMigrations = $this->planner()->pendingMigrations( |
| 153 | $namespace, |
| 154 | $migrations, |
| 155 | $appliedMigrations, |
| 156 | ); |
| 157 | |
| 158 | if (count($pendingMigrations) === 0) { |
| 159 | return true; |
| 146 | string $namespace, |
| 147 | array $migrations, |
| 148 | bool $tableExists, |
| 149 | bool $yes, |
| 150 | ): bool { |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 152 | $pendingMigrations = $this->planner()->pendingMigrations( |
| 153 | $namespace, |
| 154 | $migrations, |
| 155 | $appliedMigrations, |
| 156 | ); |
| 157 | |
| 158 | if (count($pendingMigrations) === 0) { |
| 162 | return new TestRunConfirmation()->confirm($this->io, $yes); |
| 163 | } |
| 146 | string $namespace, |
| 147 | array $migrations, |
| 148 | bool $tableExists, |
| 149 | bool $yes, |
| 150 | ): bool { |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 152 | $pendingMigrations = $this->planner()->pendingMigrations( |
| 153 | $namespace, |
| 154 | $migrations, |
| 155 | $appliedMigrations, |
| 156 | ); |
| 157 | |
| 158 | if (count($pendingMigrations) === 0) { |
| 159 | return true; |
| 146 | string $namespace, |
| 147 | array $migrations, |
| 148 | bool $tableExists, |
| 149 | bool $yes, |
| 150 | ): bool { |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 151 | $appliedMigrations = $tableExists ? $this->log()->applied($this->env->db) : []; |
| 152 | $pendingMigrations = $this->planner()->pendingMigrations( |
| 153 | $namespace, |
| 154 | $migrations, |
| 155 | $appliedMigrations, |
| 156 | ); |
| 157 | |
| 158 | if (count($pendingMigrations) === 0) { |
| 162 | return new TestRunConfirmation()->confirm($this->io, $yes); |
| 163 | } |
| 224 | $result = new MetadataTable($this->env, $this->io)->create($this->env->db); |
| 225 | |
| 226 | if ($result === 0) { |
| 227 | return 0; |
| 246 | return new DriverPolicy($this->env->driver); |
| 247 | } |
| 271 | return new Executor($this->env, $this->log(), $this->phpLoader(), $this->io); |
| 272 | } |
| 261 | return new Log($this->env, $this->planner()); |
| 262 | } |
| 124 | string $namespace, |
| 125 | array $migrations, |
| 126 | bool $showStacktrace, |
| 127 | bool $apply, |
| 128 | bool $tableExists, |
| 129 | ): int { |
| 130 | return $this->runner()->run( |
| 131 | $namespace, |
| 132 | $migrations, |
| 133 | new RunOptions( |
| 134 | $showStacktrace, |
| 135 | $apply, |
| 136 | $tableExists, |
| 137 | $this->createMigrationsTable(...), |
| 138 | ), |
| 139 | ); |
| 140 | } |
| 207 | protected function migrationIdsAreUnique(string $namespace, array $migrations): bool |
| 208 | { |
| 209 | $duplicates = $this->planner()->duplicateMigrationIds($namespace, $migrations); |
| 210 | |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 214 | foreach ($paths as $path) { |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 214 | foreach ($paths as $path) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return count($duplicates) === 0; |
| 220 | } |
| 207 | protected function migrationIdsAreUnique(string $namespace, array $migrations): bool |
| 208 | { |
| 209 | $duplicates = $this->planner()->duplicateMigrationIds($namespace, $migrations); |
| 210 | |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 214 | foreach ($paths as $path) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return count($duplicates) === 0; |
| 220 | } |
| 207 | protected function migrationIdsAreUnique(string $namespace, array $migrations): bool |
| 208 | { |
| 209 | $duplicates = $this->planner()->duplicateMigrationIds($namespace, $migrations); |
| 210 | |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return count($duplicates) === 0; |
| 220 | } |
| 207 | protected function migrationIdsAreUnique(string $namespace, array $migrations): bool |
| 208 | { |
| 209 | $duplicates = $this->planner()->duplicateMigrationIds($namespace, $migrations); |
| 210 | |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return count($duplicates) === 0; |
| 220 | } |
| 207 | protected function migrationIdsAreUnique(string $namespace, array $migrations): bool |
| 208 | { |
| 209 | $duplicates = $this->planner()->duplicateMigrationIds($namespace, $migrations); |
| 210 | |
| 211 | foreach ($duplicates as $id => $paths) { |
| 211 | foreach ($duplicates as $id => $paths) { |
| 212 | $this->io->error("Duplicate migration id '{$id}' in namespace '{$namespace}'"); |
| 213 | |
| 214 | foreach ($paths as $path) { |
| 215 | $this->io->echolnErr(" - {$path}"); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | return count($duplicates) === 0; |
| 220 | } |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 173 | $this->io->error('No migration directories defined in configuration'); |
| 174 | |
| 175 | return false; |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 178 | if ($namespace) { |
| 179 | if (!array_key_exists($namespace, $migrationNamespaces)) { |
| 180 | $this->io->error("Migration namespace '{$namespace}' does not exist"); |
| 181 | |
| 182 | return false; |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 178 | if ($namespace) { |
| 179 | if (!array_key_exists($namespace, $migrationNamespaces)) { |
| 185 | $migrations = $migrationNamespaces[$namespace]; |
| 186 | |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 178 | if ($namespace) { |
| 179 | if (!array_key_exists($namespace, $migrationNamespaces)) { |
| 185 | $migrations = $migrationNamespaces[$namespace]; |
| 186 | |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 187 | return $this->migrationIdsAreUnique($namespace, $migrations) ? $migrations : false; |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 178 | if ($namespace) { |
| 190 | if (!array_key_exists('default', $migrationNamespaces)) { |
| 191 | $this->io->error("Migration namespace 'default' does not exist"); |
| 192 | $this->io->info( |
| 193 | 'If you have defined namespaced migrations, you must either provide a namespace using the ' |
| 194 | . "`--namespace` flag when running this command, or define a namespace named 'default' which " |
| 195 | . 'will be used when no namespace is provided.', |
| 196 | ); |
| 197 | |
| 198 | return false; |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 178 | if ($namespace) { |
| 190 | if (!array_key_exists('default', $migrationNamespaces)) { |
| 201 | $migrations = $migrationNamespaces['default']; |
| 202 | |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 204 | } |
| 168 | protected function migrationsForNamespace(string $namespace): array|false |
| 169 | { |
| 170 | $migrationNamespaces = $this->env->getMigrations(); |
| 171 | |
| 172 | if ($migrationNamespaces === false) { |
| 178 | if ($namespace) { |
| 190 | if (!array_key_exists('default', $migrationNamespaces)) { |
| 201 | $migrations = $migrationNamespaces['default']; |
| 202 | |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 203 | return $this->migrationIdsAreUnique('default', $migrations) ? $migrations : false; |
| 204 | } |
| 256 | return new PhpLoader($this->env, $this->migrationFactory); |
| 257 | } |
| 266 | return new Plan($this->env, $this->planner(), $this->log(), $this->io); |
| 267 | } |
| 251 | return new Planner($this->driverPolicy()); |
| 252 | } |
| 276 | return new Runner( |
| 277 | $this->env, |
| 278 | $this->driverPolicy(), |
| 279 | $this->planner(), |
| 280 | $this->log(), |
| 281 | $this->executor(), |
| 282 | $this->io, |
| 283 | ); |
| 284 | } |
| 241 | return $this->driverPolicy()->supportsTransactions(); |
| 242 | } |
| 224 | $result = new MetadataTable($this->env, $this->io)->create($this->env->db); |
| 225 | |
| 226 | if ($result === 0) { |
| 227 | return 0; |