repository = $repository; $this->runner = $runner; $this->store = $store; } public function run(): void { $this->store->ensureMigrationsTable(); $migrations = $this->getStatus(); // do not try to run migrations if any are running or failed foreach ($migrations as $migration) { if ($migration['status'] === self::MIGRATION_STATUS_STARTED) { throw MigratorException::runningMigrationsExist(); } if ($migration['status'] === self::MIGRATION_STATUS_FAILED) { throw MigratorException::failedMigrationsExist(); } } foreach ($migrations as $migration) { if ($migration['status'] === self::MIGRATION_STATUS_NEW) { $this->runner->runMigration($migration['name']); } } } /** @return array{name: string, status: string, started_at: string|null, completed_at: string|null, error: string|null}[] */ public function getStatus(): array { $defined = $this->repository->loadAll(); $processed = $this->store->getAll(); $processedMap = array_combine(array_column($processed, 'name'), $processed) ?: []; $all = array_unique(array_merge($defined, array_keys($processedMap))); sort($all); $status = []; foreach ($all as $name) { $data = $processedMap[$name] ?? []; $status[] = [ 'name' => $name, 'status' => $data ? $this->getMigrationStatus($data) : self::MIGRATION_STATUS_NEW, 'started_at' => $data['started_at'] ?? null, 'completed_at' => $data['completed_at'] ?? null, 'error' => $data && $data['error'] ? mb_strimwidth($data['error'], 0, 20, '…') : null, ]; } return $status; } private function getMigrationStatus(array $data): string { if (!isset($data['completed_at'])) { return self::MIGRATION_STATUS_STARTED; } return isset($data['error']) ? self::MIGRATION_STATUS_FAILED : self::MIGRATION_STATUS_COMPLETED; } }