问题场景一:自定义模型建好了,前台列表却一片空白?
很多开发者创建完自定义模型后,在列表模板里用 [!--news.url--]e/action/ShowInfo.php?classid=... 死活调不出数据,先别急着怀疑SQL语法,按这个顺序排查:
- 系统模型ID与数据表前缀:确认
phome_ecms_前缀是否匹配,若模型名为product,实际表是phome_ecms_product,但在灵动标签里误写成phome_ecms_article,肯定查不到。 - 模板变量名冲突:在
ListNews类模板中,如果同时使用了$bqno和自定义字段$nprice,且两者命名相近,可能导致字段覆盖,务必在模板头部打印<pre><?=print_r($r,true)?></pre>调试。 - 自定义字段是否加入主表:新建模型时若选“字段存放于附表”,但前台模板用了
$r['price']直接读取主表,必然失败,需在灵动标签里用select * from phome_ecms_product_data where id=$r[id]二次查询。
灵动标签SQL调用示例:
[e:loop={"SELECT p.*, d.custom_price, d.stock FROM phome_ecms_product p LEFT JOIN phome_ecms_product_data d ON p.id=d.id WHERE p.classid=5 AND p.ischeck=1 ORDER BY p.newstime DESC LIMIT 10",10,24,0}]
<li>产品名:<?=$bqr[title]?> 现价:<?=$bqr[custom_price]?> 库存:<?=$bqr[stock]?></li>
[/e:loop]
问题场景二:列表模板和内容模板的变量调用到底差在哪?
帝国CMS数据表聚合查询优化实战,从索引失效到秒开列表的七个坑
列表页的 $bqr 是每行数据的关联数组,而内容页的 $navinfo 是单条数据,但很多人不知道,内容模板里 附表字段 必须用 $navinfo[字段名] 直接取(前提是附表已加载),而列表模板却要主动关联。
列表模板中的附表字段技巧:
若不想每次写LEFT JOIN,可在后台“数据表聚合”功能中,将常用字段“合并到主表”,但聚合操作有风险——若字段类型为TEXT或BLOB,会导致主表臃肿,此时建议:
// 在列表模板顶部自定义函数
function get_data_field($id, $field) {
global $empire,$dbtbpre;
$r = $empire->fetch1("select $field from {$dbtbpre}ecms_product_data where id='$id'");
return $r[$field];
}
// 使用:<?=get_data_field($bqr[id],'custom_price')?>
问题场景三:万能标签和智能标签的生死抉择
- 智能标签(
[e:loop])适合简单的列表,它默认带分页,且自动处理审核状态,但对多表关联或复杂判断(如按价格区间+城市过滤)力不从心。 - 万能标签(
[e:万能])本质是直接执行SQL,但注意:它不接受$_GET参数,必须用$add变量拼接条件,且 必须加LIMIT,否则内存爆炸。
万能标签典型场景:跨模型聚合搜索
[e:万能]
$where = "";
if($_GET['brand']) $where .= " AND p.brand='".intval($_GET['brand'])."'";
if($_GET['price_max']) $where .= " AND p.price <= ".intval($_GET['price_max']);
$sql = "SELECT p.*, d.weight FROM phome_ecms_product p LEFT JOIN phome_ecms_product_data d ON p.id=d.id WHERE p.ischeck=1 $where ORDER BY p.newstime DESC LIMIT 20";
$result = $empire->query($sql);
while($r=$empire->fetch($result)) {
?>
<li><?=$r['title']?> 重量:<?=$r['weight']?></li>
<?php } ?>
[/e:万能]
问题场景四:全站搜索卡成狗?自定义字段索引是关键
默认搜索只查 title 和 newstime,若用户经常按 brand 或 price 搜索,必须:
- 在模型字段管理里,将需搜索的字段设为 “可搜索”。
- 后台“全站搜索配置”中,勾选“搜索时使用索引”,并添加组合索引:
ALTER TABLE `phome_ecms_product` ADD INDEX idx_brand_price (brand, price);
- 若数据量超10万,建议改搜索为
INSTR+ 全文索引,但帝国默认不支持中文分词,可加ngram插件:ALTER TABLE `phome_ecms_product` ADD FULLTEXT INDEX ft_title (title) WITH PARSER ngram;
然后在搜索模板中:
[e:loop={"SELECT id,title FROM phome_ecms_product WHERE MATCH(title) AGAINST('$searchkey' IN NATURAL LANGUAGE MODE) LIMIT 20",20,24,0}]
问题场景五:模板中调用附表字段的终极方案
避免每次写JOIN,可在 e/class/connect.php 的 $public_r 里添加自动加载逻辑:
// 在函数 getSmallImg() 后面增加
if($GLOBALS['ecms_config']['db']['use_dataname']) {
$GLOBALS['auto_load_data'] = true;
}
// 然后在子类模型继承时,重写 loadtemp变量:
function loadtemp($sid, $classid=0, $line=0) {
$r = parent::loadtemp($sid, $classid, $line);
if($GLOBALS['auto_load_data'] && $sid==5) { // 模型ID=5
$data = $GLOBALS['empire']->fetch1("select * from phome_ecms_product_data where id='$r[id]'");
$r = array_merge($r, $data);
}
return $r;
}
这样模板里直接 <?=$bqr['long_desc']?> 即可,无需多次查询,但注意:这会增加主查询压力,建议只在详情页启用。
问题场景六:聚合查询性能瓶颈——别再全表扫描了
当列表页需要同时显示主表+附表+副表(如评论数)时,千万别用三层嵌套循环,用 一次JOIN + 临时表:
// 在e/class/connect.php 写一个全局函数
function get_multi_data($classid, $limit=10) {
global $empire, $dbtbpre;
$temp_table = $dbtbpre.'temp_'.get_usecs();
$empire->query("CREATE TEMPORARY TABLE $temp_table AS
SELECT p.id, p.title, p.newstime, d.price, d.sku,
(SELECT COUNT(*) FROM phome_ecms_product_comments c WHERE c.id=p.id) AS comment_count
FROM phome_ecms_product p
LEFT JOIN phome_ecms_product_data d ON p.id=d.id
WHERE p.classid=$classid AND p.ischeck=1
ORDER BY p.newstime DESC LIMIT $limit");
$result = $empire->query("SELECT * FROM $temp_table");
// 后续用 while 循环输出
}
注意:临时表在请求结束后自动销毁,适合高并发时减少重复JOIN。
问题场景七:动态缓存——把聚合结果提前烤熟
对每天只更新几次的数据,用帝国自带的 [e:cache] 标签缓存整个查询模板块:
[e:cache 3600, 'all_products'] // 这里放循环SQL [/e:cache]
但如果数据频繁更新,建议使用 Redis 配合帝国API:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = "products_list_".$classid;
if(!$redis->exists($cacheKey)) {
// 执行聚合SQL,结果序列化后存入
$redis->set($cacheKey, serialize($list), 300);
} else {
$list = unserialize($redis->get($cacheKey));
}
并且在更新文章时,主动删除对应缓存:在 e/class/hinfofun.php 的 AddNews() 函数末尾加上 $redis->del("products_list_".$classid);。
聚合查询优化不是堆代码,而是权衡数据模型设计,多利用帝国的“数据表聚合”配置 + 合理的索引 + 恰当的缓存策略,比在模板里写十层循环高效得多,一次JOIN永远好过十次单表查,临时表不是银弹,但能救急。永远在SQL末尾加LIMIT,除非你想把数据库拖垮。



发表评论