thinkphp模型类需满足三要素:类名大驼峰、路径匹配命名空间、继承think\model;否则class not found。save()触发时间戳、验证与事件,db::insert()则绕过模型层。

模型类怎么写才被框架识别
ThinkPHP 不会自动扫描所有 class,必须满足命名、路径、继承三要素,否则调用时直接报 Class not found。
- 类名必须是大驼峰(如
User),对应表名默认转为小写下划线(user或user_info) - 文件路径要严格匹配命名空间:比如
app\index\model\User对应app/index/model/User.php - 必须
use think\Model并extends Model;只写class User不继承,User::get(1)会失败 - 如果表名和类名不一致(比如类叫
AdminUser,表却是admin),得显式设protected $table = 'admin'
save() 和 Db::insert() 的行为差异在哪
看起来都是“存数据”,但触发机制、字段处理、事件响应完全不同——混用会导致时间戳不写入、验证不生效、关联不更新。
-
save()是模型方法:自动启用时间戳(create_time/update_time)、自动过滤非法字段、触发before_insert/after_insert事件 -
Db::insert()是底层查询构造器:绕过模型层,不走验证、不调事件、不处理自动写入字段,连soft_delete字段都得手动填 - 想让软删除生效,必须用
User::destroy(1)或$user->delete(),不能用Db::name('user')->where('id', 1)->delete() - 批量插入场景下,
User::insertAll($data)会逐条触发before_insert(一次回调),而Db::insertAll()完全静默
with() 预加载为什么查不出关联数据
常见现象是 $user->articles 为空数组或 null,不是语法错,而是外键/模型名/命名空间没对齐。
- 关联方法返回的
hasOne/hasMany第一个参数是模型名(不含命名空间),比如'Article',不是'app\index\model\Article' - 第二个参数是**当前表的外键字段名**,比如
User表里存的是article_id,那就得写$this->hasOne('Article', 'article_id'),不是user_id - 如果关联模型在非默认命名空间(如
app\common\model\Profile),需在关联方法里用完整类名:$this->hasOne('\app\common\model\Profile', 'user_id') -
with('profile')只对find()/select()生效,all()方法不支持预加载(会报Call to undefined method)
模型事件注册后不触发?检查 init() 是否写对位置
模型事件只在模型类的 protected static function init() 里注册才有效——写在构造函数、控制器、或者随便一个普通方法里,等于没写。
- 必须是
static+protected+ 方法名是init,少一个就失效 - 回调里别直接改主键值,比如
before_insert中写$user->id = uniqid(),可能和数据库自增冲突;要用$user->autoWriteTimestamp(false)关闭时间戳再手动赋值 -
after_update里取不到新数据?用$user->getChangedData(),而不是$user->getData()——后者返回的是原始载入值 - 批量操作如
User::destroy([1,2,3])只触发一次before_delete,参数是第一个模型实例,不是数组;真要逐条处理,得用foreach+ 单条delete()
protected $autoWriteTimestamp = true,Db 查询结果赋给模型实例后调 save(),也不会自动补时间戳——因为模型实例此时没有标记为「新增」或「修改」。这种隐式状态依赖,必须靠 $user->isUpdate(true) 或 $user->exists(true) 显式声明。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











