关于 eloquent-best-practices
eloquent-best-practices 是面向 Laravel 开发者的 Eloquent 实践指南技能,涵盖查询优化、关系处理、安全更新和大数据集处理。
主要功能
- 01预加载关系并选择所需列
- 02复用查询范围
- 03关系返回类型与计数
- 04批量赋值保护与类型转换
- 05分块处理与数据库级更新
- 06索引和延迟加载检查
适用场景
- 01减少模型流程中的 N+1 查询
- 02以较低内存处理大型数据集
- 03检查模型安全性与类型处理
- 04通过索引改善查询性能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ iserter/ laravel-claude-agents --skill eloquent-best-practices安装后在 Claude Code 中输入/eloquent-best-practices确认已加载。
详细介绍
Eloquent 最佳实践
查询优化
始终预加载关联
// ❌ 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
}
仅选择所需的列
// ❌ 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();
使用查询作用域
// ✅ 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();
关联最佳实践
定义返回类型
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);
}
}
使用 withCount 统计数量
// ❌ 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;
}
防止批量赋值漏洞
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 = [];
}
使用类型转换确保类型安全
class Post extends Model
{
protected $casts = [
'published_at' => 'datetime',
'metadata' => 'array',
'is_featured' => 'boolean',
'views' => 'integer',
];
}
对大型数据集进行分块处理
// ✅ 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
});
数据库级操作
// ❌ 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');
谨慎使用模型事件
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();
});
}
}
应避免的常见问题
不要在循环中查询
// ❌ Bad
foreach ($userIds as $id) {
$user = User::find($id);
}
// ✅ Good
$users = User::whereIn('id', $userIds)->get();
不要忘记索引
// 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']);
});
在开发环境中禁止延迟加载
// In AppServiceProvider boot method
Model::preventLazyLoading(!app()->isProduction());
检查清单
- 在需要时预加载关联
- 仅选择所需的列
- 使用查询作用域以便复用
- 已配置批量赋值保护
- 已定义适当的类型转换
- 在外键和查询列上建立索引
- 尽可能使用数据库级操作
- 对大型数据集进行分块处理
- 适当使用模型事件
- 在开发环境中禁止延迟加载
评论
支持 Markdown