ixspx / module-generator
Generate module structure in Laravel
Requires
- php: ^8.2 || ^8.3
- illuminate/console: ^10.0 || ^11.0 || ^12.0 || ^13.0
- illuminate/filesystem: ^10.0 || ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^10.0 || ^11.0 || ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^8.0
- phpunit/phpunit: ^10.0
README
ixspx/module-generator is an enterprise-grade Laravel developer tooling package designed to accelerate development by scaffolding cleanly layered application modules (Model, Repository Interface, Repository Implementation, Service, Controller, and Service Provider) following PHP 8.2+, Laravel 12, PSR-12, and Clean Architecture standards while establishing a driver-driven, multi-specification REST API foundation supporting Standard REST, JSON:API 1.1, RFC 7807 Problem Details, and custom API drivers.
Table of Contents
- 1. Project Overview
- 2. Key Features
- 3. Modern PHP 8.2+ & Clean Architecture Standards
- 4. Architecture Overview
- 5. API Specification Drivers
- 6. Generated Structure & Code Examples
- 7. Installation
- 8. Configuration
- 9. Available Artisan Commands
- 10. Extensibility & Custom Drivers
- 11. Best Practices
- 12. License
1. Project Overview
What is this package?
ixspx/module-generator is a dual-purpose Laravel package:
- Modern Module Scaffolder (
make:mod): Scaffolds modular domain layers adhering strictly to Clean Architecture, SOLID principles, and Service-Repository patterns using modern PHP 8.2+ features (Constructor Property Promotion,readonly, typed properties,strict_types=1,finalclasses). - Multi-Specification API Starter (
make:api-install&make:api-response): Provisions a specification-aware API response engine, middleware to enforce JSON/JSON:API headers, and a centralized exception registrar. Switch between Standard REST, JSON:API 1.1, or Problem Details instantly via configuration.
2. Key Features
- π Modern Full-Stack Module Generator: Scaffolds Model, Interface, Concrete Repository, Service, Controller, and Service Provider via
php artisan make:mod {Name}. - β‘ PHP 8.2+ & Laravel 12 Native: Built with
declare(strict_types=1), Constructor Property Promotion,private readonlyproperties, typed attributes, andfinalclasses. - π Driver-Driven API Architecture: Switch between Standard REST, JSON:API 1.1, and RFC 7807 Problem Details simply by changing
config('module-generator.api_specification')or.env. - βοΈ Auto-Registration of Providers & Routes: Automatically registers scaffolded providers in
bootstrap/providers.phporconfig/app.phpand appends RESTful routes toroutes/api.php. - π¨ Publishable Stubs & Configuration: Fully customizable code templates via
php artisan vendor:publish --tag=module-generator-stubsandmodule-generator-config. - π Centralized Exception Handling: Specification-aware exception mapping for database, validation, auth, and domain exceptions.
3. Modern PHP 8.2+ & Clean Architecture Standards
All scaffolded code generated by this package enforces strict modern PHP and Clean Architecture rules:
A. Constructor Property Promotion & Visibility Rules
private readonly: Used for all internal class dependencies injected via constructor (Services, Repositories, Models, Filesystem).protected: Reserved strictly for inheritance hooks or Eloquent internal properties ($table,$fillable).public: Reserved exclusively for explicit class API methods.
B. Modern PHP Features Enforced
declare(strict_types=1)at the top of every generated file.finalclass declaration: Controllers, Services, Repositories, Providers, and Middleware arefinalby default to prevent accidental inheritance coupling.- Typed Properties & Explicit Return Types: All properties and methods specify explicit types (e.g.
LengthAwarePaginator,JsonResponse,Model,: void).
C. Clean Architecture Principles
- Dependency Inversion: Services depend on Repository Interfaces (
{Name}Interface), never on concrete Eloquent repositories or models directly. - No Service Locators in Application Code: Dependencies are injected via Constructor DI.
- Single Responsibility: Clean separation between Delivery (Controller), Business Orchestration (Service), Data Access (Repository), and Persistence (Model).
4. Architecture Overview
ββββββββββββββββββββββββββββββ
β config/module-generator β
βββββββββββββββ¬βββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββ
β ApiSpecificationFactory β
βββββββββββββββ¬βββββββββββββββ
β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββββββββ βββββββββββββββββββββββββ βββββββββββββββββββββββββ
β RestApiSpecification β β JsonApiSpecification β βProblemDetailsSpecifiβ¦ β
βββββββββββββ¬ββββββββββββ βββββββββββββ¬ββββββββββββ βββββββββββββ¬ββββββββββββ
β β β
β application/json β application/vnd.api+jsonβ application/problem+json
βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Cross-Cutting Services: ApiResponse / ForceJsonResponse / Exception β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
5. API Specification Drivers
A. Standard REST API (Default)
- Media Type:
application/json
{
"success": true,
"responseCode": 200,
"message": "Data retrieved successfully",
"data": { "id": 1, "name": "John Doe" },
"meta": { "count": 1 }
}
B. JSON:API 1.1 Specification (API_SPECIFICATION=jsonapi)
- Media Type:
application/vnd.api+json
{
"jsonapi": { "version": "1.1" },
"data": {
"type": "resources",
"id": "1",
"attributes": { "name": "John Doe" }
}
}
C. RFC 7807 Problem Details (API_SPECIFICATION=problem-details)
- Media Type:
application/problem+jsonfor error responses
{
"type": "http://localhost/errors/validation-error",
"title": "Validation error",
"status": 422,
"detail": "The email field is required.",
"instance": "http://localhost/api/v1/users",
"invalid-params": [
{ "name": "email", "reason": "The email field is required." }
]
}
6. Generated Structure & Code Examples
Running php artisan make:mod User generates the following clean, modern PHP 8.2+ structure:
1. Controller (App\Http\Controllers\User\UserController.php)
<?php declare(strict_types=1); namespace App\Http\Controllers\User; use App\Http\Controllers\Controller; use App\Services\User\UserService; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; final class UserController extends Controller { public function __construct( private readonly UserService $userService ) {} public function index(): JsonResponse { $data = $this->userService->getAll(); $meta = ['count' => $data->count()]; return ApiResponse::success($data, 'Data retrieved successfully', 200, $meta); } }
2. Business Service (App\Services\User\UserService.php)
<?php declare(strict_types=1); namespace App\Services\User; use App\Repositories\Interfaces\User\UserInterface; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; final class UserService { public function __construct( private readonly UserInterface $repository ) {} public function getAll(): Collection { return $this->repository->getAll(); } public function getById(int $id): Model { return $this->repository->findOrFail($id); } public function create(array $data): Model { return DB::transaction(function () use ($data) { return $this->repository->create($data); }); } public function paginate(int $perPage = 15): LengthAwarePaginator { return $this->repository->paginate($perPage); } }
3. Repository (App\Repositories\Repository\User\UserRepository.php)
<?php declare(strict_types=1); namespace App\Repositories\Repository\User; use App\Models\User\UserModel; use App\Repositories\Interfaces\User\UserInterface; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Model; final class UserRepository implements UserInterface { public function __construct( private readonly UserModel $model ) {} public function getAll(): Collection { return $this->model->all(); } public function findOrFail(int $id): Model { return $this->model->findOrFail($id); } public function paginate(int $perPage = 15): LengthAwarePaginator { return $this->model->paginate($perPage); } }
4. Eloquent Model (App\Models\User\UserModel.php)
<?php declare(strict_types=1); namespace App\Models\User; use Illuminate\Database\Eloquent\Model; class UserModel extends Model { protected string $table = 'users'; /** @var list<string> */ protected array $fillable = [ // Add your fillable fields here ]; }
7. Installation
composer require ixspx/module-generator
Publish Configuration & Stubs (Optional)
Publishing the configuration file and stubs is completely optional. The package functions zero-config out of the box with sensible defaults (Standard REST driver, auto-registration enabled).
# Publish configuration file to config/module-generator.php (Optional) php artisan vendor:publish --tag=module-generator-config # Publish template stubs to stubs/module-generator/ (Optional) php artisan vendor:publish --tag=module-generator-stubs
8. Configuration
return [ /* |-------------------------------------------------------------------------- | Default API Specification Driver |-------------------------------------------------------------------------- | Supported Drivers: 'rest', 'jsonapi', 'problem-details', or custom class */ 'api_specification' => env('API_SPECIFICATION', 'rest'), 'jsonapi' => [ 'version' => '1.1', 'base_url' => env('APP_URL', 'http://localhost'), ], 'problem_details' => [ 'type_base_url' => env('APP_URL', 'http://localhost') . '/errors', ], 'table_prefix' => '', 'controller_style' => 'restful', // 'restful' or 'handler' 'auto_register_provider' => true, 'auto_register_route' => true, ];
9. Available Artisan Commands
| Command | Signature | Description | Key Options | Example Usage |
|---|---|---|---|---|
| Module Generator | make:mod {name} |
Scaffolds complete 6-layer PHP 8.2+ module structure. | --table=, --table-prefix=, --style=, --no-provider, --no-route, --force |
php artisan make:mod OrderPayment |
| API Installer | make:api-install |
Installs standard API foundation infrastructure. | --force |
php artisan make:api-install --force |
| API Response Helper | make:api-response |
Scaffolds ApiResponse support class. |
None | php artisan make:api-response |
βοΈ Setting Up API Route Prefixing (api/v1) & Exception Handling in Laravel 11 & 12
After running php artisan make:api-install, configure both the API route prefixing and the centralized ApiExceptionRegistrar inside your application's bootstrap/app.php to ensure all API exceptions are returned in formatted JSON:
<?php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', then: function ($router) { Route::prefix('api/v1') ->group(base_path('routes/api.php')); }, ) ->withMiddleware(function (Middleware $middleware) { // ... }) ->withExceptions(function (Exceptions $exceptions): void { $exceptions->shouldRenderJsonWhen( fn(Request $request) => $request->is('api/*'), ); \App\Exceptions\ApiExceptionRegistrar::register($exceptions); })->create();
Tip
Why ApiExceptionRegistrar is required:
Without registering ApiExceptionRegistrar, unhandled API exceptions (e.g. 404 Not Found, 422 Validation Error, 500 Internal Error) will fall back to default unformatted HTML or generic Laravel exception pages. Calling \App\Exceptions\ApiExceptionRegistrar::register($exceptions) ensures all exceptions on api/* routes are consistently formatted according to your active API specification driver (rest, jsonapi, or problem-details).
π Controller Action Naming Styles (RESTful vs. Handler Style)
ixspx/module-generator supports two distinct controller action naming conventions:
- RESTful Style (Default): Standard Laravel resource action names (
index,show,store,update,destroy) mapped viaRoute::apiResource(). - Handler Style: Explicit handler method names (
handlerGetAll,handlerGetById,handlerCreate,handlerUpdate,handlerDelete) mapped via explicitRoute::controller()->group(). This is particularly intuitive for developers coming from languages like Go, Express/TypeScript, Java, or C#.
Option A: Per-Module Generation via --style=handler
To generate a specific module using Handler style:
php artisan make:mod User --style=handler
Scaffolded Controller (app/Http/Controllers/User/UserController.php):
final class UserController extends Controller { public function handlerGetAll(): JsonResponse { ... } public function handlerGetById(int $id): JsonResponse { ... } public function handlerCreate(Request $request): JsonResponse { ... } public function handlerUpdate(Request $request, int $id): JsonResponse { ... } public function handlerDelete(int $id): JsonResponse { ... } }
Scaffolded Route (routes/api.php):
Route::controller(\App\Http\Controllers\User\UserController::class)->group(function () { Route::get('/users', 'handlerGetAll'); Route::get('/users/{id}', 'handlerGetById'); Route::post('/users', 'handlerCreate'); Route::put('/users/{id}', 'handlerUpdate'); Route::delete('/users/{id}', 'handlerDelete'); });
Option B: Application-Wide Default Configuration
To set Handler style as the default for all scaffolded modules across your project, set controller_style in config/module-generator.php:
return [ 'controller_style' => env('MODULE_CONTROLLER_STYLE', 'handler'), ];
Now, running php artisan make:mod User will automatically scaffold using Handler style.
10. Extensibility & Custom Drivers
Extend the factory with custom API drivers in your AppServiceProvider:
use Ixspx\ModuleGenerator\Contracts\ApiSpecificationInterface; use Ixspx\ModuleGenerator\Factories\ApiSpecificationFactory; public function boot(ApiSpecificationFactory $factory): void { $factory->extend('company-api', function ($app) { return new CustomCompanyApiSpecification(); }); }
11. Best Practices
- Keep Controllers Thin: Controllers delegate formatting to
ApiResponse::success(), which formats payloads according to the configured driver. - Use Interface Binding: Inject
{Name}Interfaceinto Services to adhere to Dependency Inversion. - Centralize Exception Mapping: Throw domain exceptions inside services;
ApiExceptionRegistrarconverts them to the active specification driver format.
12. License
Licensed under the MIT License. See LICENSE for details.