Generate production-ready Laravel CRUD modules from a single Artisan command.
Each generated module follows clean architecture — model, API controller, service, repository, interface, form requests, API resource, migration, factory, feature tests, and translations. The generator wires everything automatically: no manual provider registration, no manual route includes, no manual binding setup.
| Dependency | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | 11, 12, 13 |
Note: PHP 8.2 is not compatible with Laravel 13 (which requires PHP ≥ 8.3). The CI matrix enforces this constraint explicitly.
composer require muhammedsalama/crudforge:^1.2.0The ^1.2.0 constraint allows any backward-compatible update (1.2.x, 1.3.0, etc.) while blocking major versions that may contain breaking changes. To upgrade to a future major version, review the migration guide in CHANGELOG.md first.
Laravel auto-discovery registers both service providers automatically. No manual registration is required.
Optionally run the install command to publish the config and confirm the setup:
php artisan crudforge:installcomposer update muhammedsalama/crudforgePatch and minor releases are fully backward-compatible. No changes to your configuration, generated files, or application code are required.
For major version upgrades, see the Upgrade Guide below and the relevant entry in CHANGELOG.md.
php artisan crudforge:generate Order --fields="customer_name:string,total:decimal,status:boolean,note:text"That single command:
- Generates all files for the
Ordermodule - Writes the repository binding to
bootstrap/crudforge-bindings.php - Registers the route file in
routes/crudforge.php - Reports the live API endpoint
app/
├── Http/
│ ├── Controllers/Api/OrderController.php
│ ├── Requests/StoreOrderRequest.php
│ ├── Requests/UpdateOrderRequest.php
│ └── Resources/OrderResource.php
├── Interfaces/
│ └── OrderRepositoryInterface.php
├── Models/
│ └── Order.php
├── Repositories/
│ └── OrderRepository.php
└── Services/
└── OrderService.php
bootstrap/
└── crudforge-bindings.php ← updated automatically; commit this file
database/
├── factories/OrderFactory.php
└── migrations/xxxx_xx_xx_create_orders_table.php
lang/
├── en/orders.php
└── ar/orders.php
routes/
├── crudforge-orders.php ← route definitions for this module
└── crudforge.php ← route registry; updated automatically, commit this file
tests/
└── Feature/OrderApiTest.php
| Step | Handled by |
|---|---|
| Service provider registration | Laravel auto-discovery |
| Hydrating repository bindings at runtime | CrudForgeRuntimeServiceProvider |
Loading the routes/crudforge.php manifest |
CrudForgeRuntimeServiceProvider |
Writing binding to bootstrap/crudforge-bindings.php |
crudforge:generate |
Registering route in routes/crudforge.php |
crudforge:generate |
bootstrap/providers.php, bootstrap/app.php, and routes/api.php are never modified.
After generating the Order module, the following REST endpoints are available:
GET /api/orders Paginated index
POST /api/orders Create
GET /api/orders/{order} Show
PUT /api/orders/{order} Update
DELETE /api/orders/{order} Destroy
The /api prefix is controlled by crudforge.defaults.route_prefix in the published config. See Configuration.
Supported query parameters on GET /api/orders:
| Parameter | Description |
|---|---|
search |
Full-text search across string/text fields |
sort_by |
Column name (validated against an allowlist) |
sort_direction |
asc or desc |
per_page |
1–100, default 15 |
| Alias | Normalised type | Migration column | Eloquent cast |
|---|---|---|---|
string, str, varchar |
string |
string() |
— |
text |
text |
text()->nullable() |
— |
integer, int |
integer |
integer() |
— |
bigInteger, bigint |
bigInteger |
bigInteger() |
— |
unsignedInteger, uint |
unsignedInteger |
unsignedInteger() |
— |
unsignedBigInteger, ubigint |
unsignedBigInteger |
unsignedBigInteger() |
— |
boolean, bool |
boolean |
boolean()->default(false) |
boolean |
decimal, float, double |
decimal |
decimal(10, 2) |
decimal:2 |
date |
date |
date()->nullable() |
date |
datetime |
datetime |
dateTime()->nullable() |
datetime |
timestamp |
timestamp |
timestamp()->nullable() |
datetime |
json, jsonb |
json |
json()->nullable() |
array |
foreignId, foreign_id |
foreignId |
foreignId()->constrained()->cascadeOnDelete() |
— |
Validation rules are derived automatically from the field type:
StoreRequest— required fields use'required'; nullable types (text,date,datetime,json) use'nullable'UpdateRequest— all fields use'sometimes'for safe partial updates
# One-time setup — publishes config and prints a setup summary
php artisan crudforge:install
# Generate a full CRUD module
php artisan crudforge:generate {Name} --fields="field:type,..."
# Preview what would be generated without writing any files
php artisan crudforge:generate {Name} --fields="..." --dry-run
# Overwrite existing files without confirmation
php artisan crudforge:generate {Name} --fields="..." --force
# Skip automatic registry updates (binding + route manifest)
php artisan crudforge:generate {Name} --fields="..." --no-auto-register
# Re-wire a specific module's binding (e.g. after pulling from Git)
php artisan crudforge:install-bindings Order
# Re-wire all modules at once
php artisan crudforge:install-bindings --allPublish the config to customise output paths, namespaces, and runtime defaults:
php artisan vendor:publish --tag=crudforge-config// config/crudforge.php
'paths' => [
'models' => app_path('Models'),
'controllers' => app_path('Http/Controllers/Api'),
// ...
],
'namespaces' => [
'models' => 'App\\Models',
'controllers' => 'App\\Http\\Controllers\\Api',
// ...
],
'defaults' => [
'pagination_per_page' => 15,
'route_prefix' => 'api',
],
'auto_register' => true,Important: If you customise the API route prefix in
bootstrap/app.php(e.g.withRouting(apiPrefix: 'v1')), you must setcrudforge.defaults.route_prefixto the same value ('v1'). Otherwise generated routes and generated tests will use the wrong prefix.
Publish stubs to customise the generated code templates:
php artisan vendor:publish --tag=crudforge-stubsStubs are published to stubs/vendor/crudforge/. CrudForge checks for a published stub before falling back to the built-in one, so you only need to publish the stubs you intend to modify.
Every generated module follows a strict layered architecture:
HTTP Request
│
▼
Controller ← HTTP layer only; no business logic
│
▼
Service ← Business logic; depends on the interface, not the concrete class
│
▼
RepositoryInterface ← Contract; makes the concrete class swappable
│
▼
Repository ← Eloquent queries; implements the interface
│
▼
Model
The generated code has zero runtime dependency on CrudForge. Once generation is complete, the package can be moved to require-dev or removed entirely without affecting the running application.
The search logic in generated repositories is driver-aware and inlined — no runtime package dependency:
| Driver | Operator |
|---|---|
| MySQL / SQLite | LIKE |
| PostgreSQL | ILIKE (case-insensitive) |
Tag any GeneratorContract implementation with crudforge.generators in your own service provider to add it to the generation pipeline without modifying the package:
use MuhammedSalama\CrudForge\Contracts\GeneratorContract;
// In your ServiceProvider::register():
$this->app->bind(PolicyGenerator::class);
$this->app->tag([PolicyGenerator::class], 'crudforge.generators');Your generator runs after the built-in ones on every crudforge:generate call.
- Files are never overwritten without confirmation unless
--forceis passed - Declining a single overwrite skips that file and continues the rest of the run
- All output paths are validated against
base_path()to prevent path traversal - Model names must match
[A-Za-z][A-Za-z0-9]* - Field names must match
[a-z][a-z0-9_]*
1.2.x is a compatibility-only release. No changes to the public API, generated output, or configuration.
composer update muhammedsalama/crudforgeNo further action is required.
1.1.x introduced the runtime registry system that replaces direct bootstrap/providers.php editing.
Step 1 — Update the package
composer update muhammedsalama/crudforgeStep 2 — Remove any manual CrudForge entries from bootstrap/providers.php
If a previous version added a provider entry for CrudForge to bootstrap/providers.php, remove it. Both providers are now registered via Composer auto-discovery.
Step 3 — Remove manual route includes from routes/api.php
If you previously added require __DIR__.'/crudforge-*.php' lines to routes/api.php, remove them. Routes are now loaded from a single manifest (routes/crudforge.php) by CrudForgeRuntimeServiceProvider.
Step 4 — Regenerate the binding registry
php artisan crudforge:install-bindings --allThis creates bootstrap/crudforge-bindings.php. Commit it to version control.
Step 5 — Clear caches
php artisan config:clear
php artisan route:clearGenerated routes are loaded automatically by the service provider. If you have cached routes, clear the cache:
php artisan route:clearIf you previously added a manual require __DIR__.'/crudforge-*.php' in routes/api.php, remove it — routes are now auto-loaded via routes/crudforge.php and the manual require causes duplicate registration.
If you have changed the API prefix in bootstrap/app.php, ensure crudforge.defaults.route_prefix in your published config matches it.
The binding registry is missing or incomplete. Re-run the binding setup:
php artisan crudforge:install-bindings --allThen verify that bootstrap/crudforge-bindings.php exists and contains the expected interface-to-repository mapping. If the file was accidentally deleted or excluded from version control, re-running the above command regenerates it. Ensure the file is committed — it is a CrudForge-owned file, not a Laravel core file.
php artisan vendor:publish --tag=crudforge-config
php artisan config:clearUse --force to overwrite all files, or omit it to be asked per file:
php artisan crudforge:generate Order --fields="..." --forceRegenerating does not create a duplicate migration file — CrudForge detects the existing migration and overwrites it in place.
Run the test suite:
composer testRun static analysis:
composer analyseThis package follows Semantic Versioning (MAJOR.MINOR.PATCH):
| Increment | When | Example |
|---|---|---|
PATCH |
Bug fixes and internal improvements; no API changes | 1.2.0 → 1.2.1 |
MINOR |
New features added in a backward-compatible manner | 1.2.0 → 1.3.0 |
MAJOR |
Breaking changes to the public API or generated output | 1.2.0 → 2.0.0 |
Patch and minor updates are safe to apply via composer update without reviewing generated files or configuration. Major version bumps will always include a migration guide in CHANGELOG.md.
See CHANGELOG.md for the full release history.
Contributions are welcome. Please open an issue before submitting a pull request for significant changes.
The MIT License. See LICENSE.
For support, bug reports, or feature requests:
- Email: devmuhammedsalama@gmail.com
- Issues: GitHub Issues
- Built for Laravel developers who want production-ready CRUD scaffolding
- Inspired by Laravel clean architecture and modern package development practices
- Special thanks to all contributors and testers
Made with ❤️ by Muhammed Salama