自定义模型“内容页”打不开,后台却正常
你新建了一个“产品库”自定义模型,后台添加了50条数据,前台列表页用灵动标签调用,结果打开直接超时,或者转圈10秒才加载,更诡异的是,后台数据列表页秒开,而前台模板只要一调用该模型就卡死。
排查流程:
帝国CMS二次开发,从卡成PPT到秒开的慢查询优化实战
-
先看SQL日志:帝国CMS后台→系统设置→性能优化→开启SQL日志,刷新前台页面,看最后执行的SQL语句,你会发现类似:
SELECT * FROM phome_enewsproduct WHERE checked=1 ORDER BY id DESC LIMIT 10
这条SQL本身没问题,但如果你在模板里用了
[!--show.product--]这种标签去循环调用附表字段,帝国CMS会逐条数据去查询phome_enewsproduct_data_1表,产生N+1次查询。 -
验证N+1问题:把SQL日志中所有查询复制出来,数一下有多少条
SELECT * FROM phome_enewsproduct_data_1 WHERE id=xxx,50条数据,会有50条附表明细查询。 -
终极方案:不要用标签循环,改用联合查询的灵动标签SQL直接取主表和附表数据:
[e:loop={"SELECT p.*, d.product_price, d.product_stock FROM phome_enewsproduct p LEFT JOIN phome_enewsproduct_data_1 d ON p.id=d.id WHERE p.checked=1 ORDER BY p.id DESC LIMIT 10",10,24,0}]
<li>产品名:<?=$bqr[title]?> 价格:<?=$bqr[product_price]?></li>
[/e:loop]
这样一次查询拿到所有字段,速度提升一个数量级。
灵动标签SQL调用:别再用“*”,列名显式写出
很多二开者写灵动标签喜欢 SELECT *,但帝国CMS的 phome_enewsproduct 主表其实很“瘦”,真正的大字段(详情、图片、价格)都在 _data_ 附表里。SELECT * 只取出主表,然后再在模板里用 [!--product_price--] 去取附表内容,又触发N+1。
正确姿势:
[e:loop={"SELECT p.id, p.title, p.newstime, d.product_price FROM phome_enewsproduct p LEFT JOIN phome_enewsproduct_data_1 d ON p.id=d.id WHERE p.checked=1 ORDER BY p.newstime DESC LIMIT 8",8,24,0}]
<div class="item">
<h3><?=$bqr[title]?></h3>
<span>价格:<?=$bqr[product_price]?></span>
</div>
[/e:loop]
注意:在 e:loop 第二个参数 8 是显示条数,第三个参数 24 是栏目ID(0表示不限),最后一个 0 是操作类型,通常写0。
列表模板和内容模板:变量调用必须分清“主表”与“附表”
帝国CMS默认变量 [!--title--]、[!--newstime--] 是主表字段,如果你在列表模板里直接写 [!--product_price--],会输出空——因为列表页默认只查询主表,除非你开启了“列表模板读取附表字段”(后台→栏目→修改栏目→“列表模板附加SQL”里填 LEFT JOIN phome_enewsproduct_data_1 d ON d.id=id)。
模板(内容页) 则相反,帝国CMS默认会把主表和附表合并查询**,所以你直接写 [!--product_price--] 是有效的,但如果你在内容模板里调用附表字段却显示空,检查一下:
- 自定义模型是否勾选了“这个字段在内容页模板中显示”?模板中加入调试代码:
<?php var_dump($navinfo); ?>
打印所有全局变量,看
$navinfo['product_price']是否存在。
进阶技巧模板中需要调用另一个模型的附表字段,可以用SQL标签:
<?php
$other_data = $empire->fetch1("SELECT price FROM {$dbtbpre}enewsproduct_data_9 WHERE id='$navinfor[id]'");
echo $other_data['price'];
?>
$dbtbpre 是帝国CMS的表前缀变量。
万能标签 vs 智能标签:别用错场景
| 标签类型 | 原理 | 适用场景 |
|---|---|---|
万能标签 ([ecmsinfo]) |
直接写SQL,完全自主控制 | 联合查询、多表关联、复杂统计 |
智能标签 ([e:loop]) |
自动拼接 phome_enews 主表查询,需搭配栏目ID |
简单列表、按栏目/专题调用 |
经典错误:想用万能标签调用栏目“3”下的新闻,却写成:
[ecmsinfo]select * from phome_enewsnews where classid=3 limit 10,10,0,24,0[/ecmsinfo]
结果发现只显示一条,正确写法:
[ecmsinfo]select * from phome_enewsnews where classid=3 order by id desc limit 10,10,0,24,0[/ecmsinfo]
注意最后一个 0 是“取总数”参数,如果写成 1 会只取一条,万能标签的格式是:
[ecmsinfo]SQL语句,显示条数,操作类型,栏目ID,子栏目[/ecmsinfo]
你需要严格按照 SQL语句,条数,操作类型(0),栏目ID(0不限),是否包含子栏目(0/1) 来填写。
全站搜索:自定义字段必须加索引,否则卡死搜索页
帝国CMS全站搜索默认只搜索主表的 title 和 newstime 等少数字段,如果你开启“自定义字段搜索”,需要在后台→数据表管理→找到自定义模型的数据表(如 phome_enewsproduct)→管理字段→对你要搜索的字段(如 product_name、product_brand)建立一个普通索引或全文索引。
标准SQL建立索引:
ALTER TABLE phome_enewsproduct_data_1 ADD INDEX idx_product_name (product_name);
如果不加索引,用户在搜索框输入一个词,帝国CMS会执行 SELECT * FROM phome_enewsproduct WHERE title LIKE '%词%',数据量一大,全表扫描导致CPU飙高,页面直接假死。
搜索模板中调用自定义字段:在 search 模板里,帝国CMS默认只提供 $search 结果数组,不包含附表字段,你需要修改 search/index.php 的查询SQL,在 $query 中加入联合查询:
$query = "SELECT p.*, d.product_price FROM {$dbtbpre}enewsproduct p LEFT JOIN {$dbtbpre}enewsproduct_data_1 d ON p.id=d.id WHERE p.title LIKE '%".$key."%' LIMIT $offset,$page_size";
模板中调用附表字段:终极优化——迂回策略
如果列表模板需要显示附表字段,但你又不想改栏目“附加SQL”(因为会影响其他模板),可以用缓存值法:
- 在后台自定义模型的“列表模板附加SQL”里填写:
LEFT JOIN phome_enewsproduct_data_1 d ON d.id = phome_enewsproduct.id
- 然后列表模板里直接写
[!--d.product_price--]——但注意,这个变量名里不能带,需要改成[!--product_price--],并且要勾选“字段名作为变量名”选项(后台数据表字段管理里,每个字段有个“是否作为列表变量”的开关)。
更稳妥的办法是直接修改模型的主表结构,把高频展示的附表字段(如价格、库存)复制到主表,后台编辑时同步更新(通过 sys_AddNews.php 的 e/class/connect.php 中的 AddNews 函数,添加一处 $add[product_price] 即可),这是很多成熟二次开发者的常用做法——用空间换时间,牺牲少量冗余存储,换取前台秒开。
慢查询的“三板斧”排查口诀
- 开SQL日志,找重复查询。
- 联合查询替代循环,一次SQL拿全字段。
- 对where条件字段加索引,特别是
classid、checked、userid这些高频过滤字段。
帝国CMS的慢查询,90%不是帝国本身慢,而是模板写法在“打地鼠”——每条数据打一次洞穴,用一次 LEFT JOIN 把数据全部拖出来,你的网站就能从“PPT模式”切换成“火箭模式”。



发表评论