Skip to content

Commit d16aa36

Browse files
committed
feat: add command to check published/modified/redundant view files in user project space.
feat: update publish views command to work better with more visuals.
1 parent 835e7d5 commit d16aa36

File tree

3 files changed

+284
-36
lines changed

3 files changed

+284
-36
lines changed

src/Commands/DiffViewsCommand.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
namespace Javaabu\Forms\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Filesystem\Filesystem;
7+
use Symfony\Component\Finder\Finder;
8+
9+
class DiffViewsCommand extends Command
10+
{
11+
protected $signature = 'forms:diff-views
12+
{--only-redundant : Show only published views that are identical to the package views}';
13+
14+
protected $description = 'Find redundant or modified published form views in your application.';
15+
16+
public function handle(): int
17+
{
18+
$filesystem = new Filesystem();
19+
20+
$packagePath = __DIR__ . '/../../resources/views';
21+
$publishedPath = resource_path('views/vendor/forms');
22+
23+
if (! $filesystem->isDirectory($packagePath)) {
24+
$this->error('Package view directory not found.');
25+
return 1;
26+
}
27+
28+
if (! $filesystem->isDirectory($publishedPath)) {
29+
$this->info('No published views found in your application.');
30+
return 0;
31+
}
32+
33+
$packageFiles = $this->getViewFiles($packagePath, $filesystem);
34+
$publishedFiles = $this->getViewFiles($publishedPath, $filesystem);
35+
36+
$onlyRedundant = (bool) $this->option('only-redundant');
37+
38+
$rows = [];
39+
$counts = [
40+
'redundant' => 0,
41+
'modified' => 0,
42+
];
43+
44+
// ONLY check files in your app, ignore anything not published.
45+
foreach ($publishedFiles as $relativePath => $publishedRealPath) {
46+
47+
if (! isset($packageFiles[$relativePath])) {
48+
// orphan — ignore as per your requirements
49+
continue;
50+
}
51+
52+
$packageContent = $filesystem->get($packageFiles[$relativePath]);
53+
$publishedContent = $filesystem->get($publishedRealPath);
54+
55+
if ($packageContent === $publishedContent) {
56+
if (! $onlyRedundant) {
57+
$rows[] = ['<fg=gray>REDUNDANT</>', $relativePath];
58+
} else {
59+
$rows[] = ['<fg=gray>REDUNDANT</>', $relativePath];
60+
}
61+
$counts['redundant']++;
62+
} else {
63+
if (! $onlyRedundant) {
64+
$rows[] = ['<fg=yellow>MODIFIED</>', $relativePath];
65+
}
66+
$counts['modified']++;
67+
}
68+
}
69+
70+
if (empty($rows)) {
71+
$this->info($onlyRedundant
72+
? 'No redundant published views found.'
73+
: 'No published views match your filters.'
74+
);
75+
76+
$this->printSummary($counts);
77+
return 0;
78+
}
79+
80+
$this->table(
81+
['Status', 'View'],
82+
$rows
83+
);
84+
85+
$this->printSummary($counts);
86+
87+
return 0;
88+
}
89+
90+
protected function getViewFiles(string $basePath, Filesystem $filesystem): array
91+
{
92+
if (! $filesystem->isDirectory($basePath)) {
93+
return [];
94+
}
95+
96+
$files = [];
97+
$finder = new Finder();
98+
99+
$finder->files()->in($basePath)->name('*.blade.php');
100+
101+
foreach ($finder as $file) {
102+
$relativePath = str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePathname());
103+
$files[$relativePath] = $file->getRealPath();
104+
}
105+
106+
ksort($files);
107+
108+
return $files;
109+
}
110+
111+
protected function printSummary(array $counts): void
112+
{
113+
$this->newLine();
114+
$this->info('Summary:');
115+
$this->line(' <fg=gray>Redundant (can be deleted):</> '.$counts['redundant']);
116+
$this->line(' <fg=yellow>Modified (customized override):</> '.$counts['modified']);
117+
}
118+
}

src/Commands/PublishViewCommand.php

Lines changed: 165 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,66 +5,195 @@
55
use Illuminate\Console\Command;
66
use Illuminate\Filesystem\Filesystem;
77
use Symfony\Component\Finder\Finder;
8-
use function Laravel\Prompts\multisearch;
8+
use function Laravel\Prompts\select;
99
use function Laravel\Prompts\confirm;
10+
use function Laravel\Prompts\info;
1011

1112
class PublishViewCommand extends Command
1213
{
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.';
1519

1620
public function handle()
1721
{
1822
$sourcePath = __DIR__ . '/../../resources/views';
19-
$targetBase = base_path('resources/views/vendor/forms');
23+
$targetBase = resource_path('views/vendor/forms');
2024
$filesystem = new Filesystem();
21-
$finder = new Finder();
22-
$finder->files()->in($sourcePath);
2325

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;
3229
}
3330

31+
$files = $this->getViewFiles($sourcePath, $filesystem);
32+
3433
if (empty($files)) {
3534
$this->error('No view files found to publish.');
3635
return 1;
3736
}
3837

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,
46121
);
47122

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?",
54161
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++;
60169
continue;
61170
}
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());
62182
}
63-
$filesystem->copy($files[$relativePath], $target);
64-
$this->info("Published: {$relativePath}");
65183
}
66184

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+
}
69198
}
70199
}

src/FormsServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public function register(): void
6060
if ($this->app->runningInConsole()) {
6161
$this->commands([
6262
\Javaabu\Forms\Commands\PublishViewCommand::class,
63+
\Javaabu\Forms\Commands\DiffViewsCommand::class,
6364
]);
6465
}
6566
}

0 commit comments

Comments
 (0)