模型与查询构建器
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read182 words

模型与查询构建器

Eloquent 是 Laravel 的 ORM,把数据库表映射成 PHP 类。它比裸 SQL 更安全(自动参数绑定)、更易读,但如果不了解它的工作方式,很容易写出产生几百条查询的代码。


创建模型

# 同时创建模型、Migration、Factory、Seeder、Controller
php artisan make:model Task -mfsc
# 只创建模型和 Migration
php artisan make:model Task -m
<?php
// app/Models/Task.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Task extends Model
{
use HasFactory, SoftDeletes;
// 允许批量赋值的字段
protected $fillable = [
'title', 'description', 'status', 'priority',
'due_date', 'user_id', 'team_id',
];
// 隐藏字段(toArray()/toJson() 时不包含)
protected $hidden = ['deleted_at'];
// 类型转换(自动转换从数据库读取的值)
protected $casts = [
'due_date'     => 'date',           // Carbon 对象
'completed_at' => 'datetime',       // Carbon 对象
'is_pinned'    => 'boolean',
'metadata'     => 'array',          // JSONB 字段
'priority'     => 'integer',
];
// Laravel 11 推荐:改用 casts() 方法(支持枚举)
protected function casts(): array
{
return [
'status' => TaskStatus::class,  // PHP 8.1 枚举
];
}
}

基础 CRUD

// 创建
$task = Task::create([
'title'   => '完成项目报告',
'status'  => 'pending',
'user_id' => auth()->id(),
]);
// 查询单条
$task = Task::find(42);             // 返回 null 或模型
$task = Task::findOrFail(42);       // 找不到抛 404 异常
$task = Task::firstOrCreate(       // 找到返回,找不到创建
['title' => '待办', 'user_id' => 1],
['status' => 'pending']
);
// 更新
$task->update(['status' => 'done']);
// 或者:
$task->status = 'done';
$task->save();
// 软删除(需要 use SoftDeletes)
$task->delete();                    // 设置 deleted_at,不真实删除
Task::withTrashed()->find(42);      // 包含软删除的查询
Task::onlyTrashed()->get();         // 只查软删除的
$task->restore();                   // 恢复软删除
$task->forceDelete();               // 真实删除

查询构建器

// 链式查询
$tasks = Task::query()
->where('user_id', auth()->id())
->where('status', 'pending')
->where('priority', '>=', 3)
->whereNotNull('due_date')
->orderBy('due_date')
->limit(10)
->get();
// 条件性查询(when():条件为真时才应用)
$tasks = Task::query()
->when($request->status, fn($q, $s) => $q->where('status', $s))
->when($request->search, fn($q, $s) => $q->where('title', 'like', "%{$s}%"))
->when($request->due_before, fn($q, $d) => $q->where('due_date', '<=', $d))
->paginate(20);
// 原始 SQL 表达式(谨慎使用)
$tasks = Task::whereRaw('EXTRACT(month FROM due_date) = ?', [now()->month])->get();
$tasks = Task::selectRaw('*, EXTRACT(epoch FROM due_date - now()) AS seconds_until_due')
->orderByRaw('seconds_until_due ASC NULLS LAST')
->get();

查询作用域(Scope)

作用域让常用查询条件变成可复用的方法:

// app/Models/Task.php
// 本地作用域(方法名以 scope 开头)
public function scopePending($query)
{
return $query->where('status', 'pending');
}
public function scopeForUser($query, int $userId)
{
return $query->where('user_id', $userId);
}
public function scopeDueSoon($query, int $days = 3)
{
return $query->whereNotNull('due_date')
->whereBetween('due_date', [today(), today()->addDays($days)]);
}
public function scopeHighPriority($query)
{
return $query->where('priority', '>=', 4);
}
// 全局作用域(自动应用到所有查询)
protected static function booted(): void
{
static::addGlobalScope('active_user', function ($query) {
if (auth()->check()) {
$query->where('user_id', auth()->id());
}
});
}
// 使用作用域
$tasks = Task::pending()->forUser(auth()->id())->dueSoon()->get();
$tasks = Task::highPriority()->latest()->paginate(10);
// 绕过全局作用域
$allTasks = Task::withoutGlobalScope('active_user')->get();

模型事件与观察者

php artisan make:observer TaskObserver --model=Task
<?php
// app/Observers/TaskObserver.php
namespace App\Observers;
use App\Models\Task;
class TaskObserver
{
public function creating(Task $task): void
{
// 创建前自动设置默认值
$task->status = $task->status ?? 'pending';
}
public function created(Task $task): void
{
// 创建后发送通知
$task->user->notify(new TaskCreatedNotification($task));
}
public function updated(Task $task): void
{
// 状态变更时触发事件
if ($task->wasChanged('status') && $task->status === 'done') {
event(new \App\Events\TaskCompleted($task));
}
}
public function deleting(Task $task): void
{
// 删除时级联删除附件
$task->attachments()->delete();
}
}
// 在 AppServiceProvider::boot() 中注册:
Task::observe(TaskObserver::class);

常用集合操作

// Eloquent 返回 Collection,支持链式操作(在 PHP 内存中,不是 SQL)
$tasks = Task::all();
$pendingCount  = $tasks->where('status', 'pending')->count();
$byPriority    = $tasks->groupBy('priority');
$titles        = $tasks->pluck('title');           // ['任务A', '任务B', ...]
$highPriority  = $tasks->filter(fn($t) => $t->priority >= 4);
$totalHours    = $tasks->sum('estimated_hours');
$firstDue      = $tasks->sortBy('due_date')->first();
// 大数据集:lazy() 避免一次性加载所有行
Task::lazy()->each(function (Task $task) {
// 每次只加载一批到内存
ProcessTask::dispatch($task);
});
// chunk():分批处理(适合队列 Job)
Task::chunk(500, function ($tasks) {
foreach ($tasks as $task) {
// 处理 500 条,然后继续下 500 条
}
});

下一节关联关系:一对多、多对多、多态——Eloquent 的关联是 ORM 最强大也最容易写错的部分。hasManybelongsToManymorphTo——以及它们对应的 N+1 问题解决方案。