Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build Laravel 10+ apps with Eloquent, Sanctum auth, Horizon queues, Livewire, and RESTful API resources.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/eloquent.md
1# Eloquent ORM23## Model Patterns45```php6<?php78namespace App\Models;910use Illuminate\Database\Eloquent\Model;11use Illuminate\Database\Eloquent\Factories\HasFactory;12use Illuminate\Database\Eloquent\SoftDeletes;13use Illuminate\Database\Eloquent\Casts\Attribute;1415class Post extends Model16{17use HasFactory, SoftDeletes;1819protected $fillable = [20'title',21'slug',22'content',23'published_at',24'user_id',25];2627protected $casts = [28'published_at' => 'datetime',29'metadata' => 'array',30'is_featured' => 'boolean',31];3233// Accessor using new Attribute syntax (Laravel 9+)34protected function title(): Attribute35{36return Attribute::make(37get: fn (string $value) => ucfirst($value),38set: fn (string $value) => strtolower($value),39);40}4142// Mutator for computed property43protected function excerpt(): Attribute44{45return Attribute::make(46get: fn () => str($this->content)->limit(100),47);48}49}50```5152## Relationships5354```php55// One-to-Many56class User extends Model57{58public function posts(): HasMany59{60return $this->hasMany(Post::class);61}6263public function latestPost(): HasOne64{65return $this->hasOne(Post::class)->latestOfMany();66}6768public function oldestPost(): HasOne69{70return $this->hasOne(Post::class)->oldestOfMany();71}72}7374class Post extends Model75{76public function user(): BelongsTo77{78return $this->belongsTo(User::class);79}8081// Inverse relationship82public function comments(): HasMany83{84return $this->hasMany(Comment::class);85}86}8788// Many-to-Many with Pivot89class User extends Model90{91public function roles(): BelongsToMany92{93return $this->belongsToMany(Role::class)94->withPivot('expires_at', 'assigned_by')95->withTimestamps()96->using(RoleUser::class); // Custom pivot model97}98}99100// Has Many Through101class Country extends Model102{103public function posts(): HasManyThrough104{105return $this->hasManyThrough(Post::class, User::class);106}107}108109// Polymorphic Relations110class Image extends Model111{112public function imageable(): MorphTo113{114return $this->morphTo();115}116}117118class Post extends Model119{120public function images(): MorphMany121{122return $this->morphMany(Image::class, 'imageable');123}124}125126// Many-to-Many Polymorphic127class Tag extends Model128{129public function posts(): MorphToMany130{131return $this->morphedByMany(Post::class, 'taggable');132}133134public function videos(): MorphToMany135{136return $this->morphedByMany(Video::class, 'taggable');137}138}139```140141## Query Scopes142143```php144class Post extends Model145{146// Local scope147public function scopePublished($query): void148{149$query->whereNotNull('published_at')150->where('published_at', '<=', now());151}152153public function scopePopular($query, int $threshold = 100): void154{155$query->where('views', '>=', $threshold);156}157158// Global scope159protected static function booted(): void160{161static::addGlobalScope('active', function ($query) {162$query->where('status', 'active');163});164}165}166167// Usage168$posts = Post::published()->popular(500)->get();169170// Custom Scope Class171namespace App\Models\Scopes;172173use Illuminate\Database\Eloquent\Builder;174use Illuminate\Database\Eloquent\Model;175use Illuminate\Database\Eloquent\Scope;176177class AncientScope implements Scope178{179public function apply(Builder $builder, Model $model): void180{181$builder->where('created_at', '<', now()->subYears(10));182}183}184185// Apply in model186protected static function booted(): void187{188static::addGlobalScope(new AncientScope);189}190```191192## Custom Casts193194```php195namespace App\Casts;196197use Illuminate\Contracts\Database\Eloquent\CastsAttributes;198199class Money implements CastsAttributes200{201public function get($model, string $key, $value, array $attributes): float202{203return $value / 100; // Store cents, return dollars204}205206public function set($model, string $key, $value, array $attributes): int207{208return (int) ($value * 100);209}210}211212// In model213protected $casts = [214'price' => Money::class,215];216```217218## Query Optimization219220```php221// Eager Loading (prevent N+1)222$posts = Post::with(['user', 'comments.user'])->get();223224// Lazy Eager Loading225$posts = Post::all();226$posts->load('user');227228// Eager Load with Constraints229$users = User::with(['posts' => function ($query) {230$query->where('published', true)->orderBy('created_at', 'desc');231}])->get();232233// Count relationships efficiently234$posts = Post::withCount('comments')->get();235foreach ($posts as $post) {236echo $post->comments_count;237}238239// Exists checks240$users = User::withExists('posts')->get();241242// Chunk for large datasets243Post::chunk(100, function ($posts) {244foreach ($posts as $post) {245// Process post246}247});248249// Lazy collection for memory efficiency250Post::lazy()->each(function ($post) {251// Process one at a time252});253```254255## Model Events256257```php258class Post extends Model259{260protected static function booted(): void261{262static::creating(function ($post) {263$post->slug = str($post->title)->slug();264});265266static::updating(function ($post) {267if ($post->isDirty('title')) {268$post->slug = str($post->title)->slug();269}270});271272static::deleted(function ($post) {273$post->images()->delete();274});275}276}277278// Using Observers279namespace App\Observers;280281class PostObserver282{283public function creating(Post $post): void284{285$post->user_id = auth()->id();286}287288public function updated(Post $post): void289{290cache()->forget("post.{$post->id}");291}292}293294// Register in AppServiceProvider295use App\Models\Post;296use App\Observers\PostObserver;297298public function boot(): void299{300Post::observe(PostObserver::class);301}302```303304## Advanced Queries305306```php307// Subqueries308$users = User::select(['id', 'name'])309->addSelect(['latest_post_title' => Post::select('title')310->whereColumn('user_id', 'users.id')311->latest()312->limit(1)313])->get();314315// When conditional queries316$posts = Post::query()317->when($search, fn ($query) => $query->where('title', 'like', "%{$search}%"))318->when($category, fn ($query) => $query->where('category_id', $category))319->get();320321// Database transactions322DB::transaction(function () {323$user = User::create([...]);324$user->profile()->create([...]);325$user->assignRole('member');326});327328// Pessimistic locking329$user = User::where('id', 1)->lockForUpdate()->first();330331// Upserts332User::upsert(333[334['email' => '[email protected]', 'name' => 'John'],335['email' => '[email protected]', 'name' => 'Jane'],336],337['email'], // Unique columns338['name'] // Columns to update339);340```341342## Performance Tips3433441. **Always eager load relationships** - Avoid N+1 queries3452. **Use chunking for large datasets** - Prevent memory exhaustion3463. **Index foreign keys** - Speed up joins3474. **Use select() to limit columns** - Reduce data transfer3485. **Cache expensive queries** - Use Redis/Memcached3496. **Use database indexing** - Add indexes in migrations3507. **Avoid using model events for heavy operations** - Use queues instead3518. **Use lazy collections** - For processing large datasets352