diff --git a/README.md b/README.md index f658486..2207996 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Kickstart your project and save time with Larament! This time-saving starter kit - All component labels are automatically translatable. - A `composer review` command that runs PINT, PHPStan, and PEST. - Helper file is included for custom helper functions. +- A custom `php artisan make:filament-action` command is available for creating actions. ### Design ![user-global-search](https://raw.githubusercontent.com/CodeWithDennis/larament/main/resources/images/user-global-search.jpg) diff --git a/app/Console/Commands/MakeFilamentActionCommand.php b/app/Console/Commands/MakeFilamentActionCommand.php new file mode 100644 index 0000000..8afe75f --- /dev/null +++ b/app/Console/Commands/MakeFilamentActionCommand.php @@ -0,0 +1,163 @@ + + */ + protected array $actionTypes = [ + 'form' => 'Filament\\Forms\\Components\\Actions\\Action', + 'table' => 'Filament\\Tables\\Actions\\Action', + 'table-bulk' => 'Filament\\Tables\\Actions\\BulkAction', + 'custom-component' => 'Filament\\Actions\\Action', + 'infolist' => 'Filament\\Infolists\\Components\\Actions\\Action', + 'notification' => 'Filament\\Notifications\\Actions\\Action', + 'global-search' => 'Filament\\GlobalSearch\\Actions\\Action', + ]; + + public function handle(): void + { + $name = $this->getNameArgument(); + $className = $this->getClassName($name); + $type = $this->getActionType(); + $actionClass = $this->actionTypes[$type]; + $baseClass = $type === 'table-bulk' ? 'BulkAction' : 'Action'; + $path = $this->getFilePath($className, $type); + + if ($this->fileExists($path)) { + warning("$className already exists."); + + return; + } + + $stubContent = $this->getStubContent(); + $this->generateActionFile($path, $className, $actionClass, $stubContent, $baseClass); + + info("$className created successfully at:"); + info($path); + } + + private function getNameArgument(): string + { + return $this->argument('name') ?? text( + label: 'Enter the name of the Filament Action', + required: 'Action name is required.', + ); + } + + private function getClassName(string $name): string + { + return Str::endsWith(Str::lower($name), 'action') + ? Str::studly($name) + : Str::studly($name).'Action'; + } + + private function getActionType(): string + { + $selectedTypes = array_filter([ + 'form' => $this->option('form'), + 'table' => $this->option('table'), + 'table-bulk' => $this->option('table-bulk'), + 'custom-component' => $this->option('custom-component'), + 'infolist' => $this->option('infolist'), + 'notification' => $this->option('notification'), + 'global-search' => $this->option('global-search'), + ]); + + if (count($selectedTypes) > 1) { + warning('Please specify only one action type.'); + + exit; + } + + if (count($selectedTypes) === 0) { + $selectedType = select( + label: 'Please select the type of the Filament Action', + options: [ + 'form' => 'Form', + 'table' => 'Table', + 'table-bulk' => 'TableBulk', + 'infolist' => 'Infolist', + 'notification' => 'Notification', + 'global-search' => 'GlobalSearch', + 'custom-component' => 'Custom', + ] + ); + + return (string) $selectedType; + } + + return (string) key($selectedTypes); + } + + private function getFilePath(string $className, string $type): string + { + $subDirectory = match ($type) { + 'form' => 'Forms', + 'table', 'table-bulk' => 'Tables', + 'infolist' => 'Infolists', + 'notification' => 'Notifications', + 'global-search' => 'GlobalSearch', + 'custom-component' => 'Custom', + default => '', + }; + + return app_path("Filament/Actions/$subDirectory/$className.php"); + } + + private function fileExists(string $path): bool + { + return File::exists($path); + } + + private function getStubContent(): string + { + $stubPath = app_path('Filament/Actions/Stubs/FilamentAction.stub'); + + return File::get($stubPath); + } + + private function generateActionFile(string $path, string $className, string $actionClass, string $stubContent, string $baseClass): void + { + $namespace = $this->getNamespace($path); + $defaultName = Str::camel(Str::replaceLast('Action', '', $className)); + + $content = str_replace( + ['{{ namespace }}', '{{ className }}', '{{ defaultName }}', '{{ actionClass }}', '{{ baseClass }}'], + [$namespace, $className, $defaultName, $actionClass, $baseClass], + $stubContent + ); + + File::ensureDirectoryExists(dirname($path)); + File::put($path, $content); + } + + private function getNamespace(string $path): string + { + $relativePath = Str::after($path, app_path().'/'); + + return 'App\\'.Str::replace('/', '\\', dirname($relativePath)); + } +} diff --git a/app/Filament/Actions/GlobalSearch/TestAction.php b/app/Filament/Actions/GlobalSearch/TestAction.php new file mode 100644 index 0000000..93c0019 --- /dev/null +++ b/app/Filament/Actions/GlobalSearch/TestAction.php @@ -0,0 +1,20 @@ +