Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ Execute business logic with a single, expressive call. No more hunting through s
### 📬 **Dispatchable Actions**
Seamlessly queue your actions for background processing. It's as easy as changing `run()` to `dispatch()`!

### 💡 **Smart Code Completion**
Full IntelliSense support with auto-completion for runnable and dispatchable actions across all major IDEs.

### 🔄 **Smart Array Conversion**
Convert between arrays and objects effortlessly with our powerful attribute system. Perfect for APIs!

Expand Down Expand Up @@ -247,6 +250,9 @@ php artisan make:action CalculateShipping --invokable

# DTO with array conversion
php artisan make:dto OrderData

# Enable Smart Code Completion
php artisan ide-helper:actions
```

## 🌟 Real-World Examples
Expand Down
2 changes: 2 additions & 0 deletions src/ActionableProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Support\ServiceProvider;
use LumoSolutions\Actionable\Console\BaseStubCommand;
use LumoSolutions\Actionable\Console\Commands\ActionsIdeHelperCommand;
use LumoSolutions\Actionable\Console\Commands\MakeActionCommand;
use LumoSolutions\Actionable\Console\Commands\MakeDtoCommand;

Expand All @@ -17,6 +18,7 @@ public function boot(): void
$this->commands([
MakeActionCommand::class,
MakeDtoCommand::class,
ActionsIdeHelperCommand::class,
]);

$this->publishes(
Expand Down
127 changes: 127 additions & 0 deletions src/Console/Commands/ActionsIdeHelperCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

namespace LumoSolutions\Actionable\Console\Commands;

use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use LumoSolutions\Actionable\Services\ActionDocBlockService;

class ActionsIdeHelperCommand extends Command
{
protected $signature = 'ide-helper:actions
{--namespace=App\\ : The namespace to scan for actions}
{--dry-run : Show what would be changed without modifying files}';

protected $description = 'Generate IDE helper doc blocks for Action classes using IsRunnable and IsDispatchable traits';

protected ActionDocBlockService $service;

public function __construct(ActionDocBlockService $service)
{
parent::__construct();
$this->service = $service;
}

public function handle(): int
{
$namespace = $this->option('namespace');
$dryRun = $this->option('dry-run');

$this->info("Scanning for Action classes in namespace: {$namespace} ");

$files = $this->getPhpFiles($namespace);

if (empty($files)) {
$this->error("No PHP files found in namespace: {$namespace}");

return self::FAILURE;
}

$processedCount = 0;
$skippedCount = 0;
$errorCount = 0;

foreach ($files as $file) {
$relativePath = str_replace(base_path().DIRECTORY_SEPARATOR, '', $file->getRealPath());

try {
$result = $this->service->processFile($file->getPathname(), $dryRun);

if ($result['processed']) {
$processedCount++;

if ($dryRun) {
$this->line("<info>Would update:</info> {$relativePath}");
$this->showDocBlockChanges($result['docBlocks']);
} else {
$this->info("Updated: {$relativePath}");
}
} else {
$skippedCount++;
if ($this->output->isVerbose()) {
$this->line("<comment>Skipped:</comment> {$relativePath} - {$result['reason']}");
}
}
} catch (Exception $e) {
$errorCount++;
$this->error("Error processing {$relativePath}: {$e->getMessage()}");

if ($this->output->isVeryVerbose()) {
$this->line($e->getTraceAsString());
}
}
}

$this->showSummary($processedCount, $skippedCount, $errorCount, $dryRun);

return ($errorCount > 0) ? self::FAILURE : self::SUCCESS;
}

private function getPhpFiles(string $namespace): array
{
$path = app_path(str_replace(['App\\', '\\'], ['', '/'], $namespace));

if (! is_dir($path)) {
return [];
}

return collect(File::allFiles($path))
->filter(fn ($file) => $file->getExtension() === 'php')
->values()
->all();
}

private function showDocBlockChanges(array $docBlocks): void
{
if (empty($docBlocks)) {
return;
}

$this->line(' <comment>Doc blocks to add:</comment>');
foreach ($docBlocks as $docBlock) {
$this->line(" <info>*</info> {$docBlock}");
}
}

private function showSummary(int $processedCount, int $skippedCount, int $errorCount, bool $dryRun): void
{
$this->line('');
$this->info('Summary:');

$action = $dryRun ? 'Would be updated' : 'Updated';
$this->line(" <info>{$action}:</info> {$processedCount} files");

if ($skippedCount > 0 || $this->output->isVerbose()) {
$this->line(" <comment>Skipped:</comment> {$skippedCount} files");
}

if ($errorCount > 0) {
$this->line(" <error>Errors:</error> {$errorCount} files");
}

$this->line('');
$status = $errorCount > 0 ? 'completed with errors' : 'completed successfully';
$this->info("Process {$status}!");
}
}
Loading