|
5 | 5 | use Illuminate\Console\Command; |
6 | 6 | use Illuminate\Filesystem\Filesystem; |
7 | 7 | use Symfony\Component\Finder\Finder; |
8 | | -use function Laravel\Prompts\multisearch; |
| 8 | +use function Laravel\Prompts\select; |
9 | 9 | use function Laravel\Prompts\confirm; |
| 10 | +use function Laravel\Prompts\info; |
10 | 11 |
|
11 | 12 | class PublishViewCommand extends Command |
12 | 13 | { |
13 | | - protected $signature = 'forms:publish-view'; |
14 | | - protected $description = 'Interactively publish one or more form view files to your application for customization.'; |
| 14 | + protected $signature = 'forms:publish-view |
| 15 | + {--all : Publish all view files} |
| 16 | + {--force : Overwrite existing files without prompting}'; |
| 17 | + |
| 18 | + protected $description = 'Publish one or more form view files to your application for customization.'; |
15 | 19 |
|
16 | 20 | public function handle() |
17 | 21 | { |
18 | 22 | $sourcePath = __DIR__ . '/../../resources/views'; |
19 | | - $targetBase = base_path('resources/views/vendor/forms'); |
| 23 | + $targetBase = resource_path('views/vendor/forms'); |
20 | 24 | $filesystem = new Filesystem(); |
21 | | - $finder = new Finder(); |
22 | | - $finder->files()->in($sourcePath); |
23 | 25 |
|
24 | | - $files = []; |
25 | | - $options = []; |
26 | | - foreach ($finder as $file) { |
27 | | - $relativePath = $file->getRelativePathname(); |
28 | | - $files[$relativePath] = $file->getRealPath(); |
29 | | - $targetPath = $targetBase . '/' . $relativePath; |
30 | | - $label = $relativePath . ($filesystem->exists($targetPath) ? ' (published)' : ''); |
31 | | - $options[$relativePath] = $label; |
| 26 | + if (! $filesystem->isDirectory($sourcePath)) { |
| 27 | + $this->error('Source view directory not found.'); |
| 28 | + return 1; |
32 | 29 | } |
33 | 30 |
|
| 31 | + $files = $this->getViewFiles($sourcePath, $filesystem); |
| 32 | + |
34 | 33 | if (empty($files)) { |
35 | 34 | $this->error('No view files found to publish.'); |
36 | 35 | return 1; |
37 | 36 | } |
38 | 37 |
|
39 | | - $selected = multisearch( |
40 | | - label: 'Search and select view files to publish:', |
41 | | - options: fn ($search) => array_filter( |
42 | | - $options, |
43 | | - fn ($label) => stripos($label, $search) !== false |
44 | | - ), |
45 | | - required: 'You must select at least one file to publish.' |
| 38 | + if ($this->option('all')) { |
| 39 | + // publish all files |
| 40 | + $selected = array_keys($files); |
| 41 | + } else { |
| 42 | + // single selection via number |
| 43 | + $selectedIndex = $this->promptForFileSelection($files, $targetBase, $filesystem); |
| 44 | + |
| 45 | + if ($selectedIndex === null) { |
| 46 | + $this->info('No file selected for publishing.'); |
| 47 | + return 0; |
| 48 | + } |
| 49 | + |
| 50 | + $selected = [$selectedIndex]; |
| 51 | + } |
| 52 | + |
| 53 | + $this->publishFiles($selected, $files, $targetBase, $filesystem); |
| 54 | + |
| 55 | + return 0; |
| 56 | + } |
| 57 | + |
| 58 | + protected function getViewFiles(string $sourcePath, Filesystem $filesystem): array |
| 59 | + { |
| 60 | + $files = []; |
| 61 | + $finder = new Finder(); |
| 62 | + |
| 63 | + try { |
| 64 | + $finder->files()->in($sourcePath)->name('*.blade.php'); |
| 65 | + |
| 66 | + $tempFiles = []; |
| 67 | + foreach ($finder as $file) { |
| 68 | + $relativePath = str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePathname()); |
| 69 | + $tempFiles[$relativePath] = $file->getRealPath(); |
| 70 | + } |
| 71 | + |
| 72 | + ksort($tempFiles); |
| 73 | + |
| 74 | + $count = 0; |
| 75 | + foreach ($tempFiles as $relativePath => $realPath) { |
| 76 | + $files[$count] = [ |
| 77 | + 'relative_path' => $relativePath, |
| 78 | + 'real_path' => $realPath, |
| 79 | + ]; |
| 80 | + $count++; |
| 81 | + } |
| 82 | + } catch (\Exception $e) { |
| 83 | + $this->error('Error scanning view files: ' . $e->getMessage()); |
| 84 | + return []; |
| 85 | + } |
| 86 | + |
| 87 | + return $files; |
| 88 | + } |
| 89 | + |
| 90 | + /** |
| 91 | + * Show a simple numbered list and let the user choose a single file. |
| 92 | + */ |
| 93 | + protected function promptForFileSelection(array $files, string $targetBase, Filesystem $filesystem): ?int |
| 94 | + { |
| 95 | + $this->newLine(); |
| 96 | + info('Available view files:'); |
| 97 | + |
| 98 | + $options = []; |
| 99 | + |
| 100 | + foreach ($files as $index => $file) { |
| 101 | + $relative = $file['relative_path']; |
| 102 | + |
| 103 | + $targetPath = $targetBase . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relative); |
| 104 | + $exists = $filesystem->exists($targetPath); |
| 105 | + |
| 106 | + $status = $exists |
| 107 | + ? '<fg=yellow>Published</>' |
| 108 | + : '<fg=green>New</>'; |
| 109 | + |
| 110 | + // Key is the relative path (unique) |
| 111 | + // Label is the filename + status (no numbers) |
| 112 | + $options[$index] = $status . ': ' . $relative; |
| 113 | + } |
| 114 | + |
| 115 | + $this->newLine(); |
| 116 | + |
| 117 | + // returns the KEY we defined (relative path) |
| 118 | + $selectedRelativePath = select( |
| 119 | + label: 'Which view would you like to publish?', |
| 120 | + options: $options, |
46 | 121 | ); |
47 | 122 |
|
48 | | - foreach ($selected as $relativePath) { |
49 | | - $target = $targetBase . '/' . $relativePath; |
50 | | - $filesystem->ensureDirectoryExists(dirname($target)); |
51 | | - if ($filesystem->exists($target)) { |
52 | | - if (!confirm( |
53 | | - label: "{$relativePath} already exists. Overwrite?", |
| 123 | + $selected_file = $this->parseChoice($selectedRelativePath); |
| 124 | + |
| 125 | + // Find the index in the $files array |
| 126 | + $selectedIndex = array_search($selected_file, array_column($files, 'relative_path'), true); |
| 127 | + |
| 128 | + return $selectedIndex !== false ? $selectedIndex : null; |
| 129 | + } |
| 130 | + |
| 131 | + public function parseChoice(string $choice) |
| 132 | + { |
| 133 | + [$type, $value] = explode(': ', strip_tags($choice)); |
| 134 | + |
| 135 | + return $value; |
| 136 | + } |
| 137 | + |
| 138 | + protected function publishFiles(array $selected, array $files, string $targetBase, Filesystem $filesystem): void |
| 139 | + { |
| 140 | + $published = 0; |
| 141 | + $skipped = 0; |
| 142 | + $overwritten = 0; |
| 143 | + |
| 144 | + foreach ($selected as $fileIndex) { |
| 145 | + if (! isset($files[$fileIndex])) { |
| 146 | + continue; |
| 147 | + } |
| 148 | + |
| 149 | + $file = $files[$fileIndex]; |
| 150 | + $relativePath = $file['relative_path']; |
| 151 | + $sourcePath = $file['real_path']; |
| 152 | + $targetPath = $targetBase . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativePath); |
| 153 | + |
| 154 | + $filesystem->ensureDirectoryExists(dirname($targetPath)); |
| 155 | + |
| 156 | + $exists = $filesystem->exists($targetPath); |
| 157 | + |
| 158 | + if ($exists && ! $this->option('force')) { |
| 159 | + $shouldOverwrite = confirm( |
| 160 | + label: "File {$relativePath} already exists. Overwrite?", |
54 | 161 | default: false, |
55 | | - yes: 'Overwrite', |
56 | | - no: 'Skip', |
57 | | - hint: 'If you skip, the existing file will be kept.' |
58 | | - )) { |
59 | | - $this->line("Skipped: {$relativePath}"); |
| 162 | + yes: 'Yes, overwrite', |
| 163 | + no: 'No, skip' |
| 164 | + ); |
| 165 | + |
| 166 | + if (! $shouldOverwrite) { |
| 167 | + $this->line(" <fg=yellow>SKIPPED</> {$relativePath}"); |
| 168 | + $skipped++; |
60 | 169 | continue; |
61 | 170 | } |
| 171 | + |
| 172 | + $overwritten++; |
| 173 | + } |
| 174 | + |
| 175 | + try { |
| 176 | + $filesystem->copy($sourcePath, $targetPath); |
| 177 | + $action = $exists ? 'UPDATED' : 'PUBLISHED'; |
| 178 | + $this->line(" <fg=green>{$action}</> {$relativePath}"); |
| 179 | + $published++; |
| 180 | + } catch (\Exception $e) { |
| 181 | + $this->line(" <fg=red>FAILED</> {$relativePath}: " . $e->getMessage()); |
62 | 182 | } |
63 | | - $filesystem->copy($files[$relativePath], $target); |
64 | | - $this->info("Published: {$relativePath}"); |
65 | 183 | } |
66 | 184 |
|
67 | | - $this->info('Selected view files published successfully.'); |
68 | | - return 0; |
| 185 | + $this->newLine(); |
| 186 | + |
| 187 | + if ($published > 0) { |
| 188 | + $this->info("Successfully published {$published} file(s)."); |
| 189 | + } |
| 190 | + |
| 191 | + if ($overwritten > 0) { |
| 192 | + $this->info("Overwritten {$overwritten} existing file(s)."); |
| 193 | + } |
| 194 | + |
| 195 | + if ($skipped > 0) { |
| 196 | + $this->info("Skipped {$skipped} file(s)."); |
| 197 | + } |
69 | 198 | } |
70 | 199 | } |
0 commit comments