Laravel 13 Eloquent 高级特性
Eloquent 是 Laravel 的 ORM(对象关系映射),提供了优雅的 ActiveRecord 实现。本文将深入探讨 Laravel 13 Eloquent 的高级特性。
模型定义
基础模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use App\Observers\UserObserver;
#[ObservedBy(UserObserver::class)] class User extends Model { use SoftDeletes;
protected $fillable = [ 'name', 'email', 'password', ];
protected $hidden = [ 'password', 'remember_token', ];
protected $casts = [ 'email_verified_at' => 'datetime', 'password' => 'hashed', 'settings' => 'array', 'is_admin' => 'boolean', ]; }
|
自定义主键
1 2 3 4 5 6
| class User extends Model { protected $primaryKey = 'uuid'; public $incrementing = false; protected $keyType = 'string'; }
|
关联关系
一对一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class User extends Model { public function profile() { return $this->hasOne(Profile::class); }
public function latestPost() { return $this->hasOne(Post::class)->latestOfMany(); }
public function oldestPost() { return $this->hasOne(Post::class)->oldestOfMany(); } }
class Profile extends Model { public function user() { return $this->belongsTo(User::class); } }
|
一对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class User extends Model { public function posts() { return $this->hasMany(Post::class); } }
class Post extends Model { public function user() { return $this->belongsTo(User::class); } }
|
多对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class User extends Model { public function roles() { return $this->belongsToMany(Role::class) ->withPivot('assigned_at', 'assigned_by') ->withTimestamps() ->as('subscription'); } }
class Role extends Model { public function users() { return $this->belongsToMany(User::class); } }
|
远程一对一
1 2 3 4 5 6 7
| class User extends Model { public function country() { return $this->hasOneThrough(Country::class, Profile::class); } }
|
远程一对多
1 2 3 4 5 6 7
| class User extends Model { public function comments() { return $this->hasManyThrough(Comment::class, Post::class); } }
|
多态关联
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Post extends Model { public function comments() { return $this->morphMany(Comment::class, 'commentable'); }
public function tags() { return $this->morphToMany(Tag::class, 'taggable'); } }
class Comment extends Model { public function commentable() { return $this->morphTo(); } }
|
查询构建
条件查询
1 2 3 4 5 6 7 8 9 10 11 12 13
| $users = User::where('active', true) ->where('age', '>=', 18) ->whereIn('role', ['admin', 'editor']) ->whereBetween('created_at', [$start, $end]) ->whereNull('deleted_at') ->whereNotNull('email_verified_at') ->whereDate('created_at', '2024-01-01') ->whereYear('created_at', 2024) ->whereMonth('created_at', 1) ->whereDay('created_at', 1) ->whereTime('created_at', '12:00:00') ->whereColumn('updated_at', '>', 'created_at') ->get();
|
高级条件
1 2 3 4 5 6 7 8 9 10
| $users = User::where(function ($query) { $query->where('active', true) ->orWhere('role', 'admin'); })->where('verified', true)->get();
$users = User::whereRelation('posts', 'published', true)->get(); $users = User::whereDoesntHave('posts')->get(); $users = User::whereHas('posts', function ($query) { $query->where('views', '>', 1000); })->get();
|
聚合查询
1 2 3 4 5 6 7
| $count = User::count(); $sum = Order::sum('total'); $avg = Product::avg('price'); $max = Product::max('price'); $min = Product::min('price'); $exists = User::where('email', $email)->exists(); $doesntExist = User::where('email', $email)->doesntExist();
|
预加载
基本预加载
1 2 3
| $posts = Post::with('user')->get(); $posts = Post::with(['user', 'comments'])->get(); $posts = Post::with('user.profile')->get();
|
约束预加载
1 2 3
| $posts = Post::with(['comments' => function ($query) { $query->where('approved', true)->latest(); }])->get();
|
延迟预加载
1 2 3
| $posts = Post::all(); $posts->load('user', 'comments'); $posts->loadMissing('user');
|
预加载计数
1 2 3 4 5
| $posts = Post::withCount('comments')->get(); $posts = Post::withCount(['comments', 'likes'])->get(); $posts = Post::withCount(['comments as pending_comments' => function ($query) { $query->where('approved', false); }])->get();
|
访问器和修改器
访问器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class User extends Model { protected function fullName(): Attribute { return Attribute::make( get: fn (mixed $value, array $attributes) => $attributes['first_name'] . ' ' . $attributes['last_name'] ); }
protected function avatar(): Attribute { return Attribute::make( get: fn (?string $value) => $value ?: 'https://example.com/default-avatar.png' ); } }
|
修改器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class User extends Model { protected function password(): Attribute { return Attribute::make( set: fn (string $value) => bcrypt($value) ); }
protected function name(): Attribute { return Attribute::make( get: fn (string $value) => ucfirst($value), set: fn (string $value) => strtolower($value) ); } }
|
类型转换
基本类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13
| class User extends Model { protected $casts = [ 'is_admin' => 'boolean', 'settings' => 'array', 'options' => 'collection', 'created_at' => 'datetime', 'expires_at' => 'datetime:Y-m-d', 'amount' => 'decimal:2', 'price' => 'encrypted', 'preferences' => 'encrypted:array', ]; }
|
自定义类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class User extends Model { protected $casts = [ 'address' => Address::class, ]; }
class Address implements CastsAttributes { public function get(Model $model, string $key, mixed $value, array $attributes): ?Address { if ($value === null) { return null; }
$data = json_decode($value, true); return new Address($data['street'], $data['city'], $data['zip']); }
public function set(Model $model, string $key, mixed $value, array $attributes): string { return json_encode([ 'street' => $value->street, 'city' => $value->city, 'zip' => $value->zip, ]); } }
|
模型事件
事件钩子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class User extends Model { protected static function booted(): void { static::creating(function (User $user) { $user->uuid = (string) Str::uuid(); });
static::updating(function (User $user) { if ($user->isDirty('email')) { $user->email_verified_at = null; } });
static::deleting(function (User $user) { $user->posts()->delete(); }); } }
|
软删除
1 2 3 4 5 6 7 8 9 10 11 12 13
| class User extends Model { use SoftDeletes;
protected $dates = ['deleted_at']; }
$user->delete(); $user->forceDelete(); $user->restore(); User::withTrashed()->get(); User::onlyTrashed()->get();
|
查询作用域
全局作用域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class ActiveScope implements Scope { public function apply(Builder $builder, Model $model): void { $builder->where('active', true); } }
class User extends Model { protected static function booted(): void { static::addGlobalScope(new ActiveScope); } }
class User extends Model { protected static function booted(): void { static::addGlobalScope('active', function (Builder $builder) { $builder->where('active', true); }); } }
|
本地作用域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class User extends Model { public function scopeActive($query) { return $query->where('active', true); }
public function scopeAdmin($query) { return $query->where('role', 'admin'); }
public function scopeSearch($query, string $term) { return $query->where('name', 'like', "%{$term}%") ->orWhere('email', 'like', "%{$term}%"); } }
$users = User::active()->admin()->get(); $users = User::search('john')->get();
|
集合操作
1 2 3 4 5 6 7 8
| $users = User::all();
$active = $users->filter(fn ($user) => $user->active); $names = $users->map(fn ($user) => $user->name); $sorted = $users->sortBy('name'); $grouped = $users->groupBy('role'); $first = $users->firstWhere('role', 'admin'); $contains = $users->contains('email', 'john@example.com');
|
最佳实践
1. 使用批量赋值保护
1 2 3 4 5
| protected $fillable = ['name', 'email'];
protected $guarded = ['id', 'is_admin'];
|
2. 使用预加载避免 N+1
1 2 3 4 5 6 7 8
| $posts = Post::with('user')->get();
$posts = Post::all(); foreach ($posts as $post) { echo $post->user->name; }
|
3. 使用查询作用域
1 2 3 4 5
| User::active()->verified()->get();
User::where('active', true)->where('verified', true)->get();
|
总结
Laravel 13 的 Eloquent 提供了强大而优雅的 ORM 实现。通过合理使用关联关系、预加载、访问器和修改器、类型转换等特性,可以构建出高效、可维护的数据模型。记住使用预加载避免 N+1 查询问题,使用查询作用域封装常用查询条件,并充分利用 Eloquent 的事件系统来实现业务逻辑。