插件开发 1
更新于 2026-10-13
插件开发 · 数据库与模型
一、表命名与建表
- 表前缀统一
ws_,插件自建表建议加上插件标识:ws_myplugin_log
- 字符集统一
utf8mb4,引擎InnoDB
- 每张表保留
create_time/update_time(int unsigned,Unix 秒)
- 需要软删除的表加
delete_time(可空)
CREATE TABLE IF NOT EXISTS `ws_myplugin_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`site_id` int(10) unsigned NOT NULL DEFAULT 0,
`content` varchar(255) NOT NULL DEFAULT '',
`ip` varchar(64) NOT NULL DEFAULT '',
`create_time` int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_site` (`site_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='插件日志';
多站点项目里,自建表请加
site_id并在查询时过滤,避免站点间数据串台。
二、执行 SQL
安装、升级脚本放在 install/ 下,也可在生命周期回调中直接执行:
use think\facade\Db;
Db::execute("ALTER TABLE `ws_myplugin_log` ADD COLUMN `ua` varchar(255) NOT NULL DEFAULT ''");
三、读写数据
3.1 查询构造器
use think\facade\Db;
// 查询
$list = Db::name('myplugin_log')->where('site_id', $siteId)->order('id desc')->limit(20)->select();
$one = Db::name('myplugin_log')->where('id', $id)->find();
$cnt = Db::name('myplugin_log')->where('site_id', $siteId)->count();
$val = Db::name('myplugin_log')->where('id', $id)->value('content');
// 写入
$id = Db::name('myplugin_log')->insertGetId([
'content' => 'hello',
'create_time' => time(),
]);
// 更新
Db::name('myplugin_log')->where('id', $id)->update(['content' => 'updated']);
// 删除
Db::name('myplugin_log')->where('id', $id)->delete();
3.2 事务
多步写入必须放在事务中:
Db::transaction(function () use ($order) {
Db::name('order')->where('id', $order['id'])->update(['status' => 1]);
Db::name('license')->insert([
'order_no' => $order['order_no'],
'create_time' => time(),
]);
});
四、模型
需要复杂逻辑时定义模型:
<?php
namespace addons\myplugin\model;
use think\Model;
class Log extends Model
{
protected $name = 'myplugin_log'; // 物理表名(不含前缀)
protected $autoWriteTimestamp = 'int';
protected $createTime = 'create_time';
protected $updateTime = 'update_time';
protected $type = [
'create_time' => 'timestamp:Y-m-d H:i',
];
/** 关联站点 */
public function site()
{
return $this->belongsTo(\app\common\model\Site::class, 'site_id');
}
/** 查询作用域 */
public function scopeToday($query)
{
return $query->where('create_time', '>=', strtotime('today'));
}
}
用法:
Log::where('site_id', $siteId)->today()->paginate(20);
⚠️ 模型的
$name是物理表名(不含前缀),与表名含保留字时需注意:ws_model的物理表名列本身叫 `table`,是 MySQL 保留字,写 SQL 时要用反引号。
五、容易踩的坑
| 坑 | 现象 | 正确做法 |
|---|---|---|
->column('id,sort') 不可靠 | 拿不到 [id => sort] 映射 | 改用 field('id,sort')->select()->toArray() 自行遍历 |
| 排序字段加反引号 | order express error | order('sort desc') 不加反引号 |
runtime_path() 在应用内 | 缓存写到了 runtime/<应用>/ | 自定义缓存用项目根 runtime/<名字>/ |
未过滤 site_id | 站点间数据串台 | 所有查询都带上 site_id |
直写 delete_time 判断 | 已删数据仍出现 | 用 whereNull('delete_time') |
六、数据迁移建议
- 用
info.ini的version驱动升级:upgrade($from,$to)里按版本区间做增量变更
- 不要在升级里
DROP TABLE,除非用户明确确认
- 结构变更前先备份:后台 → 运维工具 → 数据备份
- 所有变更脚本要可重复执行(
ADD COLUMN IF NOT EXISTS之类),避免中途失败后无法重跑