Laravel 13 Eloquent 改进详解
摘要
Laravel 13 对 Eloquent ORM 进行了多项改进,包括访问器、修改器和关系定义的增强。本文将深入讲解 Laravel 13 的 Eloquent 改进,包括:
- 访问器与修改器新语法
- 属性转换增强
- 关系定义改进
- 查询优化
- 实战案例与最佳实践
本文适合希望掌握 Laravel 13 Eloquent 新特性的开发者。
1. 访问器与修改器
1.1 访问器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model { protected function fullName(): Attribute { return Attribute::make( get: fn($value, $attributes) => $attributes['first_name'] . ' ' . $attributes['last_name'], ); } protected function avatar(): Attribute { return Attribute::make( get: fn($value) => $value ?? 'default-avatar.png', ); } }
|
1.2 修改器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class User extends Model { protected function password(): Attribute { return Attribute::make( set: fn($value) => bcrypt($value), ); } protected function email(): Attribute { return Attribute::make( set: fn($value) => strtolower($value), ); } }
|
1.3 访问器与修改器组合
1 2 3 4 5 6 7 8 9 10
| class User extends Model { protected function name(): Attribute { return Attribute::make( get: fn($value) => ucfirst($value), set: fn($value) => strtolower($value), ); } }
|
1.4 属性缓存
1 2 3 4 5 6 7 8 9
| class User extends Model { protected function expensiveComputation(): Attribute { return Attribute::make( get: fn($value) => $this->computeExpensiveValue(), )->shouldCache(); } }
|
2. 属性转换
2.1 基本转换
1 2 3 4 5 6 7 8 9 10
| class User extends Model { protected $casts = [ 'is_admin' => 'boolean', 'options' => 'array', 'settings' => 'collection', 'created_at' => 'datetime', 'expires_at' => 'date', ]; }
|
2.2 枚举转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| enum Status: string { case Draft = 'draft'; case Published = 'published'; case Archived = 'archived'; }
class Post extends Model { protected $casts = [ 'status' => Status::class, ]; }
$post->status = Status::Published; echo $post->status->value;
|
2.3 加密转换
1 2 3 4 5 6 7
| class User extends Model { protected $casts = [ 'secret' => 'encrypted', 'api_key' => 'encrypted', ]; }
|
2.4 自定义转换
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
| <?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class Json implements CastsAttributes { public function get($model, string $key, $value, array $attributes) { return json_decode($value, true); } public function set($model, string $key, $value, array $attributes) { return json_encode($value); } }
class Post extends Model { protected $casts = [ 'metadata' => Json::class, ]; }
|
3. 关系定义
3.1 一对一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class User extends Model { public function profile() { return $this->hasOne(Profile::class); } }
class Profile extends Model { public function user() { return $this->belongsTo(User::class); } }
|
3.2 一对多
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 author() { return $this->belongsTo(User::class, 'author_id'); } }
|
3.3 多对多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class User extends Model { public function roles() { return $this->belongsToMany(Role::class) ->withPivot('assigned_at') ->withTimestamps(); } }
class Role extends Model { public function users() { return $this->belongsToMany(User::class); } }
|
3.4 多态关系
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(); } }
|
4. 查询优化
4.1 延迟加载
1 2 3 4 5
| $posts = Post::all(); foreach ($posts as $post) { echo $post->author->name; }
|
4.2 预加载
1 2 3 4 5
| $posts = Post::with('author')->get(); foreach ($posts as $post) { echo $post->author->name; }
|
4.3 嵌套预加载
1
| $posts = Post::with(['author', 'comments.user'])->get();
|
4.4 条件预加载
1 2 3
| $posts = Post::with(['comments' => function ($query) { $query->where('approved', true); }])->get();
|
4.5 懒加载
1 2
| $posts = Post::all(); $posts->load('author', 'comments');
|
5. 模型事件
5.1 事件监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class User extends Model { protected static function booted() { static::creating(function ($user) { $user->api_token = Str::random(60); }); static::updating(function ($user) { if ($user->isDirty('email')) { $user->email_verified_at = null; } }); } }
|
5.2 观察者
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
| <?php
namespace App\Observers;
use App\Models\User;
class UserObserver { public function created(User $user) { } public function updated(User $user) { } public function deleted(User $user) { } }
use App\Models\User; use App\Observers\UserObserver;
User::observe(UserObserver::class);
|
5.3 属性观察者
1 2 3 4 5 6 7
| use Illuminate\Database\Eloquent\Attributes\ObservedBy;
#[ObservedBy(UserObserver::class)] class User extends Model { }
|
6. 实战案例
6.1 完整模型示例
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use App\Observers\UserObserver; use App\Enums\UserStatus;
#[ObservedBy(UserObserver::class)] class User extends Model { protected $fillable = [ 'name', 'email', 'password', 'status', 'settings', ]; protected $hidden = [ 'password', 'remember_token', ]; protected $casts = [ 'status' => UserStatus::class, 'settings' => 'array', 'email_verified_at' => 'datetime', ]; protected function password(): Attribute { return Attribute::make( set: fn($value) => bcrypt($value), ); } protected function avatar(): Attribute { return Attribute::make( get: fn($value) => $value ? Storage::url($value) : asset('images/default-avatar.png'), ); } public function posts(): HasMany { return $this->hasMany(Post::class, 'author_id'); } public function roles(): BelongsToMany { return $this->belongsToMany(Role::class) ->withTimestamps(); } public function scopeActive($query) { return $query->where('status', UserStatus::Active); } public function scopeAdmins($query) { return $query->whereHas('roles', fn($q) => $q->where('name', 'admin')); } }
|
7. 最佳实践
7.1 模型命名
1 2 3 4 5 6 7 8 9
| User Post Comment
Users Posts Comments
|
7.2 关系命名
1 2 3 4 5 6 7
| public function publishedPosts() public function recentComments()
// 不推荐 public function posts2() public function commentsNew()
|
8. 总结
Laravel 13 的 Eloquent 改进提供了更强大的模型定义能力:
- 访问器/修改器:简洁的属性定义
- 属性转换:支持枚举和自定义转换
- 关系定义:灵活的关系类型
- 查询优化:预加载解决 N+1
参考资料