Laravel 11 Eloquent模型定义

Eloquent 是 Laravel 自带的 ORM(对象关系映射),让你能够使用优雅的 ActiveRecord 模式与数据库交互。 每个数据库表都对应一个「模型」,通过模型你可以轻松地查询、插入、更新和删除数据。 本章将系统讲解如何定义 Eloquent 模型,并配置其与数据库的映射关系。

🎯 什么是 Eloquent 模型?

模型通常位于 app/Models 目录(Laravel 11 默认位置)。每个模型对应一张数据库表,你可以在模型中定义表名、主键、可填充字段、关联关系等。 Eloquent 提供了丰富的功能,让数据库操作变得简单而强大。

📦 创建模型

使用 Artisan 命令 make:model 快速创建模型:

php artisan make:model Post

该命令会在 app/Models 目录下生成 Post.php 文件:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // 模型相关配置
}

常用选项:

  • --migration 同时生成迁移文件
  • --controller 生成对应的控制器
  • --factory 生成模型工厂
  • --all 生成模型、迁移、控制器、工厂、资源等全套文件

📌 模型约定

Eloquent 遵循「约定优于配置」的原则,通过简单的命名规则自动推断表名、主键等。

1. 表名

默认情况下,模型类名的复数形式作为表名(小写+下划线)。例如 Post 模型对应 posts 表。如需自定义,设置 $table 属性:

protected $table = 'blog_posts';
2. 主键

默认主键是 id,且为自增整数。可通过 $primaryKey 自定义:

protected $primaryKey = 'post_id';

如果主键不是自增的,需设置 $incrementing = false;如果主键不是整数,需设置 $keyType = 'string'

3. 时间戳

Eloquent 默认期望表中存在 created_atupdated_at 字段。如果不需要自动维护,设置 $timestamps = false

public $timestamps = false;

自定义时间戳字段名:

const CREATED_AT = 'created';
const UPDATED_AT = 'updated';
4. 数据库连接

默认使用 config/database.php 中的默认连接。可通过 $connection 属性指定其他连接:

protected $connection = 'mysql_secondary';
💡 提示: 如果表名或字段名不符合约定,只需在模型中覆盖对应属性即可,Eloquent 会尊重你的配置。

🔒 可填充字段与守卫字段

为了安全,Eloquent 默认不允许批量赋值。你需要通过 $fillable$guarded 属性指定哪些字段可以被批量赋值。

$fillable 白名单
protected $fillable = ['title', 'content', 'user_id'];

允许批量赋值的字段列表。

$guarded 黑名单
protected $guarded = ['id', 'created_at', 'updated_at'];

禁止批量赋值的字段。如果 $guarded 为空数组,表示所有字段都可批量赋值。不推荐使用 $guarded = [],除非你完全信任用户输入。

⚠️ 注意: 批量赋值防护是安全的重要措施,一定要配置 $fillable$guarded,防止恶意用户修改不应该修改的字段。

🔄 属性类型转换

通过 $casts 属性可以自动将数据库字段转换为 PHP 类型,简化数据处理:

protected $casts = [
    'is_published' => 'boolean',
    'price' => 'decimal:2',
    'options' => 'array',       // JSON 字段自动转为数组
    'published_at' => 'datetime',
    'metadata' => 'json',
    'views' => 'integer',
];

支持的类型:integerrealfloatdoublestringbooleanobjectarraycollectiondatedatetimetimestampencrypted 等。

Laravel 11 还支持自定义类型转换,通过实现 Illuminate\Contracts\Database\Eloquent\CastsAttributes 接口。

📅 日期格式化

Eloquent 会自动将 created_atupdated_at 以及通过 $casts 标记为 datetime 的字段转换为 Carbon 实例。可以通过 $dates 属性(Laravel 11 推荐使用 $casts)或 serializeDate 方法自定义格式:

protected function serializeDate(DateTimeInterface $date)
{
    return $date->format('Y-m-d H:i:s');
}

🔗 关联关系

