API CRUD Generator

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.

make:crud — Artisan CRUD Generator

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.

Command Options

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)

Field Definition Format

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"

Supported Column Types

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.

What Gets Generated

Migration

  • All typed columns from --fields
  • ->nullable() applied when field rule contains nullable
  • ->unique() applied when field rule contains standalone unique
  • foreignId('relation_id')->constrained('table')->cascadeOnDelete() for every --belongs-to
  • $table->softDeletes() when --soft-deletes is set
  • Standard $table->id() and $table->timestamps()

Model

  • Correct 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 set
  • Typed relationship methods for all four relationship types:
public 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

Controller

  • Full apiResource controller with index, show, store, update, destroy
  • Route model binding on show, update, and destroy
  • store returns HTTP 201
  • Pagination with data + meta (current_page, last_page, per_page, total)
  • Search across --searchable fields using ?search=term
  • Sort via ?sort_by=field&sort_direction=asc
  • Configurable ?per_page=N (default from --per-page)
  • Eager loading of all defined relationships on every endpoint
  • Validation rules generated for all --fields and --belongs-to foreign keys
  • unique rules automatically modified on update to ignore the current record's ID

Smart Validation

Custom Error Messages

For 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.',
]);

Update — Unique Ignore

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

FK Validation Rules

Context Generated Rule
store required\|integer\|exists:table,id
update sometimes\|integer\|exists:table,id

Relationship Table Warnings

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

Stub-Based Generation

All three generated files are driven by publishable stubs. Users can override them to match their own code style without touching the package.

Stubs

Stub File Controls
stubs/crud.migration.stub Migration structure
stubs/crud.model.stub Model structure
stubs/crud.controller.stub Controller structure

Publishing Stubs

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.

--force Behaviour

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

Full Example

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:

  • A migration with 7 typed columns, a user_id foreign key, soft deletes, and timestamps
  • A model with $fillable, $casts, SoftDeletes, and 3 relationship methods
  • A controller with full CRUD, search, sort, pagination, eager loading, and smart validation
  • The Route::apiResource('posts', PostController::class) entry appended to routes/api.php
  • The migration executed immediately

Manual CrudController

The 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.