diff --git a/packages/support/src/Commands/Concerns/CanAskForComponentLocation.php b/packages/support/src/Commands/Concerns/CanAskForComponentLocation.php index 35445e10e0..0efa175ba1 100644 --- a/packages/support/src/Commands/Concerns/CanAskForComponentLocation.php +++ b/packages/support/src/Commands/Concerns/CanAskForComponentLocation.php @@ -23,14 +23,14 @@ trait CanAskForComponentLocation if (blank($locations)) { return [ - "App\\{$pathNamespace}", + "App\\Filament\\{$pathNamespace}", app_path($path), '', ]; } $options = [ - null => "App\\{$pathNamespace}", + null => "App\\Filament\\{$pathNamespace}", ...array_map( fn (string $namespace): string => "{$namespace}\\{$pathNamespace}", array_combine( @@ -47,7 +47,7 @@ trait CanAskForComponentLocation if (blank($namespace)) { return [ - "App\\{$pathNamespace}", + "App\\Filament\\{$pathNamespace}", app_path($path), '', ]; diff --git a/packages/tables/src/Commands/FileGenerators/LivewireTableClassGenerator.php b/packages/tables/src/Commands/FileGenerators/LivewireTableComponentClassGenerator.php similarity index 98% rename from packages/tables/src/Commands/FileGenerators/LivewireTableClassGenerator.php rename to packages/tables/src/Commands/FileGenerators/LivewireTableComponentClassGenerator.php index 0932ee89f9..af852234c5 100644 --- a/packages/tables/src/Commands/FileGenerators/LivewireTableClassGenerator.php +++ b/packages/tables/src/Commands/FileGenerators/LivewireTableComponentClassGenerator.php @@ -21,7 +21,7 @@ use Nette\PhpGenerator\Literal; use Nette\PhpGenerator\Method; use Nette\PhpGenerator\TraitUse; -class LivewireTableClassGenerator extends ClassGenerator +class LivewireTableComponentClassGenerator extends ClassGenerator { use CanGenerateTables; use CanReadModelSchemas; diff --git a/packages/tables/src/Commands/FileGenerators/TableClassGenerator.php b/packages/tables/src/Commands/FileGenerators/TableClassGenerator.php new file mode 100644 index 0000000000..c9a9732ec6 --- /dev/null +++ b/packages/tables/src/Commands/FileGenerators/TableClassGenerator.php @@ -0,0 +1,88 @@ + $modelFqn + */ + final public function __construct( + protected string $fqn, + protected string $modelFqn, + protected bool $isGenerated, + ) {} + + public function getNamespace(): string + { + return $this->extractNamespace($this->getFqn()); + } + + /** + * @return array + */ + public function getImports(): array + { + return [ + Table::class, + ...($this->hasPartialImports() ? ['Filament\Tables'] : []), + Builder::class, + $this->getModelFqn(), + ]; + } + + public function getBasename(): string + { + return class_basename($this->getFqn()); + } + + protected function addMethodsToClass(ClassType $class): void + { + $this->addConfigureMethodToClass($class); + } + + protected function addConfigureMethodToClass(ClassType $class): void + { + $method = $class->addMethod('configure') + ->setPublic() + ->setStatic() + ->setReturnType(Table::class) + ->setBody($this->generateTableMethodBody($this->getModelFqn())); + $method->addParameter('table') + ->setType(Table::class); + + $this->configureConfigureMethod($method); + } + + protected function configureConfigureMethod(Method $method): void {} + + public function getFqn(): string + { + return $this->fqn; + } + + /** + * @return ?class-string + */ + public function getModelFqn(): ?string + { + return $this->modelFqn; + } + + public function isGenerated(): bool + { + return $this->isGenerated; + } +} diff --git a/packages/tables/src/Commands/MakeColumnCommand.php b/packages/tables/src/Commands/MakeColumnCommand.php index 89e2bb1ef9..99fef8d43a 100644 --- a/packages/tables/src/Commands/MakeColumnCommand.php +++ b/packages/tables/src/Commands/MakeColumnCommand.php @@ -136,7 +136,7 @@ class MakeColumnCommand extends Command $path, $viewNamespace, ] = $this->askForComponentLocation( - path: 'Filament/Tables/Columns', + path: 'Tables/Columns', question: 'Where would you like to create the column?', ); @@ -154,8 +154,8 @@ class MakeColumnCommand extends Command $this->viewPath, ] = $this->askForViewLocation( str($this->fqn) - ->afterLast('\\Filament\\Tables\\Columns\\') - ->prepend('Filament\\Tables\\Columns\\') + ->afterLast('\\Tables\\Columns\\') + ->prepend('Tables\\Columns\\') ->replace('\\', '/') ->explode('/') ->map(Str::kebab(...)) diff --git a/packages/tables/src/Commands/MakeLivewireTableCommand.php b/packages/tables/src/Commands/MakeLivewireTableCommand.php index da855b6d9a..878f1d428a 100644 --- a/packages/tables/src/Commands/MakeLivewireTableCommand.php +++ b/packages/tables/src/Commands/MakeLivewireTableCommand.php @@ -6,7 +6,7 @@ use Filament\Support\Commands\Concerns\CanAskForLivewireComponentLocation; use Filament\Support\Commands\Concerns\CanAskForViewLocation; use Filament\Support\Commands\Concerns\CanManipulateFiles; use Filament\Support\Commands\Exceptions\InvalidCommandOutput; -use Filament\Tables\Commands\FileGenerators\LivewireTableClassGenerator; +use Filament\Tables\Commands\FileGenerators\LivewireTableComponentClassGenerator; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; @@ -19,6 +19,7 @@ use function Laravel\Prompts\suggest; use function Laravel\Prompts\text; #[AsCommand(name: 'make:filament-livewire-table', aliases: [ + 'filament:livewire-table', 'make:livewire-table', ])] class MakeLivewireTableCommand extends Command @@ -35,6 +36,7 @@ class MakeLivewireTableCommand extends Command * @var array */ protected $aliases = [ + 'filament:livewire-table', 'make:livewire-table', ]; @@ -172,10 +174,6 @@ class MakeLivewireTableCommand extends Command ->studly() ->replace('/', '\\'); - if (blank($this->modelFqnEnd)) { - $this->modelFqnEnd = 'Resource'; - } - $this->modelFqn = "{$modelNamespace}\\{$this->modelFqnEnd}"; } @@ -223,7 +221,7 @@ class MakeLivewireTableCommand extends Command throw new InvalidCommandOutput; } - $this->writeFile($this->path, app(LivewireTableClassGenerator::class, [ + $this->writeFile($this->path, app(LivewireTableComponentClassGenerator::class, [ 'fqn' => $this->fqn, 'modelFqn' => $this->modelFqn, 'isGenerated' => $this->isGenerated, diff --git a/packages/tables/src/Commands/MakeTableCommand.php b/packages/tables/src/Commands/MakeTableCommand.php new file mode 100644 index 0000000000..6385c656a2 --- /dev/null +++ b/packages/tables/src/Commands/MakeTableCommand.php @@ -0,0 +1,206 @@ + + */ + protected string $modelFqn; + + protected string $modelFqnEnd; + + protected bool $isGenerated; + + /** + * @var array + */ + protected $aliases = [ + 'filament:table', + ]; + + /** + * @return array + */ + protected function getArguments(): array + { + return [ + new InputArgument( + name: 'name', + mode: InputArgument::OPTIONAL, + description: 'The name of the table class to generate, optionally prefixed with directories', + ), + new InputArgument( + name: 'model', + mode: InputArgument::OPTIONAL, + description: 'The name of the model to generate the table for, optionally prefixed with directories', + ), + ]; + } + + /** + * @return array + */ + protected function getOptions(): array + { + return [ + new InputOption( + name: 'generate', + shortcut: 'G', + mode: InputOption::VALUE_NONE, + description: 'Generate the table columns based on the attributes of a model', + ), + new InputOption( + name: 'model-namespace', + shortcut: null, + mode: InputOption::VALUE_REQUIRED, + description: 'The namespace of the model class, [App\\Models] by default', + ), + new InputOption( + name: 'force', + shortcut: 'F', + mode: InputOption::VALUE_NONE, + description: 'Overwrite the contents of the files if they already exist', + ), + ]; + } + + public function handle(): int + { + try { + $this->configureFqnEnd(); + $this->configureModel(); + $this->configureIsGenerated(); + + $this->configureLocation(); + + $this->createTable(); + } catch (InvalidCommandOutput) { + return static::INVALID; + } + + $this->components->info("Table [{$this->fqn}] created successfully."); + + return static::SUCCESS; + } + + protected function configureFqnEnd(): void + { + $this->fqnEnd = (string) str($this->argument('name') ?? text( + label: 'What is the Table name?', + placeholder: 'BlogPostsTable', + required: true, + )) + ->trim('/') + ->trim('\\') + ->trim(' ') + ->studly() + ->replace('/', '\\'); + } + + protected function configureModel(): void + { + $modelNamespace = $this->option('model-namespace') ?? 'App\\Models'; + + $modelFqns = collect(get_declared_classes()) + ->filter(fn (string $class): bool => is_subclass_of($class, Model::class) && + str($class)->startsWith("{$modelNamespace}\\")) + ->map(fn (string $class): string => str($class)->after("{$modelNamespace}\\")) + ->all(); + + $this->modelFqnEnd = (string) str($this->argument('model') ?? suggest( + label: 'What is the model name?', + options: function (string $search) use ($modelFqns): array { + $search = str($search)->trim()->replace(['\\', '/'], ''); + + if (blank($search)) { + return $modelFqns; + } + + return array_filter( + $modelFqns, + fn (string $class): bool => str($class)->replace(['\\', '/'], '')->contains($search, ignoreCase: true), + ); + }, + placeholder: 'BlogPost', + )) + ->trim('/') + ->trim('\\') + ->trim(' ') + ->studly() + ->replace('/', '\\'); + + $this->modelFqn = "{$modelNamespace}\\{$this->modelFqnEnd}"; + } + + protected function configureIsGenerated(): void + { + $this->isGenerated = $this->option('generate') || confirm( + label: 'Would you like to generate the table columns based on the attributes of the model?', + default: false, + ); + } + + protected function configureLocation(): void + { + [ + $namespace, + $path, + ] = $this->askForComponentLocation( + path: 'Tables', + question: 'Where would you like to create the table?', + ); + + $this->fqn = "{$namespace}\\{$this->fqnEnd}"; + $this->path = (string) str("{$path}\\{$this->fqnEnd}.php") + ->replace('\\', '/') + ->replace('//', '/'); + } + + protected function createTable(): void + { + if (! $this->option('force') && $this->checkForCollision($this->path)) { + throw new InvalidCommandOutput; + } + + $this->writeFile($this->path, app(TableClassGenerator::class, [ + 'fqn' => $this->fqn, + 'modelFqn' => $this->modelFqn, + 'isGenerated' => $this->isGenerated, + ])); + } +} diff --git a/packages/tables/src/TablesServiceProvider.php b/packages/tables/src/TablesServiceProvider.php index b9351e3030..028c7fa84b 100644 --- a/packages/tables/src/TablesServiceProvider.php +++ b/packages/tables/src/TablesServiceProvider.php @@ -24,6 +24,7 @@ class TablesServiceProvider extends PackageServiceProvider ->hasCommands([ Commands\MakeColumnCommand::class, Commands\MakeLivewireTableCommand::class, + Commands\MakeTableCommand::class, ]) ->hasTranslations() ->hasViews();