From 2b787e64750796ef4e9f2390eb31cee2b92dc360 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Mon, 28 Jul 2025 22:37:34 +0100 Subject: [PATCH] Ask for record title attribute when generating a resource --- .../Resources/ResourceClassGenerator.php | 24 +++++++++ .../src/Commands/MakeResourceCommand.php | 29 +++++++++++ ...e_class_with_a_record_title_attribute.snap | 50 +++++++++++++++++++ .../Commands/MakeResourceCommandTest.php | 14 ++++++ 4 files changed, 117 insertions(+) create mode 100644 tests/.pest/snapshots/src/Panels/Commands/MakeResourceCommandTest/it_can_generate_a_resource_class_with_a_record_title_attribute.snap diff --git a/packages/panels/src/Commands/FileGenerators/Resources/ResourceClassGenerator.php b/packages/panels/src/Commands/FileGenerators/Resources/ResourceClassGenerator.php index d6a22258c1..7e8ee2b9fb 100644 --- a/packages/panels/src/Commands/FileGenerators/Resources/ResourceClassGenerator.php +++ b/packages/panels/src/Commands/FileGenerators/Resources/ResourceClassGenerator.php @@ -52,6 +52,7 @@ class ResourceClassGenerator extends ClassGenerator protected ?string $tableFqn, protected ?string $clusterFqn, protected ?string $parentResourceFqn, + protected ?string $recordTitleAttribute, protected bool $hasViewOperation, protected bool $isGenerated, protected bool $isSoftDeletable, @@ -105,6 +106,7 @@ class ResourceClassGenerator extends ClassGenerator $this->addNavigationIconPropertyToClass($class); $this->addClusterPropertyToClass($class); $this->addParentResourcePropertyToClass($class); + $this->addRecordTitleAttributePropertyToClass($class); } protected function addMethodsToClass(ClassType $class): void @@ -172,6 +174,23 @@ class ResourceClassGenerator extends ClassGenerator protected function configureParentResourceProperty(Property $property): void {} + protected function addRecordTitleAttributePropertyToClass(ClassType $class): void + { + $recordTitleAttribute = $this->getRecordTitleAttribute(); + + if (blank($recordTitleAttribute)) { + return; + } + + $property = $class->addProperty('recordTitleAttribute', $recordTitleAttribute) + ->setProtected() + ->setStatic() + ->setType('?string'); + $this->configureRecordTitleAttributeProperty($property); + } + + protected function configureRecordTitleAttributeProperty(Property $property): void {} + protected function addFormMethodToClass(ClassType $class): void { $formSchemaFqn = $this->getFormSchemaFqn(); @@ -472,4 +491,9 @@ class ResourceClassGenerator extends ClassGenerator { return $this->isSimple; } + + public function getRecordTitleAttribute(): ?string + { + return $this->recordTitleAttribute; + } } diff --git a/packages/panels/src/Commands/MakeResourceCommand.php b/packages/panels/src/Commands/MakeResourceCommand.php index 1c2ebc3be7..a70e7a2447 100644 --- a/packages/panels/src/Commands/MakeResourceCommand.php +++ b/packages/panels/src/Commands/MakeResourceCommand.php @@ -33,6 +33,7 @@ use Symfony\Component\Console\Input\InputOption; use function Filament\Support\discover_app_classes; use function Laravel\Prompts\confirm; use function Laravel\Prompts\suggest; +use function Laravel\Prompts\text; #[AsCommand(name: 'make:filament-resource', aliases: [ 'filament:make-resource', @@ -96,6 +97,8 @@ class MakeResourceCommand extends Command protected ?string $tableFqn = null; + protected ?string $recordTitleAttribute = null; + protected bool $hasViewOperation; protected bool $isGenerated; @@ -197,6 +200,12 @@ class MakeResourceCommand extends Command mode: InputOption::VALUE_REQUIRED, description: 'The panel to create the resource in', ), + new InputOption( + name: 'record-title-attribute', + shortcut: null, + mode: InputOption::VALUE_REQUIRED, + description: 'The title attribute, used to label each record in the UI', + ), new InputOption( name: 'resource-namespace', shortcut: null, @@ -234,6 +243,7 @@ class MakeResourceCommand extends Command { try { $this->configureModel(); + $this->configureRecordTitleAttribute(); $this->configurePanel(question: 'Which panel would you like to create this resource in?'); $this->configureIsSimple(); $this->configureIsNested(); @@ -342,6 +352,24 @@ class MakeResourceCommand extends Command } } + protected function configureRecordTitleAttribute(): void + { + $this->recordTitleAttribute = $this->option('record-title-attribute'); + + if (filled($this->recordTitleAttribute)) { + return; + } + + info('The "title attribute" is used to label each record in the UI.'); + + info('You can leave this blank if records do not have a title.'); + + $this->recordTitleAttribute = text( + label: 'What is the title attribute for this model?', + placeholder: 'name', + ); + } + protected function configureIsSimple(): void { $this->isSimple = $this->option('simple'); @@ -612,6 +640,7 @@ class MakeResourceCommand extends Command 'formSchemaFqn' => $this->formSchemaFqn, 'infolistSchemaFqn' => $this->infolistSchemaFqn, 'tableFqn' => $this->tableFqn, + 'recordTitleAttribute' => $this->recordTitleAttribute, 'hasViewOperation' => $this->hasViewOperation, 'isGenerated' => $this->isGenerated, 'isSoftDeletable' => $this->isSoftDeletable, diff --git a/tests/.pest/snapshots/src/Panels/Commands/MakeResourceCommandTest/it_can_generate_a_resource_class_with_a_record_title_attribute.snap b/tests/.pest/snapshots/src/Panels/Commands/MakeResourceCommandTest/it_can_generate_a_resource_class_with_a_record_title_attribute.snap new file mode 100644 index 0000000000..25b8ec4fb1 --- /dev/null +++ b/tests/.pest/snapshots/src/Panels/Commands/MakeResourceCommandTest/it_can_generate_a_resource_class_with_a_record_title_attribute.snap @@ -0,0 +1,50 @@ + ListPosts::route('/'), + 'create' => CreatePost::route('/create'), + 'edit' => EditPost::route('/{record}/edit'), + ]; + } +} diff --git a/tests/src/Panels/Commands/MakeResourceCommandTest.php b/tests/src/Panels/Commands/MakeResourceCommandTest.php index 95b670a182..31e2566aaa 100644 --- a/tests/src/Panels/Commands/MakeResourceCommandTest.php +++ b/tests/src/Panels/Commands/MakeResourceCommandTest.php @@ -30,6 +30,20 @@ it('can generate a resource class', function (): void { ->toMatchSnapshot(); }); +it('can generate a resource class with a record title attribute', function (): void { + $this->artisan('make:filament-resource', [ + 'model' => 'Post', + '--model-namespace' => 'Filament\Tests\Fixtures\Models', + '--panel' => 'admin', + '--record-title-attribute' => 'title', + '--no-interaction' => true, + ]); + + assertFileExists($path = app_path('Filament/Resources/Posts/PostResource.php')); + expect(file_get_contents($path)) + ->toMatchSnapshot(); +}); + it('can generate a resource form', function (): void { $this->artisan('make:filament-resource', [ 'model' => 'Post',