Introduction
Artisan generates Eloquent models with sensible defaults — but you can override every convention (table name, primary key, timestamps, connection) when your schema doesn't match the expected shape.
Key Concepts
make:modelgenerator: The Artisan command that scaffolds a new model, optionally with a migration, factory, seeder, and controller.$table/$primaryKey: Properties that override the inferred table name and primary key column.HasUuids/HasUlidstraits: Enable UUID or ULID primary keys instead of auto-incrementing integers.$fillable/$guarded: Allow-lists (or block-lists) controlling which attributes can be mass-assigned.- Strict mode:
Model::preventLazyLoading,preventSilentlyDiscardingAttributes,preventAccessingMissingAttributes— development helpers that throw on common mistakes.
Real World Context
Every new entity in your app starts as a php artisan make:model command. Knowing the flags (-mfsc, --all, -mfscr --api) saves hours across a project lifecycle, and getting the conventions right up front prevents schema/model drift.
Deep Dive
Learn how to generate models, configure them, and customize Eloquent's conventions for your specific needs.
Generating Models
Use Artisan to create models:
bash# Basic model php artisan make:model Post # Model with migration php artisan make:model Post -m # Model with migration, factory, seeder, and controller php artisan make:model Post -mfsc # Model with all related files php artisan make:model Post --all # API resource controller php artisan make:model Post -mfscr --api
Basic Model Structure
php<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; }
Customizing Table Names
Override the default table name:
phpclass Post extends Model { /** * The table associated with the model. */ protected $table = 'blog_posts'; }
Customizing Primary Keys
phpclass Post extends Model { /** * The primary key for the model. */ protected $primaryKey = 'post_id'; /** * Indicates if the primary key is auto-incrementing. */ public $incrementing = false; /** * The data type of the primary key. */ protected $keyType = 'string'; // For UUIDs }
Using UUIDs as Primary Keys
phpuse Illuminate\Database\Eloquent\Concerns\HasUuids; class Post extends Model { use HasUuids; // Eloquent automatically generates UUIDs } // Or use ULIDs (Universally Unique Lexicographically Sortable Identifier) use Illuminate\Database\Eloquent\Concerns\HasUlids; class Post extends Model { use HasUlids; }
Timestamps Configuration
phpclass Post extends Model { /** * Disable timestamps entirely. */ public $timestamps = false; /** * Customize timestamp column names. */ const CREATED_AT = 'creation_date'; const UPDATED_AT = 'last_updated'; }
Database Connection
Use a different database connection:
phpclass Post extends Model { /** * The database connection for the model. */ protected $connection = 'mysql_secondary'; }
Default Attribute Values
phpclass Post extends Model { /** * Default values for attributes. */ protected $attributes = [ 'status' => 'draft', 'is_featured' => false, ]; } // Usage $post = new Post; $post->status; // 'draft' (default)
Strict Mode (Recommended)
Enable strict mode to catch issues early:
php// In AppServiceProvider boot() use Illuminate\Database\Eloquent\Model; public function boot(): void { // Prevent lazy loading (use eager loading instead) Model::preventLazyLoading(! $this->app->isProduction()); // Prevent silently discarding attributes Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); // Prevent accessing missing attributes Model::preventAccessingMissingAttributes(! $this->app->isProduction()); }
Mass Assignment Protection
Control which attributes can be mass-assigned:
phpclass Post extends Model { /** * Attributes that ARE mass assignable. */ protected $fillable = [ 'title', 'body', 'category_id', ]; } // Or specify which are NOT mass assignable class Post extends Model { /** * Attributes that are NOT mass assignable. */ protected $guarded = [ 'id', 'is_admin', ]; } // In a different model, you might turn guarding off entirely (be careful — this // is almost never a good idea outside of internal admin tooling): class LegacyImport extends Model { protected $guarded = []; }
Why Mass Assignment Protection?
php// Without protection, malicious users could do: // POST /users with {"name": "John", "is_admin": true} User::create($request->all()); // is_admin would be set! // With $fillable = ['name', 'email']: User::create($request->all()); // is_admin is ignored
Complete Model Example
php<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Post extends Model { use HasFactory, SoftDeletes; /** * The attributes that are mass assignable. */ protected $fillable = [ 'title', 'slug', 'body', 'published_at', 'user_id', 'category_id', ]; /** * The attributes that should be hidden for arrays/JSON. */ protected $hidden = [ 'internal_notes', ]; /** * Get the attributes that should be cast. */ protected function casts(): array { return [ 'published_at' => 'datetime', 'is_featured' => 'boolean', 'metadata' => 'array', ]; } }
Common Pitfalls
- Disabling mass assignment with
$guarded = []— Everything becomes mass-assignable, including columns you never expected users to set. - Mismatched primary keys with UUID/ULID without the trait — The column is VARCHAR but Eloquent still tries to auto-increment it, leading to errors.
- Forgetting strict mode in development — Subtle lazy-loading bugs survive into production.
Best Practices
- Generate related files with
-mfscflags — Model, migration, factory, seeder, controller in one command. - Always declare
$fillableexplicitly — Never rely on$guarded = []. - Enable strict mode — In
AppServiceProvider::boot(), gate on!$this->app->isProduction().
Summary
php artisan make:model Namegenerates a model; add flags for related files.- Override conventions with
$table,$primaryKey,$incrementing,$keyType,$timestamps,$connection. HasUuids/HasUlidstraits handle non-integer primary keys.$fillable(allow-list) or$guarded(block-list) protects against mass assignment.- Strict mode helpers catch lazy loading and attribute mistakes in development.