Version 1.5.1 introduces the make:crud Artisan command, a full API CRUD scaffold generator. It replaces the abstract CrudController base class with a code generation approach — giving you production-ready Model, Controller, and Migration files in a single command, fully customisable through publishable stubs.
php artisan make:crud {name} [options]
Run the command with your model name and a set of options to instantly scaffold a complete API resource.
| Option | Type | Description |
|---|---|---|
name |
Argument | Model name in any case — e.g. Post, blog_post, UserProfile |
--fields |
String | Comma-separated field definitions: column:type:validation |
--belongs-to |
Multiple | BelongsTo relationship — e.g. --belongs-to=User |
--has-many |
Multiple | HasMany relationship — e.g. --has-many=Comment |
--has-one |
Multiple | HasOne relationship — e.g. --has-one=Profile |
--belongs-to-many |
Multiple | BelongsToMany relationship — e.g. --belongs-to-many=Tag |
--soft-deletes |
Flag | Adds SoftDeletes trait to model and softDeletes() column to migration |
--searchable |
String | Comma-separated fields to enable ?search= on in the index endpoint |
--per-page |
Integer | Default pagination size (default: 15) |
--register-routes |
Flag | Automatically appends Route::apiResource(...) to routes/api.php |
--migrate |
Flag | Runs php artisan migrate immediately after generating the migration |
--force |
Flag | Overwrites existing model and controller (migration is never duplicated) |
Fields are defined as a comma-separated string using the format column:type:validation:
--fields="title:string:required|max:255,body:text:nullable,price:decimal:required|min:0"
| Input Type | Migration Column | Model Cast |
|---|---|---|
string, varchar |
string() |
— |
text, longtext, mediumtext |
text() / longText() / mediumText() |
— |
integer, int |
integer() |
integer |
biginteger, bigint |
bigInteger() |
integer |
tinyinteger, smallinteger |
tinyInteger() / smallInteger() |
integer |
float |
float() |
float |
double |
double() |
float |
decimal |
decimal(10, 2) |
decimal:2 |
boolean, bool |
boolean()->default(false) |
boolean |
date |
date() |
date |
datetime |
dateTime() |
datetime |
timestamp |
timestamp() |
datetime |
json |
json() |
array |
uuid |
uuid() |
— |
enum |
enum([], []) |
— |
The nullable validation rule automatically appends ->nullable() to the migration column.
--fields->nullable() applied when field rule contains nullable->unique() applied when field rule contains standalone uniqueforeignId('relation_id')->constrained('table')->cascadeOnDelete() for every --belongs-to$table->softDeletes() when --soft-deletes is set$table->id() and $table->timestamps()namespace, class, and extends Model$fillable populated from --fields plus all foreign key columns from --belongs-to$casts automatically populated for typed fields (boolean, decimal, date, json, etc.)use SoftDeletes trait and import when --soft-deletes is setpublic function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
public function comments(): \Illuminate\Database\Eloquent\Relations\HasMany
public function profile(): \Illuminate\Database\Eloquent\Relations\HasOne
public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
apiResource controller with index, show, store, update, destroyshow, update, and destroystore returns HTTP 201data + meta (current_page, last_page, per_page, total)--searchable fields using ?search=term?sort_by=field&sort_direction=asc?per_page=N (default from --per-page)--fields and --belongs-to foreign keysunique rules automatically modified on update to ignore the current record's IDFor every exists: and unique: rule the generated controller includes a human-readable message:
$request->validate([
'user_id' => 'required|integer|exists:users,id',
'slug' => 'required|unique:posts',
], [
'user_id.exists' => 'The selected user does not exist in the users table.',
'user_id.required' => 'The user field is required.',
'slug.unique' => 'This slug has already been taken.',
]);
On update, unique rules are automatically adjusted so the current record is excluded from the uniqueness check:
// store
'slug' => 'required|unique:posts'
// update (auto-generated)
'slug' => 'required|unique:posts,slug,' . $post->id
| Context | Generated Rule |
|---|---|
store |
required\|integer\|exists:table,id |
update |
sometimes\|integer\|exists:table,id |
After generation, the command checks the database for every related table. If any are missing it tells you exactly which ones and how to fix them — at generation time, not at runtime:
WARN The following related tables do not exist yet.
Eager loading and FK validation will fail until their migrations are run:
Comment → table comments not found
Run: php artisan make:model Comment -m
Tag → table tags not found
Run: php artisan make:model Tag -m
All three generated files are driven by publishable stubs. Users can override them to match their own code style without touching the package.
| Stub File | Controls |
|---|---|
stubs/crud.migration.stub |
Migration structure |
stubs/crud.model.stub |
Model structure |
stubs/crud.controller.stub |
Controller structure |
php artisan vendor:publish --tag=lara-util-x-stubs
Stubs are published to stubs/vendor/lara-util-x/ and are automatically picked up by the command. The package defaults are used as a fallback if no published stubs are found.
| File | Without --force |
With --force |
|---|---|---|
| Migration | Skipped if any migration for the table exists | Still skipped — migrations are never duplicated |
| Model | Skipped with warning | Overwritten |
| Controller | Skipped with warning | Overwritten |
php artisan make:crud Post \
--fields="title:string:required|max:255,slug:string:required|unique:posts,body:text:required,excerpt:string:nullable,price:decimal:nullable|min:0,is_published:boolean:nullable,published_at:datetime:nullable" \
--belongs-to=User \
--has-many=Comment \
--belongs-to-many=Tag \
--soft-deletes \
--searchable="title,body,excerpt" \
--per-page=10 \
--register-routes \
--migrate \
--force
This single command produces:
user_id foreign key, soft deletes, and timestamps$fillable, $casts, SoftDeletes, and 3 relationship methodsRoute::apiResource('posts', PostController::class) entry appended to routes/api.phpThe abstract CrudController base class is retained for manual use. If you prefer to extend it and wire validation, search, and relationships yourself instead of using make:crud, see the CRUD Controller documentation for the generic controller approach.