Download the PHP package xiasf/think-orm without Composer
On this page you can find all versions of the php package xiasf/think-orm. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download xiasf/think-orm
More information about xiasf/think-orm
Files in xiasf/think-orm
Package think-orm
Short Description 从 ThinkPHP 5.0.24 抽离的独立 ORM 包,保留 think\ 命名空间,提供 Db / Model / Validate / Paginator / Collection / Loader / Config 等完整模型层能力,可在非 ThinkPHP 项目(脚本、微服务、其他框架)中无缝复用 ThinkPHP 5.0 风格 ORM 用法。
License Apache-2.0
Informations about the package think-orm
think-orm
ThinkPHP 5.0.24 ORM 的独立 composer 包移植。保留原 think\ 命名空间,零改动拷贝核心源码,配套 8 个桩文件替换框架依赖。
官方 topthink/think-orm 从 TP 5.1+ 抽出,API 与 5.0.24 不兼容,因此必须直接 fork 5.0.24 实际源码。
安装
需要 PHP >= 7.2 + ext-pdo。MySQL 默认 ext-pdo_mysql。
三步上手
1. 启动
2. 创建模型(默认解析到 app\<module>\model\<Name>)
⚠️ 强烈建议:yf 项目直接继承
app\common\BaseModel,不要继承think\ModelBaseModel 在
think\Model之上封装了 yf 业务高频方法(add / adds / upd / upds / updBy / updAttr / del / info / infoBy / lists / listBy / listByIds / listPageBy / search / search_or / countBy / maxBy / minBy / avgBy / sumBy / valueBy / inc / dec / upSert / resultSet / resultListSet),统一了 CRUD 入口、自动时间戳、错误处理、验证场景、字段格式化等约定。直接继承
think\Model是反模式:会丢掉 yf 项目的统一调用风格、错误转异常、validatorName 推断等关键能力。
推荐做法:继承 app\common\BaseModel
业务调用全部走 BaseModel 统一 API:
BaseModel 的核心能力(详见 example/app/common/BaseModel.php)
| 能力 | 说明 |
|---|---|
| 统一 CRUD 入口 | add/adds/upd/upds/updBy/updAttr/del/delBy 替代散落的 insert/update/delete |
| 统一查询入口 | info/infoBy/lists/listBy/listByIds/listPageBy/search/search_or |
| 统一聚合 | countBy/maxBy/minBy/avgBy/sumBy/valueBy |
| 统一自增减 | inc/dec |
| upSert | 不存在则插入、存在则更新 |
| 自动时间戳 | autoWriteTimestamp = 'datetime' 默认开启 |
| 字段格式化 | resultSet 把 decimal 转 float、JSON 字段自动 decode、append 字段追加 |
| 验证场景自动推断 | 从命名空间推断 validatorName()(如 app\parkinglot\model\v1\Car → parkinglot/Car) |
| 错误转异常 | validateData() 包了 set_error_handler,规则写错抛 ValidateException 而非静默 |
| trait spd/sca | 子类实现 get_Scope() / get_withModel() / get_ExtendField() 等 hook |
拷贝到自己的项目:BaseModel 不在
src/里(它是业务层而非框架层)。把example/app/common/BaseModel.php和example/app/common/traits/model/Model.php复制到你项目的app/common/下即可。完整业务参考见example/app/parkinglot/(BModel + 条件关联 + pivot 过滤 + 多层 with)。
仅在简单脚本场景直接继承 think\Model
如果你的项目只是想用 ORM,不需要 yf 风格 CRUD:
3. 用 model() / validate() 操作
创建验证器(默认解析到 app\validate\<Name>)
命名空间与 model()/validate() 解析
model() / validate() 解析器有 4 种输入形式,所有形式都自动把下划线命名转成大驼峰:
| 调用 | 解析结果 | 目录 |
|---|---|---|
model('user') |
app\model\User |
app/model/User.php |
model('user_order') |
app\model\UserOrder |
app/model/UserOrder.php(下划线 → 大驼峰) |
model('iot/xxx') |
app\iot\model\Xxx |
app/iot/model/Xxx.php(模块/名字) |
model('iot/user_order') |
app\iot\model\UserOrder |
app/iot/model/UserOrder.php |
model('iot/v1/xxx') |
app\iot\model\v1\Xxx |
app/iot/model/v1/Xxx.php(多层路径) |
model('iot/v1/user_order') |
app\iot\model\v1\UserOrder |
app/iot/model/v1/UserOrder.php |
model('\\App\\Special\\User') |
原样使用 | 直接 new 这个类 |
转换规则(Loader::parseName)
- 正向(小写下划线 → 大驼峰):
user_order→UserOrder、car_parking_log→CarParkingLog - 调用方用小写下划线(更符合 URL/参数习惯),实际类名是大驼峰
- 反向:
UserOrder→user_order(仅在某些自动场景用,比如with('userOrder')也可解析到user_order关联)
验证器同规则
validate('iot/Car') → app\iot\validate\Car、validate('iot/v1/CarOrder') → app\iot\validate\v1\CarOrder。
自定义命名空间根
默认根命名空间是 app(由 App::$namespace 控制)。如果你的项目不叫 app:
common 模块 fallback
如果 model('iot/NotExists') 在 iot 模块下找不到,Loader 会自动尝试 app\common\model\NotExists。这是 yf 的多模块共享模型机制。
实际例子(yf 项目风格)
历史写法
model('smartpark/smartpark')->class也能用(model()返回实例,->class取其 FQCN),但不推荐——多一次实例化、绕一道字符串解析、IDE 无法跟踪。新代码统一用TargetClass::class。
文件结构:
yf 风格业务模块参考:parkinglot example
example/app/parkinglot/ 是从 yf 真实业务抽出来的最小可运行参考,覆盖以下高级模式(每个都有对应测试):
1. BModel:双 readonly + 默认关联
2. $insert 自动字段 + 修改器(默认值兜底)
3. belongsTo + ->bind() 字段绑定
把关联表字段"绑"成本模型字段,访问 $owner->name 实际取自 user 表:
4. belongsToMany + pivot 条件过滤
通过 Request::instance()->param('smartpark_id/d') 在运行时给 pivot 加条件:
5. 多层嵌套 with
6. 7 个 yf 风格验证规则(BaseValidator)
| 规则 | 语义 | 示例 |
|---|---|---|
sometimes |
字段存在时才校验(TP 默认行为已涵盖,这里仅作声明) | 'mobile' => 'sometimes\|regex:^1\d{10}$' |
conflict:a,b |
当前字段存在时,a/b 都不能存在 | 'email' => 'conflict:mobile,name' |
r_if:field,v1,v2 |
field 等于 v1 或 v2 时当前字段必填 | 'mobile' => 'r_if:contact_type,phone,sms' |
r_with:a,b |
a 或 b 存在时当前字段必填 | 'nick_name' => 'r_with:name,email' |
r_with_all:a,b |
a 和 b 都存在时当前字段必填 | 同上 |
r_without:a,b |
a 或 b 不存在时当前字段必填 | 同上 |
r_without_all:a,b |
a 和 b 都不存在时当前字段必填 | 同上 |
本包的
Validate::checkItem已扩展:所有r_*规则即使在字段为空时也会触发(默认 TP 行为只对require*规则如此)。这是与上游 TP 5.0.24 的唯一行为差异,但完全是 yf 业务必需的。
7. BaseModel::validateData 错误转异常
规则写错(如 'require|integeregt:0' 拼错)时,原 TP 5.0.24 会触发 PHP Warning 然后静默通过。本包的 BaseModel 用 set_error_handler 包裹,转成 ValidateException 便于排查:
配置
数据库(完整选项)
与 yf 项目
database.php对齐:上面列出的配置项与 TP 5.0.24 / yf 项目惯例完全一致。从 yf 切到本包时,把 yf 的application/database.php返回数组直接传给Orm::boot(['database' => $yfConfig])即可。
自定义常量
守护进程使用
适用场景:workerman / swoole / while(true) loop / cron 等 CLI 常驻进程。
核心问题:常驻进程持有 PDO 连接数小时,MySQL wait_timeout(默认 8h,DBA 可能调到 1h 甚至更短)会主动切断空闲连接,下次 query 时炸 MySQL server has gone away。
关键配置:phpfpm vs cli
| 配置项 | phpfpm / mod_php | cli 守护进程 |
|---|---|---|
params[PDO::ATTR_PERSISTENT] |
true(多请求间复用 PDO,减握手) |
不设 / [](守护进程已常驻,persistent 反而阻止 break_reconnect 关闭僵尸连接) |
break_reconnect |
false(请求短,靠重建连接即可) |
true(必须开,遇到 gone away 关键词自动 close → 重连) |
read_master |
按需 | true(守护进程有"写后立即读"场景的几乎必然) |
phpfpm 启动
cli 守护进程启动
为什么 cli 不能开 ATTR_PERSISTENT?
- 守护进程本来就常驻,PDO 已天然复用,persistent 不带来额外收益
- persistent PDO 由 PHP 内部缓存,
Connection::close()只是$this->linkID = null,不真关闭底层 PDO。break_reconnect 失效,僵尸连接永远占着 - persistent 连接跨脚本共享,多个 worker 进程(如 workerman 多子进程)共用同一个 PHP 持久化池容易撞 transaction 状态污染
完整守护进程 demo
参考 example/daemon/BaseWorker.php —— 单文件、零外部依赖、不依赖 pcntl(Windows 也能跑)。三个核心机制:
1. initDb():启动时预连接
2. heartbeat():定期探活
3. reconnectDb():清两道缓存
踩过的坑:break_reconnect = true 时 Connection::close() 只清自己内部的 linkID,但 Model::$links 是另一个静态缓存,按 model 类名缓存了 Query 实例(持有 Connection)。Connection 重建后,Model 仍会拿到老的僵尸 Connection。
checkDbBreak 关键词列表
参考 Connection::isBreak():
业务异常(如 validate failed)不应触发重连 —— checkDbBreak 用 stripos 匹配关键词,无匹配返回 false。
完整启动示例
参考文件:
example/daemon/BaseWorker.php—— 抽象基类(initDb / heartbeat / checkDbBreak / reconnectDb / 信号处理)example/daemon/QueueWorker.php—— 队列消费具体实现(单条事务、失败回滚、自动建表 + seed)example/run_daemon.php—— 启动脚本(cli 配置演示)
注入(可选)
未注入时所有调用都走 NoOp 桩,不报错。
API 速查
| 用法 | 例子 |
|---|---|
| 模型实例 | model('User') |
| 验证器实例 | validate('User') |
| 单条 | User::get($id) / User::where('x',1)->find() |
| 多条 | User::all() / User::where(...)->select() |
| 单值 | User::where('id',1)->value('name') |
| 列值 | User::column('name','id') |
| 插入 | User::create($data) / (new User)->save($data) |
| 更新 | User::update($data) / $u->save() |
| 删除 | User::destroy($ids) / $u->delete() |
| 物理删除(含软删模型) | User::destroy($ids, true) / $u->delete(true) —— 注意 不是 force()->delete()(见下方差异说明) |
| 自增自减 | User::where('id',1)->inc('hits')->update() |
| 关联 | User::with('posts,profile')->select() |
| 分页 | User::where(...)->paginate(15) |
| 事务 | Db::transaction(fn() => ...) —— 异常抛出 |
| 事务(yf 风格 helper) | transaction(fn() => ..., $errMsg, $errCode, $exception) —— 异常转引用传出,失败返回 false |
| 分批 | User::chunk(100, function($rows){...}) |
| 软删恢复 | User::onlyTrashed()->find()->restore() |
测试
跑 example
example/run.php 端到端演示:model() 解析 → 创建 → 字段格式化(JSON/数组/decimal→float/append)→ 关联预加载 → readonly → 验证器场景 → 聚合。包含 13 个 section:di 模块(Notice/Smartpark)+ parkinglot 模块(Car/CarOwner/Parkinglot/Smartpark/User 的 BModel + 条件关联 + bind + 多层嵌套 + pivot 过滤 + readonly)。所有 SQL 通过 PSR-3 logger 打到 stdout。
跑守护进程示例
example/run_daemon.php 演示守护进程下安全使用 ORM 的完整模式:队列消费 worker,每 2 秒轮询一批任务、单条事务处理、失败回滚、定期心跳、断线重连。详见下方 守护进程使用 章节。
测试覆盖(435 tests / 855 assertions):
| 范围 | 测试文件 |
|---|---|
| 桩文件 | SupportStubTest |
| 配置 | ConfigTest |
| Loader 类 | LoaderTest(parseName / parseClass / addNamespaceAlias / addClassMap / model() / validate() 解析、缓存、common fallback、FQCN passthrough、异常) |
| 验证规则 | ValidateRulesTest(全部内置规则 + 中文消息) |
| 集合 | CollectionTest |
| CRUD | QueryCrudTest、InsertAllTest |
| 事务 | TransactionTest(commit/rollback/嵌套 + yf 风格 transaction() helper 4 个场景) |
| Query Builder | QueryBuilderTest(where/join/group/having/order/limit/page/inc/dec) |
| Query 高级 API | AdvancedQueryTest(whereRaw/whereOrRaw/whereExists/whereNotExists/whereExp/whereTime/whereNotNull/whereNotBetween/whereNotLike/useSoftDelete/fetchSql/getPk/getTableFields) |
| 子查询 | SubqueryTest |
| 分批 | ChunkCursorTest |
| Model CRUD | ModelCrudTest、ModelAccessorMutatorTest、ModelAutoTimestampTest |
| Model 高级 | ModelWorkflowTest(model()/validate() helper、hidden/append、scope、事件、readonly) |
| Model 高级 API | AdvancedApiTest(Paginator URL 辅助:appends/fragment/getUrlRange/getCurrentPage/getCurrentPath/render;Model::has / hasWhere / together) |
| 验证器 | ModelValidationTest(Model 内嵌规则 + 失败回滚) |
| 软删 | SoftDeleteTest |
| 关联 | RelationHasOneTest、RelationHasManyTest、RelationBelongsToTest、RelationBelongsToManyTest、RelationHasManyThroughTest、RelationMorphTest |
| 复合主键 | ComplexPkTest |
| JSON 字段 | JsonFieldTest |
| 分页 | PaginatorTest |
| 闭包 where | ClosureWhereTest |
| yf 风格 BaseModel | YfBaseModelTest(77 个测试:validatorName、useWith、自动时间戳、JSON、读写器、append、关联、readonly、CRUD add/adds/upd/upds/updBy/updAttr/del/delBy、info/infoBy、lists/listBy/listByIds/listPageBy、search/search_or 分页、聚合 countBy/maxBy/minBy/avgBy/sumBy/valueBy/inc/dec、upSert、resultSet/resultListSet、trait spd/sca/listIndexBy/listIndexByIds/fieldWhere/withModel/withScope/get_ExtendField/rollbackQuery、validatorName 显式覆盖、validateData 错误转异常、PSR-3 SQL 日志、validate/model helper、端到端) |
| yf parkinglot 模块 | ParkinglotIntegrationTest(20 个测试:BModel 双 readonly + 条件关联 + helper,$insert 自动字段 + 修改器,belongsTo+bind,hasMany,belongsToMany+pivot 过滤,多层嵌套 with,search_or,7 个 BaseValidator 自定义规则,validateData 错误转异常) |
| 守护进程 | DaemonWorkerTest(15 个测试:initDb/heartbeat 探活、checkDbBreak 关键词识别 gone away/lost connection/broken pipe、业务异常不触发重连、reconnectDb 双缓存清空 Db::$instance+Model::$links、重建 PDO(CONNECTION_ID 变化)、cli 默认关 persistent / phpfpm 通过 params 开 persistent、限次迭代生命周期、心跳触发、模拟断线自动重连恢复) |
与原 TP 5.0.24 的差异
helper.php精简:保留exception / config / dump / debug / model / validate / db / import / trace / load_relation / collection,删除 web 相关助手(lang/input/widget/controller/action/url/session/cookie/cache/request/response/view/json/jsonp/xml/redirect/abort/halt/token/load_trait/vendor)。Config.php:移除依赖Request::module()的动态 extra-config 加载分支。Validate.php:$typeMsg改为硬编码中文;移除{%xxx%}多语言包装与Lang::get/has调用。behavior规则:本包未移植think\Hook类。Validate::behavior()直接返回 true(规则视为通过);如需自定义行为验证,覆盖该方法即可。token规则:依赖 Session,桩默认返回 null,token 规则将失败;可注入 Session 实现启用。Paginator:默认Request::param()桩返回 null,页码默认为 1;要支持 HTTP 上下文需注入自定义 Request。Validate::checkItem扩展:所有以r_开头的规则(r_if/r_with/r_with_all/r_without/r_without_all)即使在字段为空时也会触发——这是 yf 业务必需的条件必填语义。原 TP 5.0.24 只对require*规则做此处理,本包扩展至r_*。Loader.php:移除了register()/loadComposerAutoloadFiles()/registerComposerLoader()等 SPL autoload 注册逻辑——Composer 已处理自动加载。Loader::model()在跨模块查找时的EXTEND_PATH/APP_PATH改为读项目根的extend/与app/目录。-
trait 命名空间迁移:原 TP 5.0.24 的
traits\model\SoftDelete与traits\think\Instance占用顶层traits\命名空间,不适合作为公开 composer 包发布。本包改为:traits\model\SoftDelete→think\traits\model\SoftDeletetraits\think\Instance→think\traits\Instance(同时整理目录结构)
从 yf 迁移过来的代码需把
use traits\model\SoftDelete;改为use think\traits\model\SoftDelete;。 Query::setInc / setDec实时写入:移除了$lazyTime延迟累积更新分支(依赖 Cache 的 inc/dec),inc/dec 永远实时写入 DB。$lazyTime参数保留以维持签名兼容但被忽略。- 不依赖缓存:
think\Cache桩默认所有操作返回 false / null / 空。Query::cache()API 保留(每次仍走 DB),PSR-16 缓存可选注入但不推荐。 -
⚠️
Orm::boot()只能初始化一次:第二次调用仅合并Config,不会刷新这些已建立的单例:Log::$logger(已注入的 PSR-3 logger)Db::$instance(已建立的 PDO 连接)Model::$links(按类名缓存的 Query 实例)
Log::record的"按优先级"行为(重要):- 未注入 PSR-3 logger → 走文件日志(如已通过
log.file启用) - 已注入 PSR-3 logger → 直接调用
logger->log()后return,文件日志永远不写(即使log.file也配置了)
想运行时切换: 场景 正确做法 切日志文件路径(未注入 PSR-3 logger) Orm::refreshLog($newPath)或直接Log::setLogFile($newPath)切 PSR-3 logger 实例 Log::setLogger($newLogger)从 PSR-3 logger 切回文件日志 Log::setLogger(null)+Log::setLogFile($path)多套 DB 配置 用 Db::connect($configKey)显式连接(不要重复 boot 切库)守护进程断线重连 见 守护进程使用 章节 测试场景完全重置 Orm::reset()+Db::clear()+ 反射清空Model::$links - ⚠️
force()是 update 标志,不是物理删除(Laravel 用户最容易踩的坑):$model->force(true)->save()—— 跳过字段比较,强制 update 写入(TP 5.0.24 原语义)$model->force()->delete()—— 仍是软删,force 标志对 delete 无影响- 物理删除(含软删模型)的真实 API:
License
Apache-2.0(同 ThinkPHP 5.0.24)。
All versions of think-orm with dependencies
ext-pdo Version *
ext-pdo_mysql Version *