Skip to content

Commit f31402e

Browse files
authored
Panel Generation and Registration Improvements: (#139)
- Added Automatic Discovery of all module panels to avoid manual registration in providers.php. This ensures modules are completely decoupled from the main app - Panel Generation: Added ability to pass an input option to specify the Panel Label, which is used in the Main navigation as well as the brand name of the panel - Removed the code to auto-add the panels to bootstrap/providers.php file
2 parents 8b40d50 + f92f3ab commit f31402e

File tree

3 files changed

+81
-32
lines changed

3 files changed

+81
-32
lines changed

src/Commands/FileGenerators/ModulePanelProviderClassGenerator.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ final public function __construct(
3333
protected string $fqn,
3434
protected string $id,
3535
protected string $moduleName,
36+
protected string $navigationLabel,
3637
protected bool $isDefault = false,
3738
) {
3839
$this->module = \Module::find($this->moduleName);
@@ -84,6 +85,7 @@ public function getExtends(): string
8485
protected function addMethodsToClass(ClassType $class): void
8586
{
8687
$this->addPanelMethodToClass($class);
88+
$this->addNavigationLabelMethodToClass($class);
8789
}
8890

8991
public function getModule(): \Nwidart\Modules\Module
@@ -103,6 +105,25 @@ protected function addPanelMethodToClass(ClassType $class): void
103105
$this->configurePanelMethod($method);
104106
}
105107

108+
protected function addNavigationLabelMethodToClass(ClassType $class): void
109+
{
110+
$class->addMethod('getNavigationLabel')
111+
->setPublic()
112+
->setReturnType('string')
113+
->setBody($this->generateNavigationLabelMethodBody());
114+
}
115+
116+
protected function generateNavigationLabelMethodBody(): string
117+
{
118+
$navigationLabel = $this->navigationLabel;
119+
120+
return new Literal(
121+
<<<PHP
122+
return __("$navigationLabel");
123+
PHP,
124+
);
125+
}
126+
106127
public function generatePanelMethodBody(): string
107128
{
108129
$isDefault = $this->isDefault();
@@ -137,7 +158,7 @@ public function generatePanelMethodBody(): string
137158
return \$panel{$defaultOutput}
138159
->id(?)
139160
->path(?){$loginOutput}
140-
->brandName("$label")
161+
->brandName(\$this->getNavigationLabel())
141162
->colors([
142163
'primary' => {$this->simplifyFqn(Color::class)}::Amber,
143164
])

src/Commands/ModuleMakeFilamentPanelCommand.php

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
use Filament\Support\Commands\Concerns\CanGeneratePanels;
99
use Filament\Support\Commands\Concerns\CanManipulateFiles;
1010
use Filament\Support\Commands\Exceptions\FailureCommandOutput;
11-
use Illuminate\Support\Facades\App;
12-
use Illuminate\Support\ServiceProvider;
1311
use Illuminate\Support\Str;
1412
use Symfony\Component\Console\Input\InputArgument;
1513
use Symfony\Component\Console\Input\InputOption;
@@ -66,6 +64,12 @@ protected function getOptions(): array
6664
mode: InputOption::VALUE_NONE,
6765
description: 'Overwrite the contents of the files if they already exist',
6866
),
67+
new InputOption(
68+
name: 'label',
69+
shortcut: null,
70+
mode: InputOption::VALUE_OPTIONAL,
71+
description: 'The navigation label for the panel',
72+
),
6973
];
7074
}
7175

@@ -75,7 +79,7 @@ public function handle(): int
7579
$this->ensureModuleArgument();
7680
$this->generatePanel(
7781
id: $this->argument('id'),
78-
placeholderId: 'app',
82+
placeholderId: 'default',
7983
isForced: $this->option('force'),
8084
);
8185
} catch (FailureCommandOutput) {
@@ -85,6 +89,24 @@ public function handle(): int
8589
return static::SUCCESS;
8690
}
8791

92+
protected function ensureNavigationLabelOption(): void
93+
{
94+
if (! $this->option('label')) {
95+
$label = text(
96+
label: 'What is the navigation label for the panel?',
97+
placeholder: Str::title($this->argument('id') ?? $this->getModule()->getName() . ' App'),
98+
required: true,
99+
validate: fn (string $value) => empty($value) ? 'The navigation label cannot be empty.' : null,
100+
hint: 'This is used in the navigation to identify the panel.',
101+
);
102+
if (empty($label)) {
103+
$this->components->error('Navigation label cannot be empty. Aborting panel creation.');
104+
exit(1);
105+
}
106+
$this->input->setOption('label', $label);
107+
}
108+
}
109+
88110
protected function ensureModuleArgument(): void
89111
{
90112
if (! $this->argument('module')) {
@@ -123,14 +145,15 @@ public function generatePanel(?string $id = null, string $defaultId = '', string
123145
$this->components->error('Panel ID cannot be empty. Aborting panel creation.');
124146
exit(1);
125147
}
148+
$this->ensureNavigationLabelOption();
126149

127150
$basename = (string) str($id)
128151
->studly()
129152
->append('PanelProvider');
130153

131154
$path = $module->appPath(
132155
(string) str($basename)
133-
->prepend('Providers/Filament/')
156+
->prepend('/Providers/Filament/')
134157
->replace('\\', '/')
135158
->append('.php'),
136159
);
@@ -145,33 +168,9 @@ public function generatePanel(?string $id = null, string $defaultId = '', string
145168
'fqn' => $fqn,
146169
'id' => $id,
147170
'moduleName' => $module->getName(),
171+
'navigationLabel' => $this->option('label'),
148172
]));
149173

