ThinkPHP 面试精讲:模型、关联与事务高频面试题

ThinkPHP 作为国内使用最广泛的 PHP 框架之一,几乎是 PHP 中高级岗位面试的必考内容。本文聚焦模型、关联和事务三大高频考点,结合面试官常问的问题和实际代码场景,帮你系统梳理知识盲区,从容应对技术面。

一、模型篇:不只是 CURD 的封装

1. 模型和 Db 类的区别是什么?什么时候用模型?

这是面试中的开胃菜,但能区分出候选人是否真正理解 ThinkPHP 的设计哲学。

Db 类是查询构造器,面向数据库表,返回数组,适合简单的、无需业务逻辑的查询。模型是面向业务对象的封装,返回模型实例,支持获取器、修改器、自动时间戳、软删除、关联等高级特性。

面试官想听到的核心答案是:模型承载业务逻辑,Db 只做数据操作。当你的数据需要业务规则(如状态转换、字段格式化、关联操作)时,就应该用模型。

2. 获取器和修改器的工作原理

// 获取器:读取时自动处理
public function getStatusTextAttr($value, $data)
{
    $status = [0 => '禁用', 1 => '启用', 2 => '待审核'];
    return $status[$data['status']] ?? '未知';
}

// 修改器:写入时自动处理
public function setPasswordAttr($value)
{
    return password_hash($value, PASSWORD_DEFAULT);
}

面试官常追问:获取器什么时候不生效? 答案:使用 Db 类查询时不生效;模型内部 $model->getData() 获取原始数据时不触发获取器;toArray() 默认会触发,但可以通过 hidden()、visible() 控制输出字段。

3. 软删除的实现与陷阱

use think\model\concern\SoftDelete;

class User extends Model
{
    use SoftDelete;
    protected $deleteTime = 'delete_time';
    protected $defaultSoftDelete = 0;
}

高频追问:软删除后如何查询已删除数据? 使用 withTrashed();如何只查询已删除数据? 使用 onlyTrashed();如何真正删除? 使用 force()->delete()。

陷阱:软删除字段默认值要设为 null 或 0,如果设为 0 且字段类型为 int,需注意 defaultSoftDelete 的配置,否则可能导致普通查询过滤异常。

二、关联篇:一对一、一对多、多对多的实战要点

1. 关联定义与预载入

class User extends Model
{
    // 一对多:一个用户有多篇文章
    public function articles()
    {
        return $this->hasMany(Article::class, 'user_id', 'id');
    }

    // 一对一:用户有一个 profile
    public function profile()
    {
        return $this->hasOne(Profile::class, 'user_id', 'id');
    }

    // 多对多:用户有多个角色
    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_role', 'role_id', 'user_id');
    }
}

面试必问:N+1 问题怎么解决? 答案:使用 with() 预载入。

// 错误示范:N+1
$users = User::select();
foreach ($users as $user) {
    echo $user->profile->phone; // 每次循环查一次
}

// 正确示范:预载入
$users = User::with(['profile', 'articles'])->select();

2. 关联统计与延迟关联

// 统计关联数量
$users = User::withCount('articles')->select();
// 输出 $user->articles_count

// 延迟关联:先查主表,需要时再查关联
$user = User::find(1);
$articles = $user->articles()->where('status', 1)->select();

面试官常问:with 和 cache 能一起用吗? 可以,但要注意缓存键的生成,关联数据变化时缓存不会自动失效,需要手动处理。

3. 多对多关联的中间表操作

$user = User::find(1);
// 追加关联
$user->roles()->attach([2, 3]);
// 解除关联
$user->roles()->detach([2]);
// 同步关联(覆盖)
$user->roles()->sync([1, 3]);

高频追问:attach 和 sync 的区别? attach 追加,不删除已有;sync 覆盖,删除不在参数中的关联,只保留参数内的。

三、事务篇:保证数据一致性的关键

1. 三种事务写法

// 方式一:自动事务(推荐)
Db::transaction(function () {
    Db::name('user')->where('id', 1)->dec('balance', 100)->update();
    Db::name('user')->where('id', 2)->inc('balance', 100)->update();
});

// 方式二:手动事务
Db::startTrans();
try {
    Db::name('user')->where('id', 1)->dec('balance', 100)->update();
    Db::name('user')->where('id', 2)->inc('balance', 100)->update();
    Db::commit();
} catch (\Exception $e) {
    Db::rollback();
    throw $e;
}

// 方式三:模型事务
$user = User::find(1);
$user->startTrans();
// ...
$user->commit();

2. 事务中的常见陷阱

陷阱一:事务中使用了不同的数据库连接。 ThinkPHP 中 Db::name() 和模型默认使用同一连接,但如果配置了多个数据库连接,事务不会跨连接生效。

陷阱二:事务中嵌套事务。 ThinkPHP 支持事务嵌套,通过 savepoint 实现,但 MySQL 的 savepoint 需要 InnoDB 引擎支持。

陷阱三:事务中执行了 DDL 语句。 如 CREATE TABLE、ALTER TABLE 会导致隐式提交,破坏事务原子性。

陷阱四:异常被 catch 后没有重新抛出。 如果捕获异常后不 throw,外层无法感知错误,事务可能被错误提交。

3. 事务与锁的配合

Db::transaction(function () {
    // 悲观锁:锁定行
    $user = Db::name('user')->where('id', 1)->lock(true)->find();
    if ($user['balance'] >= 100) {
        Db::name('user')->where('id', 1)->dec('balance', 100)->update();
    }
});

面试官可能问:乐观锁怎么实现? ThinkPHP 没有内置乐观锁,需要手动实现,通常用版本号字段:

$affected = Db::name('user')
    ->where('id', 1)
    ->where('version', $version)
    ->update(['balance' => $newBalance, 'version' => $version + 1]);

if (!$affected) {
    throw new \Exception('数据已被修改,请重试');
}

四、面试实战建议

  1. 不要只背 API,要理解设计意图。比如为什么模型比 Db 重?因为它承载了业务逻辑。
  2. 结合项目经验回答。面试官更想听到你在实际项目中如何用关联优化查询、如何用事务保证一致性。
  3. 注意版本差异。ThinkPHP 5.1、6.0、8.0 在模型和事务 API 上有细微差别,面试前确认目标公司使用的版本。
  4. 准备一个踩坑案例。比如软删除导致唯一索引冲突、事务中缓存不一致等,展示你的排查思路。

掌握以上内容,你在 ThinkPHP 面试中的模型、关联和事务环节基本可以做到对答如流。建议结合官方文档和实际项目反复练习,把知识转化为肌肉记忆。

未经允许不得转载:任鹏个人博客 » ThinkPHP 面试精讲:模型、关联与事务高频面试题

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