diff --git a/app/Console/.DS_Store b/app/Console/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/app/Console/.DS_Store differ diff --git a/app/Console/Commands/.DS_Store b/app/Console/Commands/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/app/Console/Commands/.DS_Store differ diff --git a/app/Console/Commands/MakeModule.php b/app/Console/Commands/MakeModule.php new file mode 100644 index 0000000..d57dac9 --- /dev/null +++ b/app/Console/Commands/MakeModule.php @@ -0,0 +1,309 @@ +> + */ + protected function promptForMissingArgumentsUsing(): array + { + return [ + 'module' => ['Module name', 'News, Product, Order...'], + ]; + } + + /** + * Create a new command instance. + * + * @return void + */ + public function __construct(Filesystem $files) + { + parent::__construct(); + + $this->files = $files; + } + + /** + * Execute the console command. + */ + public function handle(): void + { + /* @var string */ + $module = $this->argument('module'); + $this->moduleName = $module; + $this->moduleDirectory = modules_path($module.'/'); + + // Create module directory if not exists... + $this->makeDirectory($this->moduleDirectory); + + // Make a module file... + $this->makeModuleFile($this->moduleDirectory); + + // Models... + $this->makeDirectory($this->moduleDirectory.'Models'); + $this->makeModelFile($this->moduleDirectory.'Models'); + + // Database... + $this->makeDirectory($this->moduleDirectory.'Database'); + $this->makeDirectory($this->moduleDirectory.'Database/Migrations'); + $this->makeMigrationFile($this->moduleDirectory.'Database/Migrations'); + + // Controller... + $this->makeDirectory($this->moduleDirectory.'Controllers'); + $this->makeController($this->moduleDirectory.'Controllers'); + + // Repository... + $this->makeDirectory($this->moduleDirectory.'Repositories'); + $this->makeRepository($this->moduleDirectory.'Repositories'); + + // Filament resource... + $this->makeDirectory($this->moduleDirectory.'Filament'); + $this->makeDirectory($this->moduleDirectory.'Filament/Resources'); + $this->makeFilamentResource($this->moduleDirectory.'Filament/Resources'); + } + + /** + * Make module file + */ + public function makeModuleFile(string $moduleDirectory): void + { + $creationStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/%sModule', $moduleDirectory, $this->moduleName), + stubFile: __DIR__.'/stubs/make-module/module.stub', + stubVariables: [ + 'NAMESPACE' => sprintf('App\\Modules\\%s', $this->moduleName), + 'CLASS_NAME' => $this->moduleName.'Module', + ] + ); + + $creationStatus + ? $this->info("Module created: {$this->moduleName} created") + : $this->info("Module: {$this->moduleName} already exits"); + } + + /** + * Make model file + */ + protected function makeModelFile(string $modelPath): void + { + $creationStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/%s', $modelPath, $this->moduleName), + stubFile: __DIR__.'/stubs/make-module/model.stub', + stubVariables: [ + 'NAMESPACE' => sprintf('App\\Modules\\%s\\Models', $this->moduleName), + 'CLASS_NAME' => $this->moduleName, + ] + ); + + $creationStatus + ? $this->info("Model created: {$this->moduleName} created") + : $this->info("Model: {$this->moduleName} already exits"); + } + + /** + * Make migration file + */ + protected function makeMigrationFile(string $migrationsPath): void + { + $migrationCreator = new MigrationCreator( + files: new Filesystem, + customStubPath: app_path('Console/Commands/stubs/make-module/migration.stub') + ); + + $migrationPath = $migrationCreator->create(Str::lower('create_'.Str::snake(Str::plural($this->moduleName)).'_table'), $migrationsPath); + $migrationName = Str::afterLast($migrationPath, '/'); + + $this->info("Migration created: {$migrationName} created"); + } + + /** + * Make controller file + */ + protected function makeController(string $controllersPath): void + { + $creationStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/%sController', $controllersPath, $this->moduleName), + stubFile: app_path('Console/Commands/stubs/make-module/controller.stub'), + stubVariables: [ + 'NAMESPACE' => sprintf('App\\Modules\\%s\\Controllers', $this->moduleName), + 'CONTROLLER_NAME' => $this->moduleName.'Controller', + ] + ); + + $creationStatus + ? $this->info("Controller created: {$this->moduleName} created") + : $this->info("Controller: {$this->moduleName} already exits"); + } + + /** + * Make repository file + */ + protected function makeRepository(string $path): void + { + $creationStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/%s', $path, $this->moduleName.'Repository'), + stubFile: __DIR__.'/stubs/make-module/repository.stub', + stubVariables: [ + 'NAMESPACE' => sprintf('App\Modules\%s\Repositories', $this->moduleName), + 'MODULE_NAME' => ucfirst($this->moduleName), + 'MODEL_NAMESPACE' => sprintf('App\Modules\%s\Models\%s', $this->moduleName, $this->moduleName), + ], + ); + + $creationStatus + ? $this->info("Repository created: {$this->moduleName} created") + : $this->info("Repository: {$this->moduleName} already exits"); + } + + /** + * Make nova resource file + */ + public function makeFilamentResource(string $path): void + { + // Base resource... + $resourceCreateStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/%s', $path, $this->moduleName.'Resource'), + stubFile: __DIR__.'/stubs/make-module/filament/base-resource.stub', + stubVariables: [ + 'NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources', $this->moduleName), + 'MODEL_NAMESPACE' => sprintf('App\Modules\%s\Models\%s', $this->moduleName, $this->moduleName), + 'MODEL_NAME_SINGULAR' => Str::of($this->moduleName)->singular(), + 'MODEL_NAME_PLURAL' => Str::of($this->moduleName)->plural(), + ], + ); + + $resourceCreateStatus + ? $this->info("Filament resource created: {$this->moduleName} created") + : $this->info("Filament resource: {$this->moduleName} already exits"); + + $resourcePagesDir = $this->makeDirectory(sprintf('%s/%sResource', $path, $this->moduleName)); + + // Create page... + $resourceCreatePageStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/Create%s', $resourcePagesDir, $this->moduleName), + stubFile: __DIR__.'/stubs/make-module/filament/pages/create-resource-page.stub', + stubVariables: [ + 'CREATE_RESOURCE_PAGE_NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources\%sResource\Pages', $this->moduleName, $this->moduleName), + 'BASE_RESOURCE_NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources\%sResource', $this->moduleName, $this->moduleName), + 'MODEL_NAME_SINGULAR' => Str::of($this->moduleName)->singular(), + ], + ); + + $resourceCreatePageStatus + ? $this->info("Filament create page resource created: {$this->moduleName} created") + : $this->info("Filament create page resource: {$this->moduleName} already exits"); + + // Edit page... + $resourceEditPageStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/Edit%s', $resourcePagesDir, $this->moduleName), + stubFile: __DIR__.'/stubs/make-module/filament/pages/edit-resource-page.stub', + stubVariables: [ + 'EDIT_RESOURCE_PAGE_NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources\%sResource\Pages', $this->moduleName, $this->moduleName), + 'BASE_RESOURCE_NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources\%sResource', $this->moduleName, $this->moduleName), + 'MODEL_NAME_SINGULAR' => Str::of($this->moduleName)->singular(), + ], + ); + + $resourceEditPageStatus + ? $this->info("Filament edit page resource created: {$this->moduleName} created") + : $this->info("Filament edit page resource: {$this->moduleName} already exits"); + + // List page... + $resourceListPageStatus = $this->createFileFromStub( + createFilePath: sprintf('%s/List%s', $resourcePagesDir, Str::of($this->moduleName)->plural()), + stubFile: __DIR__.'/stubs/make-module/filament/pages/list-resource-page.stub', + stubVariables: [ + 'LIST_RESOURCE_PAGE_NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources\%sResource\Pages', $this->moduleName, $this->moduleName), + 'BASE_RESOURCE_NAMESPACE' => sprintf('App\Modules\%s\Filament\Resources\%sResource', $this->moduleName, $this->moduleName), + 'MODEL_NAME_SINGULAR' => Str::of($this->moduleName)->singular(), + 'MODEL_NAME_PLURAL' => Str::of($this->moduleName)->plural(), + ], + ); + + $resourceListPageStatus + ? $this->info("Filament list page resource created: {$this->moduleName} created") + : $this->info("Filament list page resource: {$this->moduleName} already exits"); + } + + /** + * Build the directory for the class if necessary. + */ + protected function makeDirectory(string $path): string + { + if (! $this->files->isDirectory($path)) { + $this->files->makeDirectory($path, 0777, true, true); + } + + return $path; + } + + /** + * Create a file from stub + * + * @param array $stubVariables + */ + protected function createFileFromStub(string $createFilePath, string $stubFile, array $stubVariables): int|bool + { + $contents = $this->getStubContents($stubFile, $stubVariables); + + return $this->files->missing($createFilePath) + ? $this->files->put($createFilePath.'.php', $contents) + : false; + } + + /** + * Replace the stub variables(key) with the desire value + * + * @param array $stubVariables + */ + protected function getStubContents(string $stub, array $stubVariables = []): string + { + $contents = (string) file_get_contents($stub); + + foreach ($stubVariables as $search => $replace) { + $contents = str_replace('$'.$search.'$', $replace, $contents); + } + + return $contents; + } +} diff --git a/app/Console/Commands/stubs/make-module/.DS_Store b/app/Console/Commands/stubs/make-module/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/app/Console/Commands/stubs/make-module/.DS_Store differ diff --git a/app/Console/Commands/stubs/make-module/controller.stub b/app/Console/Commands/stubs/make-module/controller.stub new file mode 100644 index 0000000..d176162 --- /dev/null +++ b/app/Console/Commands/stubs/make-module/controller.stub @@ -0,0 +1,49 @@ +schema([ + // ... + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => List$MODEL_NAME_PLURAL$::route('/'), + 'create' => Create$MODEL_NAME_SINGULAR$::route('/create'), + 'edit' => Edit$MODEL_NAME_SINGULAR$::route('/{record}/edit'), + ]; + } +} diff --git a/app/Console/Commands/stubs/make-module/filament/pages/create-resource-page.stub b/app/Console/Commands/stubs/make-module/filament/pages/create-resource-page.stub new file mode 100644 index 0000000..f3d247e --- /dev/null +++ b/app/Console/Commands/stubs/make-module/filament/pages/create-resource-page.stub @@ -0,0 +1,11 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('{{ table }}'); + } +}; diff --git a/app/Console/Commands/stubs/make-module/model.stub b/app/Console/Commands/stubs/make-module/model.stub new file mode 100644 index 0000000..801bbfa --- /dev/null +++ b/app/Console/Commands/stubs/make-module/model.stub @@ -0,0 +1,11 @@ +enabled; + } + + /** + * Disable module + */ + public function disable(): void + { + $this->enabled = false; + } + + /** + * Enable module + */ + public function enable(): void + { + $this->enabled = true; + } + + /** + * Check if module has a filament resource + */ + public function hasFilamentResource(): bool + { + return true; + } +} diff --git a/app/Console/Commands/stubs/make-module/nova-resource.stub b/app/Console/Commands/stubs/make-module/nova-resource.stub new file mode 100644 index 0000000..9cc4216 --- /dev/null +++ b/app/Console/Commands/stubs/make-module/nova-resource.stub @@ -0,0 +1,74 @@ + + */ +class $RESOURCE_NAME_SINGULAR$ extends Resource +{ + /** + * The model the resource corresponds to. + * + * @var class-string<\$MODEL_NAMESPACE$> + */ + public static $model = \$MODEL_NAMESPACE$::class; + + /** + * The single value that should be used to represent the resource when being displayed. + * + * @var string + */ + public static $title = 'id'; + + /** + * The columns that should be searched. + * + * @var array + */ + public static $search = [ + 'id', + ]; + + /** + * The relationships that should be eager loaded on index queries. + * + * @var array + */ + // public static $with = []; + + /** + * Get the displayable label of the resource. + */ + public static function label(): string + { + return __('$MODEL_NAME_PLURAL$'); + } + + /** + * Get the displayable singular label of the resource. + */ + public static function singularLabel(): string + { + return __('$MODEL_NAME_SINGULAR$'); + } + + /** + * Get the fields displayed by the resource. + * + * @param \Laravel\Nova\Http\Requests\NovaRequest $request + * @return array + */ + public function fields(NovaRequest $request): array + { + return [ + ID::make('id')->sortable(), + ]; + } +} diff --git a/app/Console/Commands/stubs/make-module/repository.stub b/app/Console/Commands/stubs/make-module/repository.stub new file mode 100644 index 0000000..8073e04 --- /dev/null +++ b/app/Console/Commands/stubs/make-module/repository.stub @@ -0,0 +1,10 @@ +array('app.locales'); } + +/** + * Get module + */ +function module(string $moduleName): ModuleContract +{ + $moduleClass = 'App\\Modules\\'.$moduleName.'\\'.$moduleName.'Module'; + + return class_exists($moduleClass) ? (new $moduleClass) : emptyModule(); +} + +/** + * Empty module + */ +function emptyModule(): ModuleContract +{ + return new EmptyModule; +} + +/** + * Modules directory path + */ +function modules_path(string $path = ''): string +{ + return app_path('Modules/'.$path); +} + +/** + * Modules + * + * @return Collection + */ +function modules(bool $withDisabled = false): Collection +{ + /** @var array */ + $modulesDir = File::directories(modules_path()); + $modules = collect(); + + foreach ($modulesDir as $modulePath) { + $moduleName = Str::afterLast($modulePath, '/'); + + $moduleOptions = [ + 'path' => $modulePath, + 'name' => $moduleName.'Module', + 'enabled' => module($moduleName)->isEnabled(), + ]; + + $modules->push($moduleOptions); + + // Include all + if ($withDisabled) { + continue; + } + + if ($moduleOptions['enabled']) { + $modules->push($moduleOptions); + } + } + + return $modules; +} diff --git a/app/Modules/EmptyModule.php b/app/Modules/EmptyModule.php new file mode 100644 index 0000000..90d9197 --- /dev/null +++ b/app/Modules/EmptyModule.php @@ -0,0 +1,32 @@ +enabled; + } + + /** + * Disable module + */ + public function disable(): void + { + $this->enabled = false; + } + + /** + * Enable module + */ + public function enable(): void + { + $this->enabled = true; + } + + /** + * Check if module has a filament resource + */ + public function hasFilamentResource(): bool + { + return true; + } +} diff --git a/app/Modules/EntrepreneurLetterNumber/Filament/Resources/EntrepreneurLetterNumberResource.php b/app/Modules/EntrepreneurLetterNumber/Filament/Resources/EntrepreneurLetterNumberResource.php new file mode 100644 index 0000000..4d58917 --- /dev/null +++ b/app/Modules/EntrepreneurLetterNumber/Filament/Resources/EntrepreneurLetterNumberResource.php @@ -0,0 +1,62 @@ +schema([ + // ... + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => ListEntrepreneurLetterNumbers::route('/'), + 'create' => CreateEntrepreneurLetterNumber::route('/create'), + 'edit' => EditEntrepreneurLetterNumber::route('/{record}/edit'), + ]; + } +} diff --git a/app/Modules/EntrepreneurLetterNumber/Filament/Resources/EntrepreneurLetterNumberResource/CreateEntrepreneurLetterNumber.php b/app/Modules/EntrepreneurLetterNumber/Filament/Resources/EntrepreneurLetterNumberResource/CreateEntrepreneurLetterNumber.php new file mode 100644 index 0000000..994c8bc --- /dev/null +++ b/app/Modules/EntrepreneurLetterNumber/Filament/Resources/EntrepreneurLetterNumberResource/CreateEntrepreneurLetterNumber.php @@ -0,0 +1,11 @@ +isProduction()); + + $this->registerModules(); + } + + /** + * Register modules + */ + public function registerModules(): void + { + modules(); } } diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 058efbb..7d2e2f6 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -9,7 +9,6 @@ use Filament\Pages; use Filament\Panel; use Filament\PanelProvider; use Filament\Support\Colors\Color; -use Filament\Widgets; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -24,8 +23,8 @@ class AdminPanelProvider extends PanelProvider { return $panel ->default() - ->id('admin') - ->path('admin') + ->id('panel') + ->path('panel') ->login() ->colors([ 'primary' => Color::Amber, @@ -37,8 +36,6 @@ class AdminPanelProvider extends PanelProvider ]) ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') ->widgets([ - Widgets\AccountWidget::class, - Widgets\FilamentInfoWidget::class, ]) ->middleware([ EncryptCookies::class, diff --git a/phpstan.neon b/phpstan.neon index 2ad050b..ae30d68 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,5 +6,7 @@ parameters: paths: - app/ - # Level 9 is the highest level level: 6 + + ignoreErrors: + - '#Unsafe usage of new static#'