150-
$hasBootstrapProvidersFile = file_exists($bootstrapProvidersPath = App::getBootstrapProvidersPath());
151-
152-
if ($hasBootstrapProvidersFile) {
153-
ServiceProvider::addProviderToBootstrapFile(
154-
$fqn,
155-
$bootstrapProvidersPath,
156-
);
157-
} else {
158-
$appConfig = file_get_contents(config_path('app.php'));
159-
160-
if (! Str::contains($appConfig, "{$fqn}::class")) {
161-
file_put_contents(config_path('app.php'), str_replace(
162-
app()->getNamespace() . 'Providers\\RouteServiceProvider::class,',
163-
"{$fqn}::class," . PHP_EOL . ' ' . app()->getNamespace() . 'Providers\\RouteServiceProvider::class,',
164-
$appConfig,
165-
));
166-
}
167-
}
168-
169174
$this->components->info("Filament panel [{$path}] created successfully.");
170-
171-
if ($hasBootstrapProvidersFile) {
172-
$this->components->warn("We've attempted to register the {$basename} in your [bootstrap/providers.php] file. If you get an error while trying to access your panel then this process has probably failed. You can manually register the service provider by adding it to the array.");
173-
} else {
174-
$this->components->warn("We've attempted to register the {$basename} in your [config/app.php] file as a service provider. If you get an error while trying to access your panel then this process has probably failed. You can manually register the service provider by adding it to the [providers] array.");
175-
}
176175
}
177176
}

src/ModulesServiceProvider.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,20 @@ public function configurePackage(Package $package): void
5959
public function packageRegistered(): void
6060
{
6161
$this->registerModuleMacros();
62+
$this->autoDiscoverPanels();
6263
}
6364

6465
public function attemptToRegisterModuleProviders(): void
6566
{
6667
// It is necessary to register them here to avoid late registration (after Panels have already been booted)
67-
$pattern1 = config('modules.paths.modules', 'Modules') . '/*' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'Providers' . DIRECTORY_SEPARATOR . '*Provider.php';
68-
$pattern2 = config('modules.paths.modules', 'Modules') . '/*' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'Providers' . DIRECTORY_SEPARATOR . 'Filament' . DIRECTORY_SEPARATOR . '*Provider.php';
68+
$pattern1 = config(
69+
'modules.paths.modules',
70+
'Modules'
71+
) . '/*' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'Providers' . DIRECTORY_SEPARATOR . '*Provider.php';
72+
$pattern2 = config(
73+
'modules.paths.modules',
74+
'Modules'
75+
) . '/*' . DIRECTORY_SEPARATOR . '*' . DIRECTORY_SEPARATOR . 'Providers' . DIRECTORY_SEPARATOR . 'Filament' . DIRECTORY_SEPARATOR . '*Provider.php';
6976
$serviceProviders = glob($pattern1);
7077
$panelProviders = glob($pattern2);
7178
// dd($panelProviders);
@@ -82,6 +89,28 @@ public function attemptToRegisterModuleProviders(): void
8289
}
8390
}
8491

92+
public function autoDiscoverPanels(): void
93+
{
94+
$this->app->beforeResolving('filament', function () {
95+
$modules = \Module::allEnabled();
96+
$cacheKey = 'filament-modules-panel-providers';
97+
$ttl = 10; // 24 hours
98+
$modules = \Module::allEnabled();
99+
$panels = collect($modules)->flatMap(function (Module $module) {
100+
$panelProviders = glob($module->getExtraPath('app/Providers/Filament') . '/*.php');
101+
102+
return collect($panelProviders)->map(function ($path) {
103+
return $this->app[Modules::class]->convertPathToNamespace($path);
104+
})->toArray();
105+
})->toArray();
106+
foreach ($panels as $panel) {
107+
if (class_exists($panel)) {
108+
$this->app->register($panel);
109+
}
110+
}
111+
});
112+
}
113+
85114
public function packageBooted(): void
86115
{
87116
$this->attemptToRegisterModuleProviders();

0 commit comments

Comments
 (0)