eloquent-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEloquent Best Practices
Eloquent 最佳实践
Query Optimization
查询优化
Always Eager Load Relationships
始终预加载关联关系
php
// ❌ N+1 Query Problem
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // N additional queries
}
// ✅ Eager Loading
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name; // No additional queries
}php
// ❌ N+1 查询问题
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // 额外执行N次查询
}
// ✅ 预加载
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name; // 无额外查询
}Select Only Needed Columns
仅选择所需列
php
// ❌ Fetches all columns
$users = User::all();
// ✅ Only needed columns
$users = User::select(['id', 'name', 'email'])->get();
// ✅ With relationships
$posts = Post::with(['user:id,name'])->select(['id', 'title', 'user_id'])->get();php
// ❌ 获取所有列
$users = User::all();
// ✅ 仅获取所需列
$users = User::select(['id', 'name', 'email'])->get();
// ✅ 关联关系场景下
$posts = Post::with(['user:id,name'])->select(['id', 'title', 'user_id'])->get();Use Query Scopes
使用查询作用域
php
// ✅ Define reusable query logic
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('status', 'published')
->whereNotNull('published_at');
}
public function scopePopular($query, $threshold = 100)
{
return $query->where('views', '>', $threshold);
}
}
// Usage
$posts = Post::published()->popular()->get();php
// ✅ 定义可复用的查询逻辑
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('status', 'published')
->whereNotNull('published_at');
}
public function scopePopular($query, $threshold = 100)
{
return $query->where('views', '>', $threshold);
}
}
// 使用示例
$posts = Post::published()->popular()->get();Relationship Best Practices
关联关系最佳实践
Define Return Types
定义返回类型
php
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Post extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}php
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Post extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}Use withCount for Counts
使用withCount高效统计
php
// ❌ Triggers additional queries
foreach ($posts as $post) {
echo $post->comments()->count();
}
// ✅ Load counts efficiently
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}php
// ❌ 触发额外查询
foreach ($posts as $post) {
echo $post->comments()->count();
}
// ✅ 高效加载统计数据
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}Mass Assignment Protection
批量赋值保护
php
class Post extends Model
{
// ✅ Whitelist fillable attributes
protected $fillable = ['title', 'content', 'status'];
// Or blacklist guarded attributes
protected $guarded = ['id', 'user_id'];
// ❌ Never do this
// protected $guarded = [];
}php
class Post extends Model
{
// ✅ 白名单可填充属性
protected $fillable = ['title', 'content', 'status'];
// 或黑名单不可填充属性
protected $guarded = ['id', 'user_id'];
// ❌ 绝对不要这样做
// protected $guarded = [];
}Use Casts for Type Safety
使用类型转换保证类型安全
php
class Post extends Model
{
protected $casts = [
'published_at' => 'datetime',
'metadata' => 'array',
'is_featured' => 'boolean',
'views' => 'integer',
];
}php
class Post extends Model
{
protected $casts = [
'published_at' => 'datetime',
'metadata' => 'array',
'is_featured' => 'boolean',
'views' => 'integer',
];
}Chunking for Large Datasets
大数据集分块处理
php
// ✅ Process in chunks to save memory
Post::chunk(200, function ($posts) {
foreach ($posts as $post) {
// Process each post
}
});
// ✅ Or use lazy collections
Post::lazy()->each(function ($post) {
// Process one at a time
});php
// ✅ 分块处理以节省内存
Post::chunk(200, function ($posts) {
foreach ($posts as $post) {
// 处理单条数据
}
});
// ✅ 或使用惰性集合
Post::lazy()->each(function ($post) {
// 逐条处理
});Database-Level Operations
数据库层面操作
php
// ❌ Slow - loads into memory first
$posts = Post::where('status', 'draft')->get();
foreach ($posts as $post) {
$post->update(['status' => 'archived']);
}
// ✅ Fast - single query
Post::where('status', 'draft')->update(['status' => 'archived']);
// ✅ Increment/decrement
Post::where('id', $id)->increment('views');php
// ❌ 速度慢 - 先加载到内存
$posts = Post::where('status', 'draft')->get();
foreach ($posts as $post) {
$post->update(['status' => 'archived']);
}
// ✅ 速度快 - 单条查询
Post::where('status', 'draft')->update(['status' => 'archived']);
// ✅ 自增/自减
Post::where('id', $id)->increment('views');Use Model Events Wisely
合理使用模型事件
php
class Post extends Model
{
protected static function booted()
{
static::creating(function ($post) {
$post->slug = Str::slug($post->title);
});
static::deleting(function ($post) {
$post->comments()->delete();
});
}
}php
class Post extends Model
{
protected static function booted()
{
static::creating(function ($post) {
$post->slug = Str::slug($post->title);
});
static::deleting(function ($post) {
$post->comments()->delete();
});
}
}Common Pitfalls to Avoid
需避免的常见陷阱
Don't Query in Loops
不要在循环中查询
php
// ❌ Bad
foreach ($userIds as $id) {
$user = User::find($id);
}
// ✅ Good
$users = User::whereIn('id', $userIds)->get();php
// ❌ 错误做法
foreach ($userIds as $id) {
$user = User::find($id);
}
// ✅ 正确做法
$users = User::whereIn('id', $userIds)->get();Don't Forget Indexes
不要忘记添加索引
php
// Migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->index();
$table->string('slug')->unique();
$table->string('status')->index();
$table->timestamp('published_at')->nullable()->index();
// Composite index for common queries
$table->index(['status', 'published_at']);
});php
// 迁移文件
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->index();
$table->string('slug')->unique();
$table->string('status')->index();
$table->timestamp('published_at')->nullable()->index();
// 为常用查询创建复合索引
$table->index(['status', 'published_at']);
});Prevent Lazy Loading in Development
开发环境禁用懒加载
php
// In AppServiceProvider boot method
Model::preventLazyLoading(!app()->isProduction());php
// 在AppServiceProvider的boot方法中
Model::preventLazyLoading(!app()->isProduction());Checklist
检查清单
- Relationships eagerly loaded where needed
- Only selecting required columns
- Using query scopes for reusability
- Mass assignment protection configured
- Appropriate casts defined
- Indexes on foreign keys and query columns
- Using database-level operations when possible
- Chunking for large datasets
- Model events used appropriately
- Lazy loading prevented in development
- 按需预加载关联关系
- 仅选择所需列
- 使用查询作用域实现复用
- 配置批量赋值保护
- 定义合适的类型转换
- 为外键和查询列添加索引
- 尽可能使用数据库层面操作
- 大数据集使用分块处理
- 合理使用模型事件
- 开发环境禁用懒加载