skyronic/laravel-file-generator
Laravel File Generator adds customizable Artisan commands for scaffolding files from templates. Define generators in config, pass variables, and create models, services, repositories, or any project file consistently, speeding up repetitive boilerplate while keeping generated code aligned with your...
Install the package and let Laravel's package discovery register it:
composer require skyronic/laravel-file-generator
Confirm what commands and config the installed version exposes (the package is small and the command names have changed across versions):
php artisan list | grep -i file
php artisan vendor:publish --provider="Skyronic\FileGenerator\FileGeneratorServiceProvider"
Look first at:
config/file-generator.php (if published) for stub paths, generator registration, and default destinations.src/ directory — with a package this size, the base generator class and the artisan command are the real documentation.stubs/ or resources/stubs/ directory to see the expected token syntax.The core workflow is: stub file + generator definition + artisan command → generated file. A generator is typically a small class that declares a source stub and a destination path, with placeholders filled from CLI parameters:
namespace App\FileGenerators;
use Skyronic\FileGenerator\FileGenerator;
class RepositoryGenerator extends FileGenerator
{
// Confirm exact hook names against the installed version.
public function getSourceFile(): string
{
return resource_path('stubs/repository.stub');
}
public function getDestinationFile(): string
{
return app_path('Repositories/' . $this->params['name'] . 'Repository.php');
}
}
Run it with the provided command (again, verify via php artisan list):
php artisan file:generate "App\FileGenerators\RepositoryGenerator" --name=User
First use case: generating the repetitive middle layer of a Laravel app — repositories, DTOs, actions, API resources, or Vue/React components — from a single canonical stub so the whole team produces identical boilerplate. Start with one artifact you write by hand more than twice a week, make a stub from the cleanest existing example, and wire a generator around it before adding more.
Stub-as-contract. Treat stubs as reviewed source code, not scratch files. Keep them in resources/stubs/ (or a dedicated stubs/ folder), commit them, and use unambiguous tokens like {{ class }}, {{ namespace }}, {{ model }}. This is the highest-leverage pattern: the stub becomes the team's agreed shape for a repository or action, and code review of the stub replaces review of every generated copy.
One generator per artifact, composed for features. Small generators (RepositoryGenerator, DtoGenerator, TestGenerator) are easier to test and reuse than a single mega-generator. For feature scaffolding, add a thin custom command that calls each generator in sequence:
// app/Console/Commands/MakeFeature.php
foreach (['Repository', 'Dto', 'Action', 'Test'] as $type) {
Artisan::call('file:generate', [
'generator' => "App\\FileGenerators\\{$type}Generator",
'--name' => $this->argument('name'),
'--force' => $this->option('force'),
]);
}
Programmatic invocation. When generation is part of a larger setup flow (tenant provisioning, module installers, test fixtures), call the command through Artisan::call() or resolve the generator from the container rather than shelling out. You get return codes, captured output, and testability for free.
Derive namespaces instead of hardcoding them. If your generator writes into app/Repositories, derive the namespace from the destination path so moving a folder or adopting a modular structure (e.g. app/Modules/Billing/...) doesn't require rewriting every generator. Pair this with PSR-4 roots in composer.json.
Layer with Laravel's native make: commands. Don't reimplement make:model, make:controller, etc. Use php artisan make: for standard artifacts, then your generators for the non-standard pieces and for wiring files together (e.g. generating the repository and the service-provider binding line).
Environment-aware destinations. Use app_path(), resource_path(), base_path() rather than relative strings. If you support both monolith and package-style layouts, put destination roots in config and read them from the generator.
Test the output, not just the exit code. A useful smoke test asserts the file exists, contains the expected class name/namespace, and passes php -l:
$this->artisan('file:generate', ['generator' => RepositoryGenerator::class, '--name' => 'User', '--force' => true]);
$this->assertFileExists(app_path('Repositories/UserRepository.php'));
$this->assertStringContainsString('namespace App\Repositories;', file_get_contents(...));
Run generators in CI against fixtures to catch stub drift after framework upgrades.
--force. If it overwrites silently, wrap it: check File::exists() in your composite command and require an explicit --force, or back up before generating. Never point a generator at a hand-edited file during routine use.config:cache hides changes. If generator definitions or stub paths live in config, run php artisan config:clear after editing. This is the most common "my change did nothing" cause.app/Repositories with a hardcoded App\Services namespace produces code that works until Composer's autoloader or an IDE inspection flags it. Derive the namespace or add a test that asserts it.{{ }} or Blade-like syntax, replacing {{ name }} before {{ name_plural }} (or vice versa) can corrupt output. Use distinct, longest-first tokens, and avoid tokens that also appear in real PHP (e.g. {{ class }} inside a docblock is fine; {{ }} inside a regex is not).<<<EOT, or heavy indentation often survive token replacement but break formatting. Generate a sample, run php -l, and eyeball the diff once per stub change./ works on Linux/macOS but can surprise on Windows CI. Prefer app_path('Repositories/' . $name . '.php') (Laravel normalizes) over hand-rolled concatenation..., no slashes) before interpolating into a destination path, especially if the command is exposed to a broader audience.composer.json, read the installed source when behavior is unclear, and write a thin wrapper command so a future replacement of the package doesn't ripple through every developer's workflow.getDestinationFile() before writing; when output is empty, check the stub path resolves with realpath(). Use -v / -vvv on the artisan command to surface underlying exceptions.resources/stubs/ and point your generators there..env, credentials, or machine-specific paths into stubs or committed generator defaults.How can I help you explore Laravel packages today?