Eloquent is Laravel's Active Record ORM: every database table maps to a Model class that provides an expressive, chainable query interface. Generate a model and its migration together with php artisan make:model Post -m; the -f flag also creates a factory and -s a seeder. To insert, update, or delete records, use create(), update(), and delete(), all gated by the \(fillable whitelist or \)guarded blacklist to prevent mass-assignment vulnerabilities. Lookup helpers include find() (returns null if missing), findOrFail() and firstOrFail() (throw a ModelNotFoundException that Laravel converts to a 404), the shorthand firstWhere(), plus whereIn() and whereBetween() for batch filters. Convenience helpers include latest(), oldest(), inRandomOrder(), pluck(), and select() with addSelect() for subqueries.
Eloquent models support rich attribute handling through casts, accessors, mutators, and observer events. The $casts property converts attributes to booleans, arrays, datetimes, decimals, or PHP enums via AsEnum::class. Accessors transform values when reading and mutators transform them when writing, both declared with the modern Attribute::make(get: ..., set: ...) helper. Lifecycle events (including retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, and restored) can be subscribed to via observers or registered directly inside the model's booted() method. The saving event fires before both inserts and updates, creating fires only before inserts, and saved is the safest hook for side effects because it fires on both operations after the row is persisted. To skip events temporarily, use $model->saveQuietly(), Model::withoutEvents(), or Model::unsetEventDispatcher(). Inspecting change state is easy with isDirty('email') (pending changes), isClean(), wasChanged() (after save), and getOriginal('email'). fresh() returns a new instance loaded from the database while leaving the original untouched; refresh() overwrites the same instance. Use replicate(['views_count']) to clone an unsaved instance with a fresh primary key.
Relationships are declared as methods returning hasOne, belongsTo, hasMany, belongsToMany, hasManyThrough, hasOneThrough, morphMany, morphTo, morphToMany, or morphedByMany. Each method follows Laravel conventions for foreign keys (typically {parent}_id) and pivot tables (alphabetical singular, e.g., role_user); pass extra arguments to override. With many-to-many relationships, use attach() to add, detach() to remove, and sync() to make the pivot exactly match a given ID list. Access extra pivot columns via withPivot() and ->pivot->column; add withTimestamps() for automatic timestamp handling. Polymorphic relationships use morphs('commentable') columns on the child table so multiple parents can share the relation. The SoftDeletes trait plus a \(table->softDeletes() migration column enables non-destructive deletion via \)post->delete() and restoration with $post->restore(). Idempotent helpers firstOrCreate(['email' => $x], ['name' => 'A']) insert only when no match exists, while updateOrCreate() updates the matching record or creates one with both attribute sets merged.
Preventing N+1 queries is critical for performance. Eager loading via with('author') fetches relations in batched queries; without it, accessing a relation in a loop fires one query per iteration. Use load() for lazy eager loading when you already have a collection, and withCount('comments') to add a {relation}_count column without loading the relation. Call Model::preventLazyLoading(!app()->isProduction()) to surface lazy-loading violations during development. Filter parent queries by relation existence with whereHas('posts', $cb) (with constraints) or has('posts', '>', 5) (with count operators). Scopes encapsulate reusable query constraints: local scopes are invoked explicitly as User::active(), while global scopes apply automatically to every query on a model and can be removed per query with withoutGlobalScopes(). For very large result sets, use chunk(), the safer chunkById() (stable across mid-iteration modifications), lazy() / lazyById() returning a LazyCollection, or cursor() which runs a single unbuffered query via generators to keep memory flat. For safe raw SQL, use DB::raw() for select fragments and always bind user input with whereRaw('email = ?', [$email]). Collections returned by Eloquent wrap arrays with a chainable API of 100+ methods including map, filter, pluck, groupBy, sum, reduce, sortBy, chunk, unique, first, and last. To auto-generate UUIDs for primary keys, override newUniqueId() in the model, set \(incrementing = false and \)keyType = 'string', and use $table->uuid('id')->primary() in the migration.