Laravel Eloquent ORM 是 Laravel 框架中最常用的数据库交互方式之一。它的核心思想是让开发者通过 PHP 对象来操作数据表,而不是直接编写 SQL 语句。每个数据库表通常对应一个模型类,而表中的每一行记录则对应模型的一个实例。这种映射关系使得代码更贴近业务逻辑,也大幅降低了 SQL 注入、字段拼写错误等问题的发生概率。Eloquent 并不是一个简单的查询构造器封装,它在 ActiveRecord 模式基础上加入了许多实用特性,比如关联关系自动维护、时间戳自动管理、访问器与修改器、查询作用域等。

要使用 Eloquent,首先需要创建一个继承自 Illuminate\Database\Eloquent\Model 的类,并指定对应的表名、主键、可填充字段等。下面是一个典型的 User 模型示例,展示了如何通过模型完成基础查询和插入操作。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
// 指定数据表名,默认是模型名的复数形式 users
protected $table = 'users';
// 主键字段,默认是 id
protected $primaryKey = 'id';
// 是否自动维护 created_at 和 updated_at 时间戳
public $timestamps = true;
// 允许批量赋值的字段
protected $fillable = ['name', 'email', 'password'];
}
创建好模型后,就可以通过静态方法或实例方法进行数据库操作。例如获取所有用户、根据主键查找用户、创建新用户等。Eloquent 提供的查询接口非常丰富,很多操作都可以链式调用,并且返回的结果通常是 Collection 对象,方便进行集合操作。
Eloquent ORM 的核心概念与基础用法
理解模型与数据表的映射规则是使用 Eloquent 的第一步。默认情况下,Eloquent 会假设表名是模型类名的蛇形复数形式,比如 User 对应 users,Post 对应 posts。如果表名不符合这个规律,就需要在模型中使用 $table 属性显式指定。主键默认是自增的 id 字段,如果需要使用 UUID 或复合主键,可以通过 $primaryKey 和 $keyType 等属性修改。自动时间戳功能默认开启,会在插入和更新记录时自动填充 created_at 和 updated_at 字段。
批量赋值是很多开发者容易出错的地方。如果没有在 $fillable 中列出允许赋值的字段,直接使用 create 方法或 fill 方法会抛出 MassAssignmentException。这实际上是框架为了防止用户恶意传入额外字段而设计的安全机制。你可以选择使用 $fillable 白名单,或者使用 $guarded 黑名单。在实际项目中,推荐使用白名单方式,明确列出所有允许客户端写入的字段,这样更安全也更容易维护。
下面通过一个简单示例展示如何使用 Eloquent 完成常见的增删改查操作。代码中使用了查询构建器的链式调用,并且通过 get 方法获取结果集。
use App\Models\User;
// 查询所有用户
$users = User::all();
// 根据主键查找用户
$user = User::find(1);
// 添加查询条件并获取第一条记录
$admin = User::where('email', 'admin@ipipp.com')->first();
// 创建新用户
$newUser = User::create([
'name' => '张三',
'email' => 'zhangsan@ipipp.com',
'password' => bcrypt('secret')
]);
// 更新用户信息
$user = User::find(1);
$user->name = '李四';
$user->save();
// 删除用户
$user->delete();
需要注意的是,Eloquent 的查询构造器在真正需要数据之前不会执行 SQL 语句,这种延迟加载机制可以让你灵活地构建查询条件。例如,你可以在循环中不断添加 where 条件,最后再通过 get 或 first 方法触发执行。此外,Eloquent 还提供了 chunk 和 cursor 方法用于处理大量数据,避免一次性加载过多记录导致内存溢出。
模型关联与预加载策略
模型关联是 Eloquent 最强大的特性之一。它允许你以面向对象的方式定义表之间的关系,比如一对一、一对多、多对多、多态关联等。定义好关联方法后,就可以像访问对象属性一样获取关联数据。例如,一个用户可以有多篇文章,那么在 User 模型中定义 posts 方法,返回 hasMany 关联即可。
下面这段代码展示了如何在模型类中定义常用的关联关系。Post 模型属于一个 User,User 模型拥有多个 Post。这种关系在查询时可以非常方便地加载。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title', 'content', 'user_id'];
// 一篇文章属于一个用户
public function user()
{
return $this->belongsTo(User::class);
}
// 一篇文章有多个评论
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class User extends Model
{
protected $fillable = ['name', 'email'];
// 一个用户有多篇文章
public function posts()
{
return $this->hasMany(Post::class);
}
}
在实际查询关联数据时,如果直接访问 $user->posts,Eloquent 会执行一次额外的 SQL 查询。当你在循环中处理多个用户并访问他们的文章时,就会产生经典的 N+1 查询问题:首先查询出 N 个用户,然后对每个用户再查询一次文章,总共执行 N+1 次 SQL。对于数据量较大的场景,这会严重拖慢响应速度。解决方法是使用 with 方法进行预加载,一次性查询出所有关联数据,减少数据库请求次数。
预加载的使用非常简单,只需要在查询模型时调用 with 并传入关联方法名。例如 $users = User::with('posts')->get(); 这样会在查询用户的同时,通过 IN 查询一次性获取所有相关文章,然后自动匹配到对应的用户模型上。你还可以使用嵌套预加载,比如 with('posts.comments'),同时加载文章和文章的评论。对于只需要部分字段的场景,可以使用 select 方法限制关联查询的字段,进一步减少数据传输量。
查询作用域、访问器与性能优化
查询作用域允许你把常用的查询条件封装到模型中,避免在多个地方重复写相同的 where 逻辑。定义一个查询作用域只需要在模型中添加一个以 scope 开头的方法,然后在调用时省略 scope 前缀即可。局部作用域接收查询构建器实例作为第一个参数,你可以在其中添加任意查询条件。全局作用域则是在模型启动时自动应用到所有查询上,适用于软删除、多租户等场景。
下面是一个局部查询作用域的示例,用来筛选出已发布的文章。通过这种方式,控制器中的代码可以变得更加简洁和语义化。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class Post extends Model
{
protected $fillable = ['title', 'content', 'status'];
// 局部查询作用域:只查询已发布的文章
public function scopePublished(Builder $query)
{
return $query->where('status', 'published');
}
// 局部查询作用域:只查询置顶文章
public function scopeFeatured(Builder $query)
{
return $query->where('is_featured', true);
}
}
// 使用方式
$posts = Post::published()->featured()->get();
访问器和修改器则允许你在获取或设置模型属性时自动进行格式转换。比如数据库中以纯文本存储密码,但在输出时希望隐藏敏感信息,或者日期字段需要统一格式。定义访问器的方法名使用 get 前缀加驼峰字段名加 Attribute 后缀,修改器则使用 set 前缀。这样可以避免在业务代码中散落重复的格式化逻辑,让模型成为数据转换的单一入口。
性能优化方面,除了使用预加载解决 N+1 问题外,还需要注意索引的使用。Eloquent 不会自动为所有查询条件创建索引,你需要根据实际查询模式在数据库层面添加合适的索引。通常建议为外键字段和常用查询条件字段建立索引。另外,当只需要几条记录时,使用 limit 限制返回数量;当处理大批量数据时,使用 chunk 方法分段处理。对于复杂的统计查询,如果关联太过复杂,可以考虑使用查询构造器或原生 SQL 替代 Eloquent,因为 ORM 抽象有时会生成低效的 SQL。了解何时使用 ORM、何时降级到查询构造器,是每个 Laravel 开发者都需要掌握的平衡技巧。
最后需要提醒的是,Eloquent 虽然强大,但并不是所有场景都适合。对于需要极高吞吐量的接口,直接使用查询构造器或缓存可以避免 ORM 带来的额外开销。理解 Eloquent 的内部行为,比如模型的隐藏属性、类型转换、事件触发等,能够帮助你在享受开发效率的同时保持代码的性能水平。
Laravel EloquentPHP ORM数据库操作修改时间:2026-09-26 15:53:07