diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..10c74aa
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,24 @@
+# Set default line ending behavior, in case people don't have core.autocrlf set
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout
+*.php text eol=lf
+*.json text eol=lf
+*.md text eol=lf
+*.yml text eol=lf
+*.yaml text eol=lf
+
+# Denote all files that are truly binary and should not be modified
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+*.ico binary
+*.pdf binary
+
+# Composer files
+/composer.lock -diff
+
+# Git attributes for better diff and merge handling
+*.stub text eol=lf
+
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
new file mode 100644
index 0000000..7d257b5
--- /dev/null
+++ b/.github/workflows/php.yml
@@ -0,0 +1,39 @@
+name: PHP Composer
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate --strict
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v3
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
+
+ # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
+ # Docs: https://getcomposer.org/doc/articles/scripts.md
+
+ # - name: Run test suite
+ # run: composer run-script test
diff --git a/README.md b/README.md
index f015832..50cab95 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,14 @@
-# Kenernel243/Artisan
+# Kernel243/Artisan
-This package provides a set of new artisan commands for Laravel
+[](https://packagist.org/packages/kernel243/artisan)
+[](https://packagist.org/packages/kernel243/artisan)
+
+This package provides a set of new artisan commands for Laravel.
This package is based on another laravel package ``dannyvilla/artisan-commands``, my package adds other options which are not yet available in this one for example the use of a modular architecture with laravel ``nwidart/laravel-modules`` and many other options.
+Visit the developer website: [developper.elongocrea.com](https://developper.elongocrea.com)
+
## Installation
Use the package manager [composer](https://getcomposer.org/) to install kernel243/artisan
@@ -86,6 +91,18 @@ php artisan make:class App\Contracts\IClassable --kind=interface
php artisan make:file folder.subfolder1.subfolder2.filename --ext=php
```
+### CRUD command
+Generate a CRUD with model, repository, service, controller and Tailwind views
+```bash
+php artisan make:crud Product --fields="title:string,description:text,price:decimal,is_active:boolean"
+```
+
+### Resource CRUD command
+Generate CRUD using a Resource class (Filament-inspired)
+```bash
+php artisan make:resource-crud Product --fields="title:string,description:text"
+```
+
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
diff --git a/composer.json b/composer.json
index 4879b3f..45ca221 100644
--- a/composer.json
+++ b/composer.json
@@ -3,11 +3,27 @@
"description": "This package provides a set of artisan commands for Laravel",
"type": "library",
"license": "MIT",
+ "keywords": [
+ "laravel",
+ "artisan",
+ "commands",
+ "generator",
+ "repository",
+ "service",
+ "controller",
+ "resource",
+ "stubs"
+ ],
"autoload": {
"psr-4": {
"Kernel243\\Artisan\\": "src/"
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "Kernel243\\Artisan\\Tests\\": "tests/"
+ }
+ },
"extra": {
"laravel": {
"providers": [
@@ -21,9 +37,22 @@
"email": "merdielongo9@gmail.com"
}
],
- "minimum-stability": "dev",
+ "minimum-stability": "stable",
+ "prefer-stable": true,
"require": {
- "php": ">=7.4.0",
- "laravel/framework": ">=5.8.0"
+ "php": "^8.0|^8.1|^8.2|^8.3",
+ "laravel/framework": "^8.0|^9.0|^10.0|^11.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0|^10.0",
+ "orchestra/testbench": "^6.0|^7.0|^8.0"
+ },
+ "scripts": {
+ "test": "phpunit",
+ "analyse": "phpstan analyse"
+ },
+ "config": {
+ "sort-packages": true,
+ "optimize-autoloader": true
}
}
diff --git a/src/CommandServiceProvider.php b/src/CommandServiceProvider.php
index 9338cf3..fb51f98 100644
--- a/src/CommandServiceProvider.php
+++ b/src/CommandServiceProvider.php
@@ -3,30 +3,28 @@
namespace Kernel243\Artisan;
use Illuminate\Support\ServiceProvider;
-use Kernel243\Artisan\Commands\Controller;
-use Kernel243\Artisan\Commands\File;
-use Kernel243\Artisan\Commands\Repository;
-use Kernel243\Artisan\Commands\Resource;
-use Kernel243\Artisan\Commands\Service;
-use Kernel243\Artisan\Commands\View;
-
-class CommandServiceProvider extends ServiceProvider {
+class CommandServiceProvider extends ServiceProvider
+{
/**
* Bootstrap the application services.
*
* @return void
*/
- public function boot()
+ public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
- Repository::class,
- Service::class,
- File::class,
- View::class,
- Resource::class,
- Controller::class
+ Commands\Repository::class,
+ Commands\Service::class,
+ Commands\File::class,
+ Commands\View::class,
+ Commands\Resource::class,
+ Commands\Controller::class,
+ Commands\Lang::class,
+ Commands\ClassMakeCommand::class,
+ Commands\CrudMakeCommand::class,
+ Commands\ResourceMakeCommand::class,
]);
}
}
@@ -36,9 +34,8 @@ public function boot()
*
* @return void
*/
- public function register()
+ public function register(): void
{
//
}
-
}
diff --git a/src/Commands/BaseCommand.php b/src/Commands/BaseCommand.php
index f1bab0e..1454ca4 100644
--- a/src/Commands/BaseCommand.php
+++ b/src/Commands/BaseCommand.php
@@ -23,15 +23,15 @@ protected function moduleExists($module)
/**
* Checks if a folder exist and return canonicalized absolute pathname (sort version)
* @param string $folder the path being checked.
- * @return mixed returns the canonicalized absolute pathname on success otherwise FALSE is returned
+ * @return string|false returns the canonicalized absolute pathname on success otherwise FALSE is returned
*/
- protected function folderExist($folder)
+ protected function folderExist(string $folder)
{
// Get canonicalized absolute pathname
$path = realpath($folder);
// If it exists, check if it's a directory
- return ($path !== false AND is_dir($path)) ? $path : false;
+ return ($path !== false && is_dir($path)) ? $path : false;
}
/**
@@ -134,6 +134,74 @@ protected function replaceResourceNamespace($namespace, $stub)
return str_replace('DummyNamespaceResource', ucfirst($namespace), $stub);
}
+ /**
+ * Read a stub file by logical name from this package stubs directory.
+ */
+ protected function getStubContent(string $name): string
+ {
+ $path = __DIR__ . '/stubs/' . str_replace('.', '/', $name) . '.stub';
+ if (!file_exists($path)) {
+ throw new \RuntimeException('Stub not found: ' . $path);
+ }
+ return (string) file_get_contents($path);
+ }
+
+ /**
+ * Write content to a file, creating parent directories when needed.
+ */
+ protected function writeFile(string $path, string $content): void
+ {
+ $dir = dirname($path);
+ if (!is_dir($dir)) {
+ mkdir($dir, 0755, true);
+ }
+ file_put_contents($path, $content);
+ }
+
+ /**
+ * Ask whether a file should be replaced unless --force is provided.
+ */
+ protected function shouldReplaceFile(string $path, string $question = 'Replace file? [y/n]'): bool
+ {
+ if (!file_exists($path)) {
+ return true;
+ }
+ if ($this->option('force')) {
+ return true;
+ }
+ do {
+ $input = strtolower($this->ask($question));
+ } while ($input !== 'y' && $input !== 'n');
+ return $input === 'y';
+ }
+
+ /**
+ * Simple success output helper.
+ */
+ protected function displaySuccess(string $message, string $path): void
+ {
+ $this->info($message);
+ $this->line(' → ' . $path);
+ }
+
+ /**
+ * Ensure a directory exists.
+ */
+ protected function ensureDirectoryExists(string $directory): void
+ {
+ if (!file_exists($directory)) {
+ mkdir($directory, 0755, true);
+ }
+ }
+
+ /**
+ * Basic filename validation for ClassMakeCommand.
+ */
+ protected function isValidFilename(string $filename): bool
+ {
+ return (bool) preg_match('#^[A-Za-z0-9_\\\\/\.-]+$#', $filename);
+ }
+
/**
* Check if a repository file exists.
*
@@ -144,11 +212,12 @@ protected function replaceResourceNamespace($namespace, $stub)
protected function repositoryFileExists($repository, $module = null): bool
{
if (is_null($module)) {
- return file_exists( base_path(lcfirst($repository).'.php')) || file_exists( base_path(lcfirst(str_replace('\\', '/', $repository)).'.php'));
+ $repositoryName = ucfirst(basename(str_replace('\\', '/', $repository)));
+ return file_exists(app_path('Repositories/'.$repositoryName.'.php'));
}
- $path = base_path('Modules/'.ucfirst($module).'/Repositories/'.lcfirst($repository).'.php');
- return file_exists($path) || file_exists('Modules/'.base_path(ucfirst($module).'/Repositories/'.lcfirst(str_replace('\\', '/', $repository)).'.php'));
+ $path = base_path('Modules/'.ucfirst($module).'/Repositories/'.ucfirst(basename(str_replace('\\', '/', $repository))).'.php');
+ return file_exists($path);
}
/**
@@ -167,7 +236,7 @@ protected function setModelAndNamespace(&$model, &$namespace, &$module)
$module = '';
for ($i = 0; $i < count($exploded) - 1; $i++) {
- if (!isEmpty($module)) {
+ if (!empty($module)) {
$namespace .= $module.'\\'.$exploded[$i].'\\'.'Entities\\';
} else {
$namespace .= $exploded[$i].'\\';
@@ -193,7 +262,7 @@ protected function setRepositoryAndNamespace(&$repository, &$namespace, &$module
$module = '';
for ($i = 0; $i < count($exploded) - 1; $i++) {
- if (!isEmpty($module)) {
+ if (!empty($module)) {
$namespace .= $module.'\\'.$exploded[$i].'\\'.'Repositories\\';
} else {
$namespace .= $exploded[$i].'\\';
@@ -213,11 +282,12 @@ protected function setRepositoryAndNamespace(&$repository, &$namespace, &$module
protected function modelFileExists($model, $module = null): bool
{
if (is_null($module)) {
- return file_exists( base_path(lcfirst($model).'.php')) || file_exists( base_path(lcfirst(str_replace('\\', '/', $model)).'.php'));
+ $modelName = ucfirst(basename(str_replace('\\', '/', $model)));
+ return file_exists(app_path('Models/'.$modelName.'.php'));
}
- $path = base_path('Modules/'.ucfirst($module).'/Entities/'.lcfirst($model).'.php');
- return file_exists($path) || file_exists('Modules/'.base_path(ucfirst($module).'/Entities/'.lcfirst(str_replace('\\', '/', $model)).'.php'));
+ $path = base_path('Modules/'.ucfirst($module).'/Entities/'.ucfirst(basename(str_replace('\\', '/', $model))).'.php');
+ return file_exists($path);
}
diff --git a/src/Commands/ClassMakeCommand.php b/src/Commands/ClassMakeCommand.php
new file mode 100644
index 0000000..1574c19
--- /dev/null
+++ b/src/Commands/ClassMakeCommand.php
@@ -0,0 +1,106 @@
+argument('filename');
+
+ if (!$this->isValidFilename($filename)) {
+ $this->error('The filename is not correct. Only alphanumeric characters, dots, underscores, backslashes, and hyphens are allowed.');
+ return self::FAILURE;
+ }
+
+ $kind = $this->getKind();
+ if ($kind === null) {
+ return self::FAILURE;
+ }
+
+ try {
+ $path = $this->buildFilePath($filename);
+ $question = "There is already a file with this name. Do you want to replace it? [y/n]";
+
+ if (!$this->shouldReplaceFile($path, $question)) {
+ return self::SUCCESS;
+ }
+
+ $pathParts = $this->splitPath($filename);
+ $basePath = base_path();
+ $this->createFoldersIfNecessary($pathParts, $basePath);
+
+ $stub = $this->getStubContent($kind);
+ $className = $pathParts[count($pathParts) - 1];
+ $namespace = $this->buildNamespace($pathParts);
+
+ $stub = $this->replaceKindName($kind, $className, $stub);
+ $stub = $this->replaceNamespace($namespace, $stub);
+
+ $this->writeFile($path, $stub);
+ $this->displaySuccess(ucfirst($kind) . ' created successfully!', $path);
+ return self::SUCCESS;
+ } catch (\Exception $e) {
+ $this->error("Error: {$e->getMessage()}");
+ return self::FAILURE;
+ }
+ }
+
+ protected function buildFilePath(string $filename): string
+ {
+ return base_path($filename . '.php');
+ }
+
+ protected function splitPath(string $filename): array
+ {
+ return str_contains($filename, '/') ? explode('/', $filename) : explode('\\', $filename);
+ }
+
+ protected function buildNamespace(array $pathParts): string
+ {
+ $namespace = '';
+ for ($i = 0; $i < count($pathParts) - 1; $i++) {
+ $namespace .= ucfirst($pathParts[$i]) . '\\';
+ }
+ return Str::replaceLast('\\', '', $namespace);
+ }
+
+ protected function replaceKindName(string $kind, string $name, string $stub): string
+ {
+ return str_replace('Dummy' . ucfirst($kind), ucfirst($name), $stub);
+ }
+
+ protected function replaceNamespace(string $namespace, string $stub): string
+ {
+ if (!empty($namespace)) {
+ return str_replace('DummyNamespace', 'namespace ' . $namespace . ';', $stub);
+ }
+ return str_replace('DummyNamespace', '', $stub);
+ }
+
+ protected function getKind(): ?string
+ {
+ $kind = $this->option('kind');
+ if ($kind === null) {
+ return 'class';
+ }
+ $validKinds = ['class', 'trait', 'interface'];
+ if (!in_array($kind, $validKinds, true)) {
+ $this->error('Invalid kind value. The kind must be one of: class, trait, interface');
+ return null;
+ }
+ return $kind;
+ }
+}
diff --git a/src/Commands/Controller.php b/src/Commands/Controller.php
index b6cf112..7096165 100644
--- a/src/Commands/Controller.php
+++ b/src/Commands/Controller.php
@@ -61,10 +61,13 @@ protected function putInFile($filename, $content, $module = null)
{
if (!is_null($module)) {
$modulePath = base_path('Modules/'.$module.'/Http/Controllers');
- if (!is_dir($modulePath)) mkdir($modulePath);
+ if (!is_dir($modulePath)) {
+ mkdir($modulePath, 0755, true);
+ }
} else {
- if (!is_dir(app_path('/Http/Controllers')))
- mkdir(app_path('/Http/Controllers'));
+ if (!is_dir(app_path('/Http/Controllers'))) {
+ mkdir(app_path('/Http/Controllers'), 0755, true);
+ }
}
file_put_contents($filename, $content);
diff --git a/src/Commands/CrudMakeCommand.php b/src/Commands/CrudMakeCommand.php
new file mode 100644
index 0000000..99ddecf
--- /dev/null
+++ b/src/Commands/CrudMakeCommand.php
@@ -0,0 +1,277 @@
+ trim($parts[0]), 'type' => trim($parts[1])];
+ }
+ }
+ return $parsedFields;
+ }
+
+ public function handle(): int
+ {
+ $name = $this->argument('name');
+ $fields = $this->parseFields($this->option('fields'));
+ if (empty($name)) {
+ $this->error('The name of the CRUD resource is required.');
+ return self::FAILURE;
+ }
+ try {
+ $this->generateModel($name, $fields);
+ $this->generateRepository($name);
+ $this->generateService($name);
+ $this->generateController($name);
+ $this->generateViews($name, $fields);
+ $this->generateRoutes($name);
+ $this->generateTailwindConfig();
+ $this->info('✓ CRUD generated successfully!');
+ return self::SUCCESS;
+ } catch (\Exception $e) {
+ $this->error("Error: {$e->getMessage()}");
+ return self::FAILURE;
+ }
+ }
+
+ protected function generateModel(string $name, array $fields): void
+ {
+ $stub = $this->getStubContent('crud.model');
+ $stub = str_replace('DummyModel', ucfirst($name), $stub);
+ $stub = str_replace('DummyTable', Str::snake(Str::plural($name)), $stub);
+ $fillableString = empty($fields) ? "// 'field1', 'field2'" : implode(",\n ", array_map(fn($f) => "'{$f['name']}'", $fields));
+ $stub = str_replace('DummyFillable', $fillableString, $stub);
+ $path = app_path('Models/' . ucfirst($name) . '.php');
+ if (!$this->shouldReplaceFile($path, "Model {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Models'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateRepository(string $name): void
+ {
+ $modelName = ucfirst($name);
+ $stub = $this->getStubContent('repository');
+ $stub = str_replace('DummyModelNamespace', 'App', $stub);
+ $stub = str_replace('DummyModel', $modelName, $stub);
+ $stub = str_replace('DummyProperty', lcfirst(Str::camel($modelName)), $stub);
+ $stub = str_replace('class DummyClass', 'class ' . $modelName . 'Repository', $stub);
+ $path = app_path('Repositories/' . $modelName . 'Repository.php');
+ if (!$this->shouldReplaceFile($path, "Repository {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Repositories'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateService(string $name): void
+ {
+ $modelName = ucfirst($name);
+ $stub = $this->getStubContent('crud.service');
+ $stub = str_replace('DummyClass', $modelName . 'Service', $stub);
+ $stub = str_replace('DummyModel', $modelName, $stub);
+ $stub = str_replace('DummyRepository', $modelName . 'Repository', $stub);
+ $path = app_path('Services/' . $modelName . 'Service.php');
+ if (!$this->shouldReplaceFile($path, "Service {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Services'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateController(string $name): void
+ {
+ $modelName = ucfirst($name);
+ $variableName = lcfirst($name);
+ $pluralVariable = Str::plural($variableName);
+ $viewPath = Str::kebab(Str::plural($name));
+ $stub = $this->getStubContent('crud.controller');
+ $stub = str_replace(['DummyController','DummyService','DummyVariable','DummyPluralVariable','DummyViewPath'],
+ [$modelName.'Controller',$modelName.'Service',$variableName,$pluralVariable,$viewPath], $stub);
+ $path = app_path('Http/Controllers/' . $modelName . 'Controller.php');
+ if (!$this->shouldReplaceFile($path, "Controller {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Http/Controllers'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateViews(string $name, array $fields): void
+ {
+ $viewPath = Str::kebab(Str::plural($name));
+ $this->generateIndexView($viewPath, $name, $fields);
+ $this->generateCreateView($viewPath, $name, $fields);
+ $this->generateEditView($viewPath, $name, $fields);
+ $this->generateShowView($viewPath, $name, $fields);
+ $this->generateLayout();
+ }
+
+ protected function generateIndexView(string $viewPath, string $name, array $fields): void
+ {
+ $stub = $this->getStubContent('crud.views.index');
+ $stub = str_replace(['DummyResource','DummyResourceKebab','$items'], [Str::plural(ucfirst($name)), $viewPath, '$'.Str::plural(lcfirst($name))], $stub);
+ $path = resource_path('views/' . $viewPath . '/index.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Index view exists. Replace it? [y/n]')) return;
+ $this->ensureDirectoryExists(resource_path('views/' . $viewPath));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateCreateView(string $viewPath, string $name, array $fields): void
+ {
+ $stub = $this->getStubContent('crud.views.create');
+ $stub = str_replace(['DummyResource','DummyResourcePlural','DummyResourceKebab','DummyFormFields'], [ucfirst($name), Str::plural($name), $viewPath, $this->generateFormFields($fields)], $stub);
+ $path = resource_path('views/' . $viewPath . '/create.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Create view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateEditView(string $viewPath, string $name, array $fields): void
+ {
+ $stub = $this->getStubContent('crud.views.edit');
+ $stub = str_replace(['DummyResource','DummyResourcePlural','DummyResourceKebab','DummyVariable','DummyFormFields'], [ucfirst($name), Str::plural($name), $viewPath, lcfirst($name), $this->generateFormFields($fields, true, lcfirst($name))], $stub);
+ $path = resource_path('views/' . $viewPath . '/edit.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Edit view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateShowView(string $viewPath, string $name, array $fields): void
+ {
+ $stub = $this->getStubContent('crud.views.show');
+ $stub = str_replace(['DummyResource','DummyResourceKebab','DummyVariable','DummyDetailFields'], [ucfirst($name), $viewPath, lcfirst($name), $this->generateDetailFields($fields, lcfirst($name))], $stub);
+ $path = resource_path('views/' . $viewPath . '/show.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Show view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateRoutes(string $name): void
+ {
+ $routesPath = base_path('routes/web.php');
+ if (!file_exists($routesPath)) { $this->warn('⚠ routes/web.php not found. Routes not added.'); return; }
+ $resourceName = Str::plural(ucfirst($name));
+ $controllerName = ucfirst($name) . 'Controller';
+ $routePrefix = Str::kebab(Str::plural($name));
+ $stub = $this->getStubContent('crud.routes');
+ $stub = str_replace(['{{ $resourceName }}','{{ $controllerName }}','{{ $routePrefix }}'], [$resourceName,$controllerName,$routePrefix], $stub);
+ $routesContent = file_get_contents($routesPath);
+ if (str_contains($routesContent, $routePrefix)) { $this->line('⚠ Routes already exist in web.php. Skipping.'); return; }
+ $routesContent .= "\n" . $stub;
+ if (!$this->option('dry-run')) {
+ file_put_contents($routesPath, $routesContent);
+ $this->line('✓ Routes added to web.php');
+ } else {
+ $this->line('[DRY RUN] Would add routes to web.php');
+ }
+ }
+
+ protected function generateLayout(): void
+ {
+ $path = resource_path('views/layouts/app.blade.php');
+ if (file_exists($path)) return;
+ $stub = $this->getStubContent('crud.layout');
+ if (!$this->shouldReplaceFile($path, 'Layout exists. Replace it? [y/n]')) return;
+ $this->ensureDirectoryExists(resource_path('views/layouts'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateTailwindConfig(): void
+ {
+ $path = base_path('tailwind.config.js');
+ if (file_exists($path)) return;
+ $stub = $this->getStubContent('tailwind.config');
+ if (!$this->shouldReplaceFile($path, 'Tailwind config exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateFormFields(array $fields, bool $forEdit = false, string $variableName = 'item'): string
+ {
+ if (empty($fields)) { return ' {{-- Add your form fields here --}}'; }
+ $lines = [];
+ foreach ($fields as $field) {
+ $fieldName = $field['name'];
+ $fieldLabel = ucfirst(str_replace('_', ' ', $fieldName));
+ $valueBinding = $forEdit ? "{{ old('{$fieldName}', \\$${variableName}->{$fieldName}) }}" : "{{ old('{$fieldName}') }}";
+ $lines[] = $this->generateFieldByType($fieldName, $fieldLabel, $valueBinding, $field['type']);
+ }
+ return implode("\n", $lines);
+ }
+
+ protected function generateFieldByType(string $fieldName, string $fieldLabel, string $valueBinding, string $type): string
+ {
+ $id = "field_{$fieldName}";
+ if (in_array($type, ['text','string','varchar'])) {
+ return <<
+
+
+
+HTML;
+ } elseif (in_array($type, ['textarea'])) {
+ return <<
+
+
+
+HTML;
+ } elseif ($type === 'email') {
+ return <<
+
+
+
+HTML;
+ } elseif (in_array($type, ['boolean','tinyint'])) {
+ return <<
+
+
+HTML;
+ }
+ return <<
+
+
+
+HTML;
+ }
+
+ protected function generateDetailFields(array $fields, string $variableName = 'item'): string
+ {
+ if (empty($fields)) { return ' {{-- Add detail fields here --}}'; }
+ $detailFields = [];
+ foreach ($fields as $field) {
+ $fieldName = $field['name'];
+ $fieldLabel = ucfirst(str_replace('_', ' ', $fieldName));
+ $detailFields[] = <<
+
$fieldLabel:
+ {{\$$variableName->$fieldName}}
+
+HTML;
+ }
+ return implode("\n", $detailFields);
+ }
+}
diff --git a/src/Commands/Lang.php b/src/Commands/Lang.php
new file mode 100644
index 0000000..0d42d65
--- /dev/null
+++ b/src/Commands/Lang.php
@@ -0,0 +1,105 @@
+hasArgument('name') ? $this->argument('name') : '';
+ $locale = $this->option('locale') ?? 'en';
+
+ if ($this->option('json')) {
+ return $this->createJson($locale);
+ }
+
+ if (empty($name)) {
+ $this->error('No filename is given. Use --json flag for JSON language files.');
+ return self::FAILURE;
+ }
+
+ if (!$this->nameIsCorrect($name)) {
+ $this->error('The given filename is not correct. Only alphanumeric characters are allowed.');
+ return self::FAILURE;
+ }
+
+ return $this->createLang($name, $locale);
+ }
+
+ protected function createLang(string $name, string $locale): int
+ {
+ try {
+ $path = $this->getLangPath($locale, $name . '.php');
+ $question = "There is already a locale file with this name. Do you want to replace it? [y/n]";
+
+ if (!$this->shouldReplaceFile($path, $question)) {
+ return self::SUCCESS;
+ }
+
+ $this->ensureLocaleDirectoryExists($locale);
+
+ $stub = $this->getStubContent('lang');
+ $this->writeFile($path, $stub);
+
+ $this->displaySuccess('Language file created successfully!', $path);
+
+ return self::SUCCESS;
+ } catch (\Exception $e) {
+ $this->error("Error: {$e->getMessage()}");
+ return self::FAILURE;
+ }
+ }
+
+ protected function createJson(string $locale): int
+ {
+ try {
+ $path = $this->getLangPath($locale . '.json');
+ $question = "There is already a locale file with this name. Do you want to replace it? [y/n]";
+
+ if (!$this->shouldReplaceFile($path, $question)) {
+ return self::SUCCESS;
+ }
+
+ $content = "{\n \n}";
+ $this->writeFile($path, $content);
+
+ $this->displaySuccess('Language file created successfully!', $path);
+
+ return self::SUCCESS;
+ } catch (\Exception $e) {
+ $this->error("Error: {$e->getMessage()}");
+ return self::FAILURE;
+ }
+ }
+
+ protected function getLangPath(string ...$parts): string
+ {
+ return resource_path('lang' . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $parts));
+ }
+
+ protected function ensureLocaleDirectoryExists(string $locale): void
+ {
+ $directory = resource_path('lang' . DIRECTORY_SEPARATOR . $locale);
+
+ if (!file_exists($directory)) {
+ if (!mkdir($directory, 0755, true) && !is_dir($directory)) {
+ throw new \RuntimeException("Unable to create directory: {$directory}");
+ }
+ }
+ }
+
+ protected function nameIsCorrect(string $name): bool
+ {
+ return (bool) preg_match('#^[a-zA-Z][a-zA-Z0-9]+$#', $name);
+ }
+}
diff --git a/src/Commands/Repository.php b/src/Commands/Repository.php
index 246a95c..c80f5e7 100644
--- a/src/Commands/Repository.php
+++ b/src/Commands/Repository.php
@@ -5,7 +5,6 @@
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
-use function PHPUnit\Framework\isEmpty;
class Repository extends BaseCommand
{
@@ -61,13 +60,16 @@ protected function getEmptyStub()
*/
protected function putInFile($filename, $content, $module = null)
{
- if (!is_null($module)) {
- $modulePath = base_path('Modules/'.$module.'/Repositories');
- if (!is_dir($modulePath)) mkdir($modulePath);
- } else {
- if (!is_dir(app_path('/Repositories')))
- mkdir(app_path('/Repositories'));
- }
+ if (!is_null($module)) {
+ $modulePath = base_path('Modules/'.$module.'/Repositories');
+ if (!is_dir($modulePath)) {
+ mkdir($modulePath, 0755, true);
+ }
+ } else {
+ if (!is_dir(app_path('/Repositories'))) {
+ mkdir(app_path('/Repositories'), 0755, true);
+ }
+ }
file_put_contents($filename, $content);
}
diff --git a/src/Commands/Resource.php b/src/Commands/Resource.php
index dc7811f..9940379 100644
--- a/src/Commands/Resource.php
+++ b/src/Commands/Resource.php
@@ -2,9 +2,7 @@
namespace Kernel243\Artisan\Commands;
-use Illuminate\Console\Command;
-
-class Resource extends Command
+class Resource extends BaseCommand
{
/**
* The name and signature of the console command
@@ -75,10 +73,13 @@ protected function putInFile($filename, $content, $module = null)
{
if (!is_null($module)) {
$modulePath = base_path('Modules/'.ucfirst($module).'/Http/Resources');
- if (!is_dir($modulePath)) mkdir($modulePath);
+ if (!is_dir($modulePath)) {
+ mkdir($modulePath, 0755, true);
+ }
} else {
- if (!is_dir(app_path('/Http/Resources')))
- mkdir(app_path('/Http/Resources'));
+ if (!is_dir(app_path('/Http/Resources'))) {
+ mkdir(app_path('/Http/Resources'), 0755, true);
+ }
}
file_put_contents($filename, $content);
@@ -91,39 +92,15 @@ protected function putInFile($filename, $content, $module = null)
* @param $module
* @return bool
*/
- protected function resourceFileExists($resource, $module = null)
+ protected function resourceFileExists($resource, $module = null): bool
{
if (is_null($module)) {
- return file_exists(base_path(lcfirst($resource).'php')) || file_exists( base_path(lcfirst(str_replace('\\', '/', $resource)).'.php'));
+ $resourceName = ucfirst(basename(str_replace('\\', '/', $resource)));
+ return file_exists(app_path('Http/Resources/'.$resourceName.'.php'));
}
- $path = base_path('Modules/'.ucfirst($module).'/Http/Resources/'.lcfirst($resource).'.php');
- return file_exists($path) || file_exists('Modules/'.base_path(ucfirst($module).'/Entities/'.lcfirst(str_replace('\\', '/', $resource)).'.php'));
- }
-
- /**
- * Check if a module folder exists
- *
- * @param $module
- * @return false|mixed
- */
- protected function moduleExists($module)
- {
- return $this->folderExist('Modules/'.$module);
- }
-
- /**
- * Checks if a folder exist and return canonicalized absolute pathname (sort version)
- * @param string $folder the path being checked.
- * @return mixed returns the canonicalized absolute pathname on success otherwise FALSE is returned
- */
- protected function folderExist($folder)
- {
- // Get canonicalized absolute pathname
- $path = realpath($folder);
-
- // If it exists, check if it's a directory
- return ($path !== false AND is_dir($path)) ? $path : false;
+ $path = base_path('Modules/'.ucfirst($module).'/Http/Resources/'.ucfirst(basename(str_replace('\\', '/', $resource))).'.php');
+ return file_exists($path);
}
/**
diff --git a/src/Commands/ResourceMakeCommand.php b/src/Commands/ResourceMakeCommand.php
new file mode 100644
index 0000000..7f5653c
--- /dev/null
+++ b/src/Commands/ResourceMakeCommand.php
@@ -0,0 +1,295 @@
+trim($parts[0]), 'type'=>trim($parts[1])]; }
+ }
+ return $parsed;
+ }
+
+ public function handle(): int
+ {
+ $name = $this->argument('name');
+ $fields = $this->parseFields($this->option('fields'));
+ if (empty($name)) { $this->error('The name of the CRUD resource is required.'); return self::FAILURE; }
+ try {
+ $this->generateBaseClasses();
+ $this->generateModel($name, $fields);
+ $this->generateRepository($name);
+ $this->generateService($name);
+ $this->generateController($name);
+ $this->generateResource($name, $fields);
+ $this->generateDefaultViews($name, $fields);
+ $this->generateRoutes($name);
+ $this->generateTailwindConfig();
+ $this->info('✓ Resource CRUD generated successfully!');
+ return self::SUCCESS;
+ } catch (\Exception $e) {
+ $this->error("Error: {$e->getMessage()}");
+ return self::FAILURE;
+ }
+ }
+
+ protected function generateBaseClasses(): void
+ {
+ $this->generateBaseResource();
+ $this->generateFormBuilder();
+ $this->generateTableBuilder();
+ }
+
+ protected function generateBaseResource(): void
+ {
+ $path = app_path('Resources/Resource.php');
+ if (file_exists($path)) return;
+ $stub = $this->getStubContent('resource.base');
+ if (!$this->shouldReplaceFile($path, 'Base Resource exists. Replace it? [y/n]')) return;
+ $this->ensureDirectoryExists(app_path('Resources'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateFormBuilder(): void
+ {
+ $path = app_path('Builders/Form.php');
+ if (file_exists($path)) return;
+ $stub = $this->getStubContent('form.builder');
+ if (!$this->shouldReplaceFile($path, 'Form builder exists. Replace it? [y/n]')) return;
+ $this->ensureDirectoryExists(app_path('Builders'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateTableBuilder(): void
+ {
+ $path = app_path('Builders/Table.php');
+ if (file_exists($path)) return;
+ $stub = $this->getStubContent('table.builder');
+ if (!$this->shouldReplaceFile($path, 'Table builder exists. Replace it? [y/n]')) return;
+ $this->ensureDirectoryExists(app_path('Builders'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateModel(string $name, array $fields): void
+ {
+ $stub = $this->getStubContent('crud.model');
+ $stub = str_replace('DummyModel', ucfirst($name), $stub);
+ $stub = str_replace('DummyTable', Str::snake(Str::plural($name)), $stub);
+ $fillableString = empty($fields) ? "// 'field1', 'field2'" : implode(",\n ", array_map(fn($f)=>"'{$f['name']}'", $fields));
+ $stub = str_replace('DummyFillable', $fillableString, $stub);
+ $path = app_path('Models/' . ucfirst($name) . '.php');
+ if (!$this->shouldReplaceFile($path, "Model {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Models'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateRepository(string $name): void
+ {
+ $modelName = ucfirst($name);
+ $stub = $this->getStubContent('repository');
+ $stub = str_replace('DummyModelNamespace', 'App', $stub);
+ $stub = str_replace('DummyModel', $modelName, $stub);
+ $stub = str_replace('DummyProperty', lcfirst(Str::camel($modelName)), $stub);
+ $stub = str_replace('class DummyClass', 'class ' . $modelName . 'Repository', $stub);
+ $path = app_path('Repositories/' . $modelName . 'Repository.php');
+ if (!$this->shouldReplaceFile($path, "Repository {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Repositories'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateService(string $name): void
+ {
+ $modelName = ucfirst($name);
+ $stub = $this->getStubContent('crud.service');
+ $stub = str_replace('DummyClass', $modelName . 'Service', $stub);
+ $stub = str_replace('DummyModel', $modelName, $stub);
+ $stub = str_replace('DummyRepository', $modelName . 'Repository', $stub);
+ $path = app_path('Services/' . $modelName . 'Service.php');
+ if (!$this->shouldReplaceFile($path, "Service {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Services'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateController(string $name): void
+ {
+ $modelName = ucfirst($name);
+ $variableName = lcfirst($name);
+ $pluralVariable = Str::plural($variableName);
+ $viewPath = 'resources.crud';
+ $stub = $this->getStubContent('crud.controller.resource');
+ $stub = str_replace(['DummyController','DummyService','DummyVariable','DummyPluralVariable','DummyViewPath','DummyResourceClass'],
+ [$modelName.'Controller',$modelName.'Service',$variableName,$pluralVariable,$viewPath,$modelName.'Resource'], $stub);
+ $path = app_path('Http/Controllers/' . $modelName . 'Controller.php');
+ if (!$this->shouldReplaceFile($path, "Controller {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Http/Controllers'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateResource(string $name, array $fields): void
+ {
+ $modelName = ucfirst($name);
+ $resourceName = Str::plural($modelName);
+ $routePrefix = Str::kebab(Str::plural($name));
+ $stub = $this->getStubContent('crud.resource');
+ $stub = str_replace(['DummyResourceName','DummyResourceClass','DummyModel','DummyResourcePlural','DummyResourceKebab'],
+ [$resourceName,$modelName.'Resource',$modelName,Str::plural($modelName),$routePrefix], $stub);
+ $formFields = $this->generateResourceFormFields($fields);
+ $stub = str_replace('DummyFormFields', $formFields, $stub);
+ $tableColumns = $this->generateResourceTableColumns($fields);
+ $stub = str_replace('DummyTableColumns', $tableColumns, $stub);
+ $validationRules = $this->generateValidationRules($fields);
+ $stub = str_replace('DummyValidationRules', $validationRules, $stub);
+ $path = app_path('Resources/' . $modelName . 'Resource.php');
+ if (!$this->shouldReplaceFile($path, "Resource {$name} exists. Replace it? [y/n]")) return;
+ $this->ensureDirectoryExists(app_path('Resources'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateDefaultViews(string $name, array $fields): void
+ {
+ $viewPath = 'resources.crud';
+ $resourceName = Str::plural(ucfirst($name));
+ $routePrefix = Str::kebab(Str::plural($name));
+ $this->ensureDirectoryExists(resource_path('views/resources/crud'));
+ $this->generateDefaultFormView($name, $fields, $viewPath);
+ $this->generateDefaultIndexView($name, $fields, $viewPath, $resourceName, $routePrefix);
+ $this->generateDefaultCreateView($name, $viewPath, $resourceName, $routePrefix);
+ $this->generateDefaultEditView($name, $viewPath, $resourceName, $routePrefix);
+ $this->generateDefaultShowView($name, $fields, $viewPath, $resourceName, $routePrefix);
+ $this->generateLayout();
+ }
+
+ protected function generateDefaultFormView(string $name, array $fields, string $viewPath): void
+ {
+ $path = resource_path('views/' . $viewPath . '/_form.blade.php');
+ if (!$this->shouldReplaceFile($path, '_form view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $this->getStubContent('crud.views.default._form'));
+ }
+
+ protected function generateDefaultIndexView(string $name, array $fields, string $viewPath, string $resourceName, string $routePrefix): void
+ {
+ $stub = $this->getStubContent('crud.views.default.index');
+ $stub = str_replace(['{{ $resourceName }}','{{ $routePrefix }}'], [$resourceName,$routePrefix], $stub);
+ $path = resource_path('views/' . $viewPath . '/index.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Index view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateDefaultCreateView(string $name, string $viewPath, string $resourceName, string $routePrefix): void
+ {
+ $stub = $this->getStubContent('crud.views.default.create');
+ $stub = str_replace(['{{ $resourceName }}','{{ $routePrefix }}'], ['New ' . ucfirst($name), $routePrefix], $stub);
+ $path = resource_path('views/' . $viewPath . '/create.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Create view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateDefaultEditView(string $name, string $viewPath, string $resourceName, string $routePrefix): void
+ {
+ $stub = $this->getStubContent('crud.views.default.edit');
+ $stub = str_replace(['{{ $resourceName }}','{{ $routePrefix }}'], [ucfirst($name), $routePrefix], $stub);
+ $path = resource_path('views/' . $viewPath . '/edit.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Edit view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateDefaultShowView(string $name, array $fields, string $viewPath, string $resourceName, string $routePrefix): void
+ {
+ $stub = $this->getStubContent('crud.views.default.show');
+ $stub = str_replace(['{{ $resourceName }}','{{ $routePrefix }}'], [ucfirst($name), $routePrefix], $stub);
+ $path = resource_path('views/' . $viewPath . '/show.blade.php');
+ if (!$this->shouldReplaceFile($path, 'Show view exists. Replace it? [y/n]')) return;
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateRoutes(string $name): void
+ {
+ $routesPath = base_path('routes/web.php');
+ if (!file_exists($routesPath)) { $this->warn('⚠ routes/web.php not found. Routes not added.'); return; }
+ $resourceName = Str::plural(ucfirst($name));
+ $controllerName = ucfirst($name) . 'Controller';
+ $routePrefix = Str::kebab(Str::plural($name));
+ $stub = $this->getStubContent('crud.routes');
+ $stub = str_replace(['{{ $resourceName }}','{{ $controllerName }}','{{ $routePrefix }}'], [$resourceName,$controllerName,$routePrefix], $stub);
+ $routesContent = file_get_contents($routesPath);
+ if (str_contains($routesContent, $routePrefix)) { $this->line('⚠ Routes already exist in web.php. Skipping.'); return; }
+ $routesContent .= "\n" . $stub;
+ if (!$this->option('dry-run')) {
+ file_put_contents($routesPath, $routesContent);
+ $this->line('✓ Routes added to web.php');
+ } else {
+ $this->line('[DRY RUN] Would add routes to web.php');
+ }
+ }
+
+ protected function generateLayout(): void
+ {
+ $path = resource_path('views/layouts/app.blade.php');
+ if (file_exists($path)) return;
+ $stub = $this->getStubContent('crud.layout');
+ if (!$this->shouldReplaceFile($path, 'Layout exists. Replace it? [y/n]')) return;
+ $this->ensureDirectoryExists(resource_path('views/layouts'));
+ $this->writeFile($path, $stub);
+ }
+
+ protected function generateResourceFormFields(array $fields): string
+ {
+ if (empty($fields)) { return " // Form::text('name', 'Name'),"; }
+ $lines = [];
+ foreach ($fields as $field) {
+ $fieldName = $field['name'];
+ $fieldLabel = ucfirst(str_replace('_', ' ', $fieldName));
+ $type = $this->getFormFieldType($field['type']);
+ $lines[] = " Form::{$type}('{$fieldName}', '{$fieldLabel}'),";
+ }
+ return implode("\n", $lines);
+ }
+
+ protected function generateResourceTableColumns(array $fields): string
+ {
+ if (empty($fields)) { return " // Table::text('id', 'ID'),"; }
+ $columns = [" Table::text('id', 'ID'),"];
+ foreach ($fields as $field) {
+ $fieldName = $field['name'];
+ $fieldLabel = ucfirst(str_replace('_', ' ', $fieldName));
+ $type = $this->getTableColumnType($field['type']);
+ $columns[] = " Table::{$type}('{$fieldName}', '{$fieldLabel}'),";
+ }
+ $columns[] = " Table::actions(),";
+ return implode("\n", $columns);
+ }
+
+ protected function generateValidationRules(array $fields): string
+ {
+ if (empty($fields)) { return " // 'field' => 'required',"; }
+ return implode("\n", array_map(fn($f)=>" '{$f['name']}' => 'required',", $fields));
+ }
+
+ protected function getFormFieldType(string $type): string
+ {
+ $map = ['string'=>'text','text'=>'textarea','textarea'=>'textarea','email'=>'email','boolean'=>'checkbox','tinyint'=>'checkbox'];
+ return $map[strtolower($type)] ?? 'text';
+ }
+
+ protected function getTableColumnType(string $type): string
+ {
+ $map = ['decimal'=>'number','integer'=>'number','bigint'=>'number','boolean'=>'boolean','tinyint'=>'boolean','datetime'=>'datetime','date'=>'datetime','timestamp'=>'datetime'];
+ return $map[strtolower($type)] ?? 'text';
+ }
+}
diff --git a/src/Commands/Service.php b/src/Commands/Service.php
index a4d2062..ce9c579 100644
--- a/src/Commands/Service.php
+++ b/src/Commands/Service.php
@@ -2,9 +2,7 @@
namespace Kernel243\Artisan\Commands;
-use Illuminate\Console\Command;
-
-class Service extends Command
+class Service extends BaseCommand
{
/**
* The name and signature of the console command.
@@ -62,13 +60,15 @@ protected function replaceClassName($name, $stub)
*/
protected function putInFile($filename, $content, $module = null)
{
- $this->info($module);
if (!is_null($module)) {
$modulePath = base_path('Modules/'.$module.'/Services');
- if (!is_dir($modulePath)) mkdir($modulePath);
+ if (!is_dir($modulePath)) {
+ mkdir($modulePath, 0755, true);
+ }
} else {
- if (!is_dir(app_path('/Services')))
- mkdir(app_path('/Services'));
+ if (!is_dir(app_path('/Services'))) {
+ mkdir(app_path('/Services'), 0755, true);
+ }
}
file_put_contents($filename, $content);
@@ -86,39 +86,15 @@ protected function replaceNamespace($namespace, $stub)
return str_replace('DummyNamespace', ucfirst($namespace), $stub);
}
- protected function serviceFileExist($service, $module = null)
+ protected function serviceFileExist($service, $module = null): bool
{
if (is_null($module)) {
- return file_exists(base_path(lcfirst($service).'php')) || file_exists( base_path(lcfirst(str_replace('\\', '/', $service)).'.php'));
+ $serviceName = ucfirst(basename(str_replace('\\', '/', $service)));
+ return file_exists(app_path('Services/'.$serviceName.'.php'));
}
- $path = base_path('Modules/'.ucfirst($module).'/Services/'.lcfirst($service).'.php');
- return file_exists($path) || file_exists('Modules/'.base_path(ucfirst($module).'/Services/'.lcfirst(str_replace('\\', '/', $service)).'.php'));
- }
-
- /**
- * Check if a module folder exists
- *
- * @param $module
- * @return false|mixed
- */
- protected function moduleExists($module)
- {
- return $this->folderExist('Modules/'.$module);
- }
-
- /**
- * Checks if a folder exist and return canonicalized absolute pathname (sort version)
- * @param string $folder the path being checked.
- * @return mixed returns the canonicalized absolute pathname on success otherwise FALSE is returned
- */
- protected function folderExist($folder)
- {
- // Get canonicalized absolute pathname
- $path = realpath($folder);
-
- // If it exists, check if it's a directory
- return ($path !== false AND is_dir($path)) ? $path : false;
+ $path = base_path('Modules/'.ucfirst($module).'/Services/'.ucfirst(basename(str_replace('\\', '/', $service))).'.php');
+ return file_exists($path);
}
/**
diff --git a/src/Commands/stubs/crud.controller.resource.stub b/src/Commands/stubs/crud.controller.resource.stub
new file mode 100644
index 0000000..152e234
--- /dev/null
+++ b/src/Commands/stubs/crud.controller.resource.stub
@@ -0,0 +1,81 @@
+service = $service;
+ $this->resource = $resource;
+ }
+
+ public function index(): View
+ {
+ $DummyPluralVariable = $this->service->getPaginated();
+ $tableBuilder = app(\App\Builders\Table::class);
+ $tableColumns = $this->resource->table($tableBuilder)->getColumns();
+ return view('DummyViewPath.index', compact('DummyPluralVariable', 'tableColumns'))
+ ->with(['resourceName' => $this->resource->getName(), 'routePrefix' => $this->resource->getRoutePrefix()]);
+ }
+
+ public function create(): View
+ {
+ $formBuilder = app(\App\Builders\Form::class);
+ $formFields = $this->resource->form($formBuilder)->getFields();
+ return view('DummyViewPath.create')
+ ->with(['resourceName' => $this->resource->getName(), 'routePrefix' => $this->resource->getRoutePrefix(), 'formFields' => $formFields]);
+ }
+
+ public function store(Request $request): RedirectResponse
+ {
+ $validated = $request->validate($this->resource->rules());
+ $this->service->create($validated);
+ return redirect()->route('DummyViewPath.index')->with('success', 'Item created successfully.');
+ }
+
+ public function show(int $id): View
+ {
+ $DummyVariable = $this->service->getById($id);
+ $formBuilder = app(\App\Builders\Form::class);
+ $formFields = $this->resource->form($formBuilder)->getFields();
+ $detailFields = array_map(function ($field) {
+ return ['name' => $field['name'], 'label' => $field['label'], 'type' => $field['type'] ?? 'text'];
+ }, $formFields);
+ return view('DummyViewPath.show', compact('DummyVariable', 'detailFields'))
+ ->with(['resourceName' => $this->resource->getName(), 'routePrefix' => $this->resource->getRoutePrefix()]);
+ }
+
+ public function edit(int $id): View
+ {
+ $DummyVariable = $this->service->getById($id);
+ $formBuilder = app(\App\Builders\Form::class);
+ $formFields = $this->resource->form($formBuilder)->getFields();
+ return view('DummyViewPath.edit', compact('DummyVariable', 'formFields'))
+ ->with(['resourceName' => $this->resource->getName(), 'routePrefix' => $this->resource->getRoutePrefix()]);
+ }
+
+ public function update(Request $request, int $id): RedirectResponse
+ {
+ $validated = $request->validate($this->resource->rules());
+ $this->service->update($id, $validated);
+ return redirect()->route('DummyViewPath.index')->with('success', 'Item updated successfully.');
+ }
+
+ public function destroy(int $id): RedirectResponse
+ {
+ $this->service->delete($id);
+ return redirect()->route('DummyViewPath.index')->with('success', 'Item deleted successfully.');
+ }
+}
+
+
diff --git a/src/Commands/stubs/crud.controller.stub b/src/Commands/stubs/crud.controller.stub
new file mode 100644
index 0000000..85271b6
--- /dev/null
+++ b/src/Commands/stubs/crud.controller.stub
@@ -0,0 +1,67 @@
+service = $service;
+ }
+
+ public function index(): View
+ {
+ $DummyPluralVariable = $this->service->getPaginated();
+ return view('DummyViewPath.index', compact('DummyPluralVariable'));
+ }
+
+ public function create(): View
+ {
+ return view('DummyViewPath.create');
+ }
+
+ public function store(Request $request): RedirectResponse
+ {
+ $validated = $request->validate([
+ // Add your validation rules here
+ ]);
+ $this->service->create($validated);
+ return redirect()->route('DummyViewPath.index')->with('success', 'Item created successfully.');
+ }
+
+ public function show(int $id): View
+ {
+ $DummyVariable = $this->service->getById($id);
+ return view('DummyViewPath.show', compact('DummyVariable'));
+ }
+
+ public function edit(int $id): View
+ {
+ $DummyVariable = $this->service->getById($id);
+ return view('DummyViewPath.edit', compact('DummyVariable'));
+ }
+
+ public function update(Request $request, int $id): RedirectResponse
+ {
+ $validated = $request->validate([
+ // Add your validation rules here
+ ]);
+ $this->service->update($id, $validated);
+ return redirect()->route('DummyViewPath.index')->with('success', 'Item updated successfully.');
+ }
+
+ public function destroy(int $id): RedirectResponse
+ {
+ $this->service->delete($id);
+ return redirect()->route('DummyViewPath.index')->with('success', 'Item deleted successfully.');
+ }
+}
+
+
diff --git a/src/Commands/stubs/crud.layout.stub b/src/Commands/stubs/crud.layout.stub
new file mode 100644
index 0000000..139024e
--- /dev/null
+++ b/src/Commands/stubs/crud.layout.stub
@@ -0,0 +1,21 @@
+
+
+
+
+
+ @yield('title', 'App')
+
+
+
+
+
+ @yield('content')
+
+
+
+
+
diff --git a/src/Commands/stubs/crud.model.stub b/src/Commands/stubs/crud.model.stub
new file mode 100644
index 0000000..3f178a4
--- /dev/null
+++ b/src/Commands/stubs/crud.model.stub
@@ -0,0 +1,41 @@
+
+ */
+ protected $fillable = [
+ DummyFillable
+ ];
+
+ /**
+ * Get the attributes that should be cast.
+ *
+ * @return array
+ */
+ protected function casts(): array
+ {
+ return [
+ // Add your casts here
+ ];
+ }
+}
+
+
diff --git a/src/Commands/stubs/crud.routes.stub b/src/Commands/stubs/crud.routes.stub
new file mode 100644
index 0000000..8b3c08b
--- /dev/null
+++ b/src/Commands/stubs/crud.routes.stub
@@ -0,0 +1,6 @@
+// {{ $resourceName }} Routes (Generated by make:crud)
+Route::resource('{{ $routePrefix }}', App\Http\Controllers\{{ $controllerName }}::class);
+// DataTables route for index page
+Route::get('{{ $routePrefix }}/datatable', [App\Http\Controllers\{{ $controllerName }}::class, 'datatable'])->name('{{ $routePrefix }}.datatable');
+
+
diff --git a/src/Commands/stubs/crud.views.create.stub b/src/Commands/stubs/crud.views.create.stub
new file mode 100644
index 0000000..00ed51c
--- /dev/null
+++ b/src/Commands/stubs/crud.views.create.stub
@@ -0,0 +1,30 @@
+@extends('layouts.app')
+
+@section('title', 'Create DummyResource')
+
+@section('content')
+
+
+
Create DummyResource
+ @if($errors->any())
+
+
+ @foreach($errors->all() as $error)
+ - {{ $error }}
+ @endforeach
+
+
+ @endif
+
+
+
+@endsection
+
+
diff --git a/src/Commands/stubs/crud.views.default._form.stub b/src/Commands/stubs/crud.views.default._form.stub
new file mode 100644
index 0000000..87140c8
--- /dev/null
+++ b/src/Commands/stubs/crud.views.default._form.stub
@@ -0,0 +1,10 @@
+@csrf
+
+ {{-- Example field --}}
+
+
+
+
+
+
+
diff --git a/src/Commands/stubs/crud.views.default.index.stub b/src/Commands/stubs/crud.views.default.index.stub
new file mode 100644
index 0000000..ad50d1b
--- /dev/null
+++ b/src/Commands/stubs/crud.views.default.index.stub
@@ -0,0 +1,36 @@
+@extends('layouts.app')
+
+@section('title', '{{ $resourceName }}')
+
+@section('content')
+
+
+
+ @if(session('success'))
+
+ {{ session('success') }}
+
+ @endif
+
+
+
+
+
+ | ID |
+
+
+
+ {{-- Your rows here --}}
+
+
+
+
+@endsection
+
+