Eloquent 提供了多种关联关系,让你轻松处理表间关系。以下是最常用的几种:

1. 一对一(hasOne / belongsTo)
// User 模型
public function profile()
{
    return $this->hasOne(Profile::class);
}

// Profile 模型
public function user()
{
    return $this->belongsTo(User::class);
}
2. 一对多(hasMany / belongsTo)
// Post 模型
public function comments()
{
    return $this->hasMany(Comment::class);
}

// Comment 模型
public function post()
{
    return $this->belongsTo(Post::class);
}
3. 多对多(belongsToMany)
// User 模型
public function roles()
{
    return $this->belongsToMany(Role::class)->withTimestamps();
}
4. 其他关联

还有 hasManyThroughhasOneThroughmorphMany(多态关联)等高级关联,可根据业务需求使用。

💡 提示: 关联关系的详细用法将在后续章节深入讲解,此处仅作简介。

🔍 模型作用域

作用域允许你定义通用的查询约束,提高代码复用性。

本地作用域

在模型中以 scope 前缀定义方法:

public function scopePublished($query)
{
    return $query->where('is_published', true);
}

public function scopeOfUser($query, $userId)
{
    return $query->where('user_id', $userId);
}

使用时:Post::published()->ofUser(1)->get();

全局作用域

用于自动为所有查询添加约束,例如软删除。可通过实现 Illuminate\Database\Eloquent\Scope 接口定义,或在模型的 booted 方法中注册:

protected static function booted()
{
    static::addGlobalScope('age', function (Builder $builder) {
        $builder->where('age', '>', 18);
    });
}

📡 模型事件与观察者

Eloquent 模型触发多种事件,可以在模型生命周期的关键时刻执行代码。

内置事件

支持的模型事件:retrievedcreatingcreatedupdatingupdatedsavingsaveddeletingdeletedrestoringrestoredreplicating

可以在模型的 booted 方法中注册事件:

protected static function booted()
{
    static::created(function ($user) {
        Log::info("新用户注册: {$user->email}");
    });
}
观察者

对于复杂的事件处理,可以使用观察者类将逻辑集中管理:

php artisan make:observer UserObserver --model=User

App\Providers\EventServiceProvider 中注册观察者:

use App\Models\User;
use App\Observers\UserObserver;

public function boot(): void
{
    User::observe(UserObserver::class);
}

🎨 实战示例:完整的博客文章模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model
{
    use SoftDeletes; // 软删除

    protected $table = 'posts';

    protected $fillable = [
        'title',
        'slug',
        'content',
        'user_id',
        'is_published',
        'published_at',
    ];

    protected $casts = [
        'is_published' => 'boolean',
        'published_at' => 'datetime',
    ];

    // 关联用户
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    // 关联评论
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }

    // 本地作用域:已发布的文章
    public function scopePublished($query)
    {
        return $query->where('is_published', true);
    }

    // 事件:创建时自动生成 slug
    protected static function booted()
    {
        static::creating(function ($post) {
            $post->slug = \Str::slug($post->title);
        });
    }
}
💡 最佳实践: 保持模型轻量,将复杂的业务逻辑提取到服务类或观察者中。合理使用 $fillable$casts 可以大大提高代码的安全性和可读性。

❓ 常见问题

Laravel 11 默认将模型放在 app/Models 目录。你可以自定义命名空间和目录,但需确保 composer.json 中的自动加载配置正确。

可以,但通常只使用其中一种。建议使用 $fillable 显式列出允许的字段,更安全清晰。

设置 $incrementing = false,并可能需要设置 $keyType = 'string'(如果主键不是整数)。

📝 小结

Eloquent 模型是 Laravel 数据库操作的核心,通过简单的配置即可实现复杂的业务逻辑。 熟练掌握模型定义,可以让你充分利用 Laravel 的强大功能,写出优雅、安全、可维护的代码。 下一章我们将深入学习 Eloquent 的增删改查操作,带你体验极简的数据交互。