option('source') ?? $this->choice('Select a source:', self::SOURCES, 0); $type = $this->option('type') ?? $this->choice('Select an import type:', self::TYPES, 0); if ($type === 'api') { $credentials = [ 'api_key' => $this->secret('API key:'), 'api_secret' => $this->secret('API secret:'), ]; $filePath = null; } else { $importsPath = storage_path('app/private/imports'); $filePath = $this->option('file') ?? $this->resolveImportPath($importsPath, $this->ask("Path to the export file, relative to {$importsPath}:")); while (blank($filePath) || ! is_file($filePath)) { if (filled($filePath)) { $this->error("File not found: {$filePath}"); } $filePath = $this->resolveImportPath( $importsPath, $this->ask("Path to the export file, relative to {$importsPath}:"), ); } $credentials = null; } $spec = new ImportSpec( source: $source, type: $type, filePath: $filePath, credentials: $credentials, ); RunMigrateImportJob::dispatch($spec); $this->info('Import queued.'); } // Answers are relative to storage/app/private/imports (e.g. "shopify" or // "shopify/products_export.csv"); absolute paths are used as-is. A // directory answer picks the first CSV file found inside it. private function resolveImportPath(string $importsPath, ?string $answer): ?string { if (blank($answer)) { return $answer; } $path = str_starts_with($answer, '/') ? $answer : "{$importsPath}/{$answer}"; if (is_dir($path)) { $csv = collect(glob("{$path}/*.csv"))->first(); return $csv ?? $path; } return $path; } }