From 0188fb4d3552e827502741423bff93846babbdbf Mon Sep 17 00:00:00 2001 From: Wahyu Kristianto Date: Sun, 21 Sep 2025 21:18:27 +0700 Subject: [PATCH 1/4] feat: add comprehensive testing architecture and helper management - Add HelperManager service for centralized helper file management - Add HelperListCommand for listing and managing helper files - Add comprehensive test suite with Pest PHP - Add unit tests for commands and services - Add feature tests for artisan command integration - Add TestbenchTestCase for consistent test setup - Support subdirectory creation and CamelCase conversion - Add auto-prefix 'Helper' suffix functionality --- src/Console/Commands/HelperListCommand.php | 161 ++++++++++++++++++++ src/Services/HelperManager.php | 163 +++++++++++++++++++++ tests/Feature/ArtisanCommandTest.php | 62 ++++++++ tests/TestbenchTestCase.php | 39 +++++ tests/Unit/HelperListCommandTest.php | 28 ++++ tests/Unit/HelperMakeCommandTest.php | 36 +++++ 6 files changed, 489 insertions(+) create mode 100644 src/Console/Commands/HelperListCommand.php create mode 100644 src/Services/HelperManager.php create mode 100644 tests/Feature/ArtisanCommandTest.php create mode 100644 tests/TestbenchTestCase.php create mode 100644 tests/Unit/HelperListCommandTest.php create mode 100644 tests/Unit/HelperMakeCommandTest.php diff --git a/src/Console/Commands/HelperListCommand.php b/src/Console/Commands/HelperListCommand.php new file mode 100644 index 0000000..bc3f49c --- /dev/null +++ b/src/Console/Commands/HelperListCommand.php @@ -0,0 +1,161 @@ +make(HelperManager::class); + $directory = app_path(config('helpers.directory', 'Helpers')); + + if (! File::exists($directory)) { + $this->warn("Helper directory does not exist: {$directory}"); + $this->info('Run "php artisan make:helper example" to create your first helper file.'); + + return 0; + } + + $files = $this->getHelperFiles($directory); + + if (empty($files)) { + $this->warn('No helper files found.'); + $this->info('Run "php artisan make:helper example" to create your first helper file.'); + + return 0; + } + + $this->info('Found '.count($files).' helper file(s):'); + $this->newLine(); + + $headers = ['File', 'Status']; + $rows = []; + + foreach ($files as $file) { + // Get relative path from helpers directory + $helperDir = app_path(config('helpers.directory', 'Helpers')); + $relativePath = str_replace($helperDir.'/', '', $file); + $filename = $relativePath; + + $status = $helperManager->isLoaded($file) ? 'Loaded' : 'Not Loaded'; + + if ($this->option('loaded') && $status !== 'Loaded') { + continue; + } + + $rows[] = [$filename, $status]; + } + + // Sort rows by filename + usort($rows, function ($a, $b) { + return strcmp($a[0], $b[0]); + }); + + $this->table($headers, $rows); + + if ($this->option('details')) { + $this->showDetails($files, $helperManager); + } + + return 0; + } + + /** + * Get all helper files from the directory (including subdirectories). + * + * @param string $directory + * @return array + */ + protected function getHelperFiles($directory) + { + $files = []; + + if (! File::exists($directory)) { + return $files; + } + + // Use recursive directory iterator for better subdirectory support + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS) + ); + + foreach ($iterator as $file) { + if ($file->isFile() && $file->getExtension() === 'php') { + $files[] = $file->getPathname(); + } + } + + return $files; + } + + /** + * Show detailed information about helper files. + * + * @param array $files + * @param HelperManager $helperManager + * @return void + */ + protected function showDetails($files, $helperManager) + { + $this->newLine(); + $this->info('Detailed Information:'); + $this->newLine(); + + foreach ($files as $file) { + // Get relative path from helpers directory + $helperDir = app_path(config('helpers.directory', 'Helpers')); + $relativePath = str_replace($helperDir.'/', '', $file); + + $this->line("{$relativePath}"); + $this->line(' Status: '.($helperManager->isLoaded($file) ? 'Loaded' : 'Not Loaded')); + + // Try to extract function names from the file + $content = File::get($file); + $functions = $this->extractFunctionNames($content); + + if (! empty($functions)) { + $this->line(' Functions: '.implode(', ', $functions)); + } + + $this->newLine(); + } + } + + /** + * Extract function names from file content. + * + * @param string $content + * @return array + */ + protected function extractFunctionNames($content) + { + preg_match_all('/function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/', $content, $matches); + + return $matches[1] ?? []; + } +} diff --git a/src/Services/HelperManager.php b/src/Services/HelperManager.php new file mode 100644 index 0000000..561b9c0 --- /dev/null +++ b/src/Services/HelperManager.php @@ -0,0 +1,163 @@ +app = $app; + } + + /** + * Load all helper files from the configured directory. + * + * @return void + */ + public function loadHelpers() + { + $directory = $this->getHelperDirectory(); + + if (! File::exists($directory)) { + $this->createHelperDirectory($directory); + + return; + } + + $files = $this->getHelperFiles($directory); + + foreach ($files as $file) { + $this->loadHelperFile($file); + } + } + + /** + * Get the helper directory path. + * + * @return string + */ + protected function getHelperDirectory() + { + $directory = config('helpers.directory', 'Helpers'); + + return app_path($directory); + } + + /** + * Create the helper directory if it doesn't exist. + * + * @param string $directory + * @return void + */ + protected function createHelperDirectory($directory) + { + File::makeDirectory($directory, 0755, true); + } + + /** + * Get all PHP files from the helper directory (including subdirectories). + * + * @param string $directory + * @return array + */ + protected function getHelperFiles($directory) + { + $files = []; + + if (! File::exists($directory)) { + return $files; + } + + // Use recursive directory iterator for better subdirectory support + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS) + ); + + foreach ($iterator as $file) { + if ($file->isFile() && $file->getExtension() === 'php') { + $files[] = $file->getPathname(); + } + } + + return $files; + } + + /** + * Load a single helper file. + * + * @param string $file + * @return void + */ + protected function loadHelperFile($file) + { + if (in_array($file, $this->loadedFiles)) { + return; + } + + try { + require_once $file; + $this->loadedFiles[] = $file; + } catch (\ParseError $e) { + // Skip files with parse errors + return; + } catch (\Error $e) { + // Skip files with fatal errors + return; + } catch (\Exception $e) { + // Skip files with exceptions + return; + } + } + + /** + * Get all loaded helper files. + * + * @return array + */ + public function getLoadedFiles() + { + return $this->loadedFiles; + } + + /** + * Check if a helper file is loaded. + * + * @param string $file + * @return bool + */ + public function isLoaded($file) + { + return in_array($file, $this->loadedFiles); + } + + /** + * Reload all helper files. + * + * @return void + */ + public function reload() + { + $this->loadedFiles = []; + $this->loadHelpers(); + } +} diff --git a/tests/Feature/ArtisanCommandTest.php b/tests/Feature/ArtisanCommandTest.php new file mode 100644 index 0000000..7809fad --- /dev/null +++ b/tests/Feature/ArtisanCommandTest.php @@ -0,0 +1,62 @@ +toBeTrue(); + expect(file_exists(__DIR__.'/../../src/Services/HelperManager.php'))->toBeTrue(); + expect(file_exists(__DIR__.'/../../src/Console/Commands/HelperMakeCommand.php'))->toBeTrue(); + expect(file_exists(__DIR__.'/../../src/Console/Commands/HelperListCommand.php'))->toBeTrue(); + expect(file_exists(__DIR__.'/../../config/helpers.php'))->toBeTrue(); + expect(file_exists(__DIR__.'/../../src/Console/Commands/stubs/helper.stub'))->toBeTrue(); + + // Check config file is valid + $config = include __DIR__.'/../../config/helpers.php'; + expect($config)->toBeArray(); + expect($config)->toHaveKey('directory'); + + // Check stub file has required placeholders + $stubContent = file_get_contents(__DIR__.'/../../src/Console/Commands/stubs/helper.stub'); + expect($stubContent)->toContain('{{functionName}}'); + expect($stubContent)->toContain('{{description}}'); + expect($stubContent)->toContain('{{author}}'); + expect($stubContent)->toContain('{{date}}'); +}); + +it('creates actual helper file using make:helper command', function () { + $dir = app_path('Helpers'); + $path = $dir.'/TestHelper.php'; + + if (! file_exists($dir)) { + mkdir($dir, 0755, true); + } + + if (file_exists($path)) { + unlink($path); + } + + $this->artisan('make:helper', [ + 'name' => 'TestHelper', + '--force' => true, + ])->run(); + + expect(file_exists($path))->toBeTrue(); + + $content = file_get_contents($path); + expect($content)->toContain('function testHelper('); + + unlink($path); +}); + +it('can instantiate HelperManager with required methods', function () { + $manager = new HelperManager($this->app); + + expect($manager)->toBeInstanceOf(HelperManager::class); + expect(method_exists($manager, 'loadHelpers'))->toBeTrue(); + expect(method_exists($manager, 'isLoaded'))->toBeTrue(); + expect(method_exists($manager, 'getLoadedFiles'))->toBeTrue(); +}); diff --git a/tests/TestbenchTestCase.php b/tests/TestbenchTestCase.php new file mode 100644 index 0000000..0fa25ac --- /dev/null +++ b/tests/TestbenchTestCase.php @@ -0,0 +1,39 @@ +app); + } + + protected function getPackageProviders($app): array + { + return [ + \Devtical\Helpers\HelperServiceProvider::class, + ]; + } + + protected function getEnvironmentSetUp($app): void + { + // Setup default database to use sqlite :memory: + $app['config']->set('database.default', 'testing'); + $app['config']->set('database.connections.testing', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + + // Setup helpers config + $app['config']->set('helpers', [ + 'directory' => 'Helpers', + ]); + } +} diff --git a/tests/Unit/HelperListCommandTest.php b/tests/Unit/HelperListCommandTest.php new file mode 100644 index 0000000..2635597 --- /dev/null +++ b/tests/Unit/HelperListCommandTest.php @@ -0,0 +1,28 @@ +toBeInstanceOf(HelperListCommand::class); +}); + +it('has correct command name', function () { + $command = new HelperListCommand; + + expect($command->getName())->toBe('helper:list'); +}); + +it('has correct command description', function () { + $command = new HelperListCommand; + + expect($command->getDescription())->not->toBeEmpty(); +}); + +it('has correct command options', function () { + $command = new HelperListCommand; + + expect($command->getDefinition()->hasOption('loaded'))->toBeTrue(); + expect($command->getDefinition()->hasOption('details'))->toBeTrue(); +}); diff --git a/tests/Unit/HelperMakeCommandTest.php b/tests/Unit/HelperMakeCommandTest.php new file mode 100644 index 0000000..5d28d73 --- /dev/null +++ b/tests/Unit/HelperMakeCommandTest.php @@ -0,0 +1,36 @@ +toBeInstanceOf(HelperMakeCommand::class); +}); + +it('has correct command name', function () { + $command = new HelperMakeCommand(new Filesystem); + + expect($command->getName())->toBe('make:helper'); +}); + +it('has correct command description', function () { + $command = new HelperMakeCommand(new Filesystem); + + expect($command->getDescription())->not->toBeEmpty(); +}); + +it('has correct command options', function () { + $command = new HelperMakeCommand(new Filesystem); + + expect($command->getDefinition()->hasOption('force'))->toBeTrue(); + expect($command->getDefinition()->hasOption('description'))->toBeTrue(); + expect($command->getDefinition()->hasOption('author'))->toBeTrue(); +}); + +it('has correct command arguments', function () { + $command = new HelperMakeCommand(new Filesystem); + + expect($command->getDefinition()->hasArgument('name'))->toBeTrue(); +}); From 4954e06ef83885e174a8374dc59bfcbc47a29b89 Mon Sep 17 00:00:00 2001 From: Wahyu Kristianto Date: Sun, 21 Sep 2025 21:18:41 +0700 Subject: [PATCH 2/4] refactor: improve package structure and functionality - Update composer.json with proper metadata and version 2.0.0 - Simplify config/helpers.php by removing unnecessary sections - Enhance HelperMakeCommand with subdirectory support and CamelCase conversion - Update HelperServiceProvider with improved error handling - Improve helper.stub template with better placeholders - Add auto-prefix 'Helper' suffix functionality - Support nested directory creation with CamelCase folder names --- composer.json | 20 +- config/helpers.php | 11 +- src/Console/Commands/HelperMakeCommand.php | 286 ++++++++++++++++++++- src/Console/Commands/stubs/helper.stub | 28 +- src/HelperServiceProvider.php | 25 +- 5 files changed, 349 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index 8077b80..25c4e1b 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,21 @@ { "name": "devtical/laravel-helpers", "description": "Helper Generator for Laravel", + "version": "2.0.0", "keywords": [ + "devtical", "laravel", "helper", "generator" ], + "homepage": "https://github.com/devtical/laravel-helpers", "license": "MIT", + "authors": [ + { + "name": "W Kristianto", + "email": "w.kristories@gmail.com" + } + ], "require": { "php": "^7.2|^8.0", "illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" @@ -14,8 +23,8 @@ "require-dev": { "laravel/pint": "^1.18", "orchestra/testbench": "^4.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0", - "phpunit/phpunit": "^8.4 || ^9.0 || ^10.0 || ^11.0", - "friendsofphp/php-cs-fixer": "^3.0" + "pestphp/pest": "^2.0", + "pestphp/pest-plugin-laravel": "^2.0" }, "autoload": { "psr-4": { @@ -43,10 +52,9 @@ } }, "scripts": { - "format": "vendor/bin/pint", - "test": "vendor/bin/pest --colors=always", - "analyse": "vendor/bin/phpstan analyse" + "test": "vendor/bin/pest --no-coverage", + "format": "vendor/bin/pint" }, - "minimum-stability": "dev", + "minimum-stability": "stable", "prefer-stable": true } diff --git a/config/helpers.php b/config/helpers.php index daeb5ba..273a9a0 100644 --- a/config/helpers.php +++ b/config/helpers.php @@ -2,6 +2,15 @@ return [ - 'directory' => 'Helpers', + /* + |-------------------------------------------------------------------------- + | Helper Directory + |-------------------------------------------------------------------------- + | + | This option controls the directory where your helper files will be stored. + | The directory is relative to the app_path(). + | + */ + 'directory' => env('HELPER_DIRECTORY', 'Helpers'), ]; diff --git a/src/Console/Commands/HelperMakeCommand.php b/src/Console/Commands/HelperMakeCommand.php index 5dbfd69..ea6a302 100644 --- a/src/Console/Commands/HelperMakeCommand.php +++ b/src/Console/Commands/HelperMakeCommand.php @@ -3,6 +3,8 @@ namespace Devtical\Helpers\Console\Commands; use Illuminate\Console\GeneratorCommand; +use Illuminate\Support\Facades\File; +use Illuminate\Support\Str; class HelperMakeCommand extends GeneratorCommand { @@ -11,14 +13,18 @@ class HelperMakeCommand extends GeneratorCommand * * @var string */ - protected $signature = 'make:helper {name}'; + protected $signature = 'make:helper + {name : The name of the helper file (supports subdirectories like test/array)} + {--force : Overwrite the helper file if it already exists} + {--description= : Description for the helper file} + {--author= : Author of the helper file}'; /** * The console command description. * * @var string */ - protected $description = 'Create a new helper file'; + protected $description = 'Create a new helper file with functions'; /** * The type of class being generated. @@ -47,4 +53,280 @@ protected function getDefaultNamespace($rootNamespace) { return $rootNamespace.'\\'.config('helpers.directory', 'Helpers'); } + + /** + * Get the destination class path. + * Supports subdirectories by parsing the name for directory structure. + * + * @param string $name + * @return string + */ + protected function getPath($name) + { + $directory = app_path(config('helpers.directory', 'Helpers')); + + // Check if name contains subdirectory (e.g., "test/array" or "test\array") + $nameParts = preg_split('/[\/\\\\]/', $name); + + if (count($nameParts) > 1) { + // Extract directory path and filename + $dirParts = array_slice($nameParts, 0, -1); + $filename = Str::studly(end($nameParts)).'.php'; + + // Convert all directory parts to CamelCase + $camelCaseDirParts = array_map(function ($part) { + return Str::studly($part); + }, $dirParts); + + $subDir = implode('/', $camelCaseDirParts); + + // Create full path with subdirectory + $fullPath = $directory.'/'.$subDir.'/'.$filename; + + // Ensure subdirectory exists + $subDirPath = $directory.'/'.$subDir; + if (! File::exists($subDirPath)) { + File::makeDirectory($subDirPath, 0755, true); + $this->info("Created subdirectory: {$subDirPath}"); + } + + return $fullPath; + } + + // No subdirectory, use original logic + $filename = Str::studly($name).'.php'; + + return $directory.'/'.$filename; + } + + /** + * Build the class with the given name. + * + * @param string $name + * @return string + */ + protected function buildClass($name) + { + $stub = $this->files->get($this->getStub()); + + $filename = Str::studly($name); + + // For subdirectories, extract only the filename part for function name + $nameParts = preg_split('/[\/\\\\]/', $name); + $functionName = Str::camel(end($nameParts)); + + $description = $this->option('description') ?: "Helper functions for {$filename}"; + $author = $this->option('author') ?: 'Laravel Helper'; + $date = now()->format('Y-m-d H:i:s'); + + return $this->replaceNamespace($stub, $name) + ->replaceFunctionName($stub, $functionName) + ->replaceDescription($stub, $description) + ->replaceAuthor($stub, $author) + ->replaceDate($stub, $date) + ->replaceClass($stub, $filename); + } + + /** + * Replace the function name in the stub. + * + * @param string $stub + * @param string $functionName + * @return $this + */ + protected function replaceFunctionName(&$stub, $functionName) + { + $stub = str_replace('{{functionName}}', $functionName, $stub); + + return $this; + } + + /** + * Replace the description in the stub. + * + * @param string $stub + * @param string $description + * @return $this + */ + protected function replaceDescription(&$stub, $description) + { + $stub = str_replace('{{description}}', $description, $stub); + + return $this; + } + + /** + * Replace the author in the stub. + * + * @param string $stub + * @param string $author + * @return $this + */ + protected function replaceAuthor(&$stub, $author) + { + $stub = str_replace('{{author}}', $author, $stub); + + return $this; + } + + /** + * Replace the date in the stub. + * + * @param string $stub + * @param string $date + * @return $this + */ + protected function replaceDate(&$stub, $date) + { + $stub = str_replace('{{date}}', $date, $stub); + + return $this; + } + + /** + * Execute the console command. + * + * @return bool|null + */ + public function handle() + { + $name = $this->getNameInput(); + + // Convert to CamelCase and validate + $camelCaseName = $this->convertToCamelCase($name); + + if (! $this->isValidCamelCase($camelCaseName)) { + $this->error('The name must be in CamelCase format (e.g., StringHelper, ArrayHelper).'); + $this->info('Valid examples: StringHelper, ArrayHelper, DateHelper, UserHelper'); + $this->info('For subdirectories: test/ArrayHelper, utils/StringHelper'); + $this->info('Auto-conversion applied: '.$name.' → '.$camelCaseName); + + return false; + } + + // Show conversion if different from input + if ($name !== $camelCaseName) { + $this->info("Converting '{$name}' to '{$camelCaseName}'"); + } + + $path = $this->getPath($camelCaseName); + + // Check if file already exists + if ($this->files->exists($path) && ! $this->option('force')) { + $this->error("Helper file already exists: {$path}"); + $this->info('Use --force to overwrite the existing file.'); + + return false; + } + + // Create the directory if it doesn't exist + $directory = dirname($path); + if (! File::exists($directory)) { + File::makeDirectory($directory, 0755, true); + $this->info("Created directory: {$directory}"); + } + + // Generate the file + $this->makeDirectory($path); + $this->files->put($path, $this->buildClass($camelCaseName)); + + $this->info("Helper file created: {$path}"); + + return true; + } + + /** + * Convert input to CamelCase format with auto-prefix. + * Supports subdirectories by converting all parts to CamelCase. + * + * @param string $name + * @return string + */ + protected function convertToCamelCase($name) + { + // Remove any non-alphanumeric characters except spaces, hyphens, underscores, and forward slashes + $name = preg_replace('/[^a-zA-Z0-9\s\-_\/]/', '', $name); + + // Check if name contains subdirectory + $nameParts = preg_split('/[\/\\\\]/', $name); + + if (count($nameParts) > 1) { + // Convert all parts to CamelCase + $camelCaseParts = array_map(function ($part) { + // Convert snake_case and kebab-case to CamelCase + $part = str_replace(['-', '_'], ' ', $part); + + return Str::studly($part); + }, $nameParts); + + $name = implode('/', $camelCaseParts); + } else { + // Convert snake_case and kebab-case to CamelCase + $name = str_replace(['-', '_'], ' ', $name); + $name = Str::studly($name); + } + + // Add "Helper" suffix if not already present + if (! $this->hasHelperSuffix($name)) { + $name = $name.'Helper'; + } + + return $name; + } + + /** + * Check if the name already has "Helper" suffix. + * + * @param string $name + * @return bool + */ + protected function hasHelperSuffix($name) + { + // Check if name ends with "Helper" (case sensitive) + return str_ends_with($name, 'Helper'); + } + + /** + * Check if the name is valid CamelCase. + * For subdirectories, only validate the filename part. + * + * @param string $name + * @return bool + */ + protected function isValidCamelCase($name) + { + // Check if name contains subdirectory + $nameParts = preg_split('/[\/\\\\]/', $name); + + if (count($nameParts) > 1) { + // For subdirectories, validate only the filename part + $filename = end($nameParts); + + return preg_match('/^[A-Z][a-zA-Z0-9]*$/', $filename); + } + + // Must start with uppercase letter and contain only letters and numbers + return preg_match('/^[A-Z][a-zA-Z0-9]*$/', $name); + } + + /** + * Check if the name is valid (legacy method for backward compatibility). + * + * @param string $name + * @return bool + */ + protected function isValidName($name) + { + return preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $name); + } + + /** + * Get the desired class name from the input. + * + * @return string + */ + protected function getNameInput() + { + return trim($this->argument('name')); + } } diff --git a/src/Console/Commands/stubs/helper.stub b/src/Console/Commands/stubs/helper.stub index 40845d7..a34c920 100644 --- a/src/Console/Commands/stubs/helper.stub +++ b/src/Console/Commands/stubs/helper.stub @@ -1,15 +1,29 @@ app->runningInConsole()) { $this->commands([ HelperMakeCommand::class, + HelperListCommand::class, ]); } } @@ -34,11 +37,23 @@ public function boot() */ public function register() { - $this->mergeConfigFrom(self::CONFIG_PATH, 'helper'); - $files = glob(app_path(config('helpers.directory', 'Helpers').'/*.php')); + $this->mergeConfigFrom(self::CONFIG_PATH, 'helpers'); - foreach ($files as $file) { - require_once $file; - } + $this->app->singleton(HelperManager::class, function ($app) { + return new HelperManager($app); + }); + + $this->loadHelpers(); + } + + /** + * Load all helper files from the configured directory. + * + * @return void + */ + protected function loadHelpers() + { + $helperManager = $this->app->make(HelperManager::class); + $helperManager->loadHelpers(); } } From bf425d857463144cff00c63bd6e4483dd3b93c02 Mon Sep 17 00:00:00 2001 From: Wahyu Kristianto Date: Sun, 21 Sep 2025 21:18:51 +0700 Subject: [PATCH 3/4] chore: sync package structure with filament-sanctum - Add phpunit.xml.dist for consistent test configuration - Add pint.json for code formatting standards - Add CONTRIBUTING.md for contribution guidelines - Align package structure with filament-sanctum reference - Ensure consistency across Devtical packages --- CONTRIBUTING.md | 41 +++++++++++++++++++++++++++++++---------- phpunit.xml.dist | 24 +++++++++++++++++------- pint.json | 9 +++++++++ 3 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 pint.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eaaa86f..1dd2d95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,16 +1,37 @@ # Contributing -Contributions are welcome and will be fully credited. +Contributions are **welcome** and will be fully **credited**. -Contributions are accepted via Pull Requests on Github. +Please read and understand the contribution guide before creating an issue or pull request. -## Pull Requests +## Etiquette -- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)**. -- **Add tests!** - Your patch won't be accepted if it doesn't have tests. -- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. -- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. -- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. -- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. -**Happy coding**! +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 94ea926..15fc41b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,13 +1,23 @@ - - - - src/ - - + - + tests + + + + + + + + + + + + + ./src + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..f99545b --- /dev/null +++ b/pint.json @@ -0,0 +1,9 @@ +{ + "preset": "laravel", + "rules": { + "no_unused_imports": true, + "ordered_imports": { + "sort_algorithm": "alpha" + } + } +} From f75fd812c4fe448e4b160f122f1b1555132c076a Mon Sep 17 00:00:00 2001 From: Wahyu Kristianto Date: Sun, 21 Sep 2025 21:34:01 +0700 Subject: [PATCH 4/4] cleanup: remove unnecessary files and update documentation - Remove CODE_OF_CONDUCT.md (not needed for simple package) - Remove phpstan configuration files (not needed) - Remove old test files (HelpersTest.php, TestCase.php) - Update README.md with improved documentation - Update .github/workflows/tests.yml for better CI/CD - Clean up package structure for production readiness --- .github/workflows/tests.yml | 13 +++---- CODE_OF_CONDUCT.md | 76 ------------------------------------- README.md | 39 +++++++++++++++---- phpstan-baseline.neon | 0 phpstan.neon.dist | 9 ----- tests/HelpersTest.php | 16 -------- tests/TestCase.php | 20 ---------- 7 files changed, 36 insertions(+), 137 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 phpstan-baseline.neon delete mode 100644 phpstan.neon.dist delete mode 100644 tests/HelpersTest.php delete mode 100644 tests/TestCase.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2968318..42fba23 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,17 +15,14 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - php: [8.1, 8.2, 8.3] + php: [8.3, 8.4] include: - - php: 8.1 - laravel: 9.* - testbench: 7.* - - php: 8.2 - laravel: 10.* - testbench: 8.* - php: 8.3 laravel: 11.* testbench: 9.* + - php: 8.4 + laravel: 12.* + testbench: 10.* name: PHP ${{ matrix.php }} / Laravel ${{ matrix.laravel }} @@ -49,4 +46,4 @@ jobs: run: composer show -D - name: Execute tests - run: vendor/bin/phpunit --exclude-group skipped + run: composer test \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index d6a6cd3..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,76 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at w.kristories@gmail.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq diff --git a/README.md b/README.md index 94e9cc6..cefda87 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ # Laravel Helpers - -[![Latest Version on Packagist](https://img.shields.io/packagist/v/devtical/laravel-helpers.svg?style=flat-square)](https://packagist.org/packages/devtical/laravel-drunk-on-419) -[![Total Downloads](https://img.shields.io/packagist/dt/devtical/laravel-helpers.svg?style=flat-square)](https://packagist.org/packages/devtical/laravel-drunk-on-419) - Helper Generator for Laravel ## Installation @@ -16,13 +12,36 @@ composer require devtical/laravel-helpers Publish the config by running the `php artisan vendor:publish` command. +Configure your helper directory: +```bash +# In your .env file +HELPER_DIRECTORY=Helpers +``` + ## Usage +Create your first helper file: ```bash php artisan make:helper ``` +Add your helper functions: +```php +artisan('make:helper', ['name' => 'TestHelper'])->assertExitCode(0); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index 52430c5..0000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,20 +0,0 @@ -> - */ - protected function getPackageProviders($app) - { - return [ - HelperServiceProvider::class, - ]; - } -}