尧图精选

FastAdmin框架中关联查询软删除失效的解决方案

🕒 发布时间:2026/9/12 4:01:06 📁 来源:尧图网络
1. 问题现象与背景分析最近在FastAdmin框架基于ThinkPHP5开发时遇到一个典型问题在进行关联模型查询时软删除soft delete功能意外失效。具体表现为使用with()或join()关联查询时即使主表数据已被软删除仍然能被查询出来。这种情况在管理后台开发中尤为常见。比如我们有个文章表article和分类表category文章表设置了软删除字段delete_time但当通过分类关联查询文章时已被删除的文章仍然会出现在结果集中。2. 软删除机制原理解析2.1 ThinkPHP5的软删除实现ThinkPHP5通过SoftDeletetrait实现软删除功能核心逻辑是数据表需要delete_time字段默认名可配置删除操作变为更新操作将delete_time设为当前时间查询时自动加上delete_time IS NULL条件// 典型用法 use traits\model\SoftDelete; class Article extends Model { use SoftDelete; protected $deleteTime delete_time; }2.2 关联查询的特殊性当进行关联查询时ThinkPHP5会生成类似这样的SQLSELECT * FROM category LEFT JOIN article ON category.id article.category_id WHERE category.id 1问题在于软删除的自动条件article.delete_time IS NULL没有被自动加上。3. 解决方案深度剖析3.1 方案一全局设置关联软删除推荐修改关联模型定义显式声明需要应用软删除// Category模型中 public function articles() { return $this-hasMany(Article)-withTrashed(false); }关键点withTrashed(false)表示关联查询也应用软删除规则需要在所有关联定义中添加此设置FastAdmin中建议在对应的模型文件中修改3.2 方案二查询时临时设置在具体查询时动态指定Category::with([articles function($query){ $query-where(article.delete_time, null); }])-select();优势灵活性高可针对不同场景设置不影响其他关联查询3.3 方案三修改底层逻辑高级继承修改Query类// 新建extend/traits/SoftDelete.php trait SoftDelete { protected function base($query) { if ($query-getOptions(with)) { foreach ($query-getOptions(with) as $relation) { $this-checkRelationSoftDelete($query, $relation); } } return parent::base($query); } }注意此方案需要较强的框架理解能力不建议新手直接使用4. FastAdmin环境下的特殊处理4.1 后台列表关联查询FastAdmin的CRUD控制器中修改index方法protected function indexBuilder() { return $this-model -with([articles function($query){ $query-whereNull(delete_time); }]) -where($this-getWhere()); }4.2 表格渲染适配修改对应的index.html模板{foreach $row.articles as $article} {if !$article.delete_time} !-- 正常显示内容 -- {/if} {/foreach}5. 常见问题排查指南5.1 检查清单现象可能原因解决方案关联查询返回已删除数据未设置withTrashed方案一或方案二部分关联数据缺失误用withTrashed(true)检查关联定义分页总数不正确统计时未过滤软删除使用fetchSql调试5.2 调试技巧获取最终SQLecho $model-fetchSql(true)-with(articles)-find();检查模型继承链print_r(class_parents($model));验证软删除字段dump($model-getDeleteTimeField());6. 性能优化建议索引优化ALTER TABLE article ADD INDEX idx_category_delete (category_id, delete_time);关联查询替代方案// 代替with使用joinwhere组合 $list Category::alias(c) -join(article a, c.id a.category_id AND a.delete_time IS NULL) -select();缓存策略$result Cache::remember(category_list, function(){ return Category::with([articles function($query){ $query-whereNull(delete_time); }])-select(); }, 3600);7. 扩展应用场景7.1 多层级关联处理当存在多层关联时如分类→文章→评论需要逐级设置Category::with([articles.comments function($query){ $query-whereNull(delete_time); }])-select();7.2 动态软删除条件根据不同业务场景动态调整$withDeleted input(param.show_deleted); Article::with([comments function($query) use ($withDeleted){ if (!$withDeleted) { $query-whereNull(delete_time); } }]);7.3 与其他查询条件组合复杂查询示例Article::with([user function($query){ $query-whereNull(delete_time) -where(status, normal) -field(id,nickname); }])-where(create_time, , 2023-01-01) -order(view_count DESC) -select();8. 最佳实践总结一致性原则所有关联模型统一使用软删除数据库字段命名规范统一建议都用delete_time代码规范建议/** * 关联文章 * return \think\model\relation\HasMany */ public function articles() { return $this-hasMany(Article) -whereNull(delete_time) -field(id,category_id,title); }测试用例设计public function testSoftDeleteWithRelation() { $article Article::find(1); $article-delete(); // 软删除 $category Category::with(articles)-find(1); $this-assertEmpty($category-articles); // 应返回空数组 }在实际项目开发中我发现这个问题最容易在以下场景被忽略从简单查询升级到关联查询时接手他人代码进行功能扩展时快速开发原型阶段忽略细节时建议在项目初期就建立完善的模型关联测试套件特别是对数据状态的测试要全面覆盖软删除场景。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →