ECMall关于数据查询缓存的问题
2011-05-24
刚接触Ecmall的二次开发不久,接到一个任务。很常见的任务,主要是对数据库进行一些操作,其中查询的方法我写成这样:
function get_order_data($goods_id)
{
include_once("gonndb/nmdb.php");
include_once("gonndb/dbinfo.php");
$connector = new nmdb($host, $username, $password);
$connector -> select_db($database);
$sql = "select a.buyer_name, a.add_time, a.status, b.phone_tel, b.phone_mob, c.price, c.quantity
from shop_order a, shop_order_extm b, shop_order_goods c
where a.order_id = b.order_id and b.order_id = c.order_id
and c.goods_id = '".$goods_id."'
order by a.add_time desc
limit 3 ";
$result = $connector -> query($sql);
//$r = array();
while($myrow = $connector -> fetch_array($result))
{
$r[] = $myrow;
}
return $r;
}
发现第一次查询的数据是对的,然后经过模板解析后的数据怎么都不正确。后来发现,Ecmall有这么一个机制。先是经过app进行数据库操作,操作完毕后会在 temp/compileed/ 下留下模板缓存,而且第一次数据库查询后会产生数据库缓存。这压根就说明,二次开发,最好不要用自己的数据库函数,用Ecmall自带的比较好。上面的方法改成:
function get_order_data($goods_id)
{
$db = &db();
$sql = "select a.buyer_name, a.add_time, a.status, b.phone_tel, b.phone_mob, c.price, c.quantity
from shop_order a, shop_order_extm b, shop_order_goods c
where a.order_id = b.order_id and b.order_id = c.order_id
and c.goods_id = '".$goods_id."'
order by a.add_time desc
limit 3 ";
$result = $db -> query($sql);
$r = array();
while($myrow = $db -> fetch_array($result))
{
$r[] = $myrow;
}
return $r;
}
这个函数只是使用了Ecmall自带的数据库函数,还是没有产生数据库缓存。看一下别人写的,如何才能产生数据库缓存呢?
下面是一个公告挂件的程序:
<?php
/**
* 公告栏挂件
*
* @param string $ad_image_url 广告图片地址
* @param string $ad_link_url 广告链接地址
* @return array
*/
class NotWidget extends BaseWidget
{
var $_name = 'not';
var $_ttl = 86400;
var $_num = 3;
function _get_data()
{
// 创建一个缓存对象
$cache_server =& cache_server();
// 获取该缓存对象数据的id
$key = $this->_get_cache_id();
// 凭证领取对象书记
$data = $cache_server->get($key);
$data1 = $cache_server->get($key);
if($data === false)
{
$acategory_mod =& m('acategory');
$article_mod =& m('article');
$data = $article_mod->find(array(
'conditions' => 'cate_id=' . $acategory_mod->get_ACC(ACC_NOTICE) . ' AND if_show = 1',
'order' => 'sort_order ASC, add_time DESC',
'fields' => 'article_id, title, add_time',
'limit' => $this->_num,
));
$cache_server->set($key, $data, $this->_ttl);
}
if($data1 === false)
{
$acategory_mod1 =& m('acategory');
$article_mod1 =& m('article');
$data1 = $article_mod1->find(array(
'conditions' => 'cate_id=' . $acategory_mod1->get_ACC(ACC_HELP) . ' AND if_show = 1',
'order' => 'sort_order ASC, add_time DESC',
'fields' => 'article_id, title, add_time',
'limit' => $this->_num,
));
$cache_server->set($key, $data1, $this->_ttl);
}
return array(
'notices' => $data,
'systems' => $data1,
);
}
}
?>
看了程序,我发现ECMALL的文章调用是这样的。定义一个data,一堆调用最后通过 ACC_NOTICE 来确定调用的分类的。最后通过
return array( 'notices' => $data, );
来对应一下。
谈谈写入数据查询缓存的步骤:
// 1. 创建缓存对象 $cache_server =& cache_server(); // 2. 获取缓存数据的 id $key = $this->_get_cache_id(); // 自定义也可以 $key = 'page_of_goods_' . $id; // 将key,数据,缓存时间设置好 $cache_server->set($key, $data, 1800);
如果是缓存模板文件的话,那就算不使用内存来缓存从数据库读取的数据也没关系,因为最后会把模板文件连同数据一起缓存进一个文件,那后面读取时就只读取这个文件而不用再去缓存数据库,但与静态化有点不同,这个文件里面还有一点php信息,并不能用来直接输出,模板引擎会在去掉这点php信息后输出。
在使用ecmall2时也觉得有时反应不过来,相比来讲ecshop还快,经过观察,其实ecshop在display时是加了一个id的,就是用来缓存这次输出的。而我们现在要做的就是在ecmall2里面实现缓存模板输出,通俗点讲就是在$this->display()时给一个id这个id要唯一。
其实所有的东西ecmall2已经准备好了,只是不知道为什么没有使用,详细的原理不再介绍修改完后就可以使你的商城在运行方面的速度上一层楼,但是网络方面可不包哦。
我们以商品详细页为例:
- 修改app/goods.app.php的index方法的最后一行成这样{$this->display('goods.index.html','goods_detail_'.$id);}(不包括大括号,以下不再提示)。
- app/frontend.base.php文件里面的class StorebaseApp extends FrontendApp类的function _config_view()方法里的最后添加如下代码{$this->_view->cache_dir = ROOT_PATH . "/temp/html_cache/store/{$template_name}";}这里设置缓存的目录。
- 再找到app/frontend.base.php的class FrontendApp extends ECBaseApp类的function display($tpl)方法的方法名改成这样{function display($tpl,$cache_id="")}接着把方法里面的parent::display($tpl);改成{parent::display($tpl,$cache_id);}。
- 找到includes/ecapp.base.php里的function display($f)方法的方法名改成{function display($f,$cache_id="")},以及里面的parent::display($f);改成{parent::display($f,$cache_id);}。
- eccore/controller/app.base.php里面的function display($n)方法改成{function display($n,$cache_id='')},以及里面的$this->_view->display($n);改{$this->_view->display($n,$cache_id);}。
再修改eccore/view/template.php的function fetch($filename, $cache_id = '')方法如下。
function fetch($filename, $cache_id = '')
{
if (!$this->_seterror)
{
error_reporting(E_ALL ^ E_NOTICE);
}
$this->_seterror++;
if (strncmp($filename,'str:', 4) == 0)
{
$out = $this->_eval($this->fetch_str(substr($filename, 4)));
}
else
{
if ($this->_checkfile)
{
if (!is_file($filename))
{
$filename = $this->template_dir . '/' . $filename;
}
}
else
{
$filename = $this->template_dir . '/' . $filename;
}
if ($this->direct_output)
{
$this->_current_file = $filename;
$out = $this->_eval($this->fetch_str(file_get_contents($filename)));
}
else
{
if ($this->is_cached($filename,$cache_id)&&$cache_id && $this->caching)
{
$out = $this->template_out;
}
else
{
if (!in_array($filename, $this->template))
{
$this->template[] = $filename;
}
$out = $this->make_compiled($filename);
if ($cache_id)
{
if ($this->appoint_cache_id)
{
$cachename = $cache_id;
}
else
{
$cachename = basename($filename, strrchr($filename, '.')) . '_' . $cache_id;
}
$data = serialize(array('template' => $this->template, 'expires' => $this->_nowtime + $this->cache_lifetime, 'maketime' => $this->_nowtime));
$out = str_replace("\r", '', $out);
while (strpos($out, "\n\n") !== false)
{
$out = str_replace("\n\n", "\n", $out);
}
if (!file_exists($this->cache_dir))
{
ecm_mkdir($this->cache_dir);
}
if (file_put_contents($this->cache_dir . '/' . $cachename . '.php', '<?php exit;?>' . $data . $out, LOCK_EX) === false)
{
trigger_error('can\'t write:' . $this->cache_dir . '/' . $cachename . '.php');
}
$this->template = array();
}
}
}
}
$this->_seterror--;
if (!$this->_seterror)
{
error_reporting($this->_errorlevel);
}
return $out; // 返回html数据
}
怎么样确定成功没有呢?你打开一个商品详细页面,多刷新几次,如果下面的执行时间不变就表示成功。其实用这个方法,你甚至可以给商城的商品分类、店铺的商品分类都缓存起来,条件是你要给$this->display()方法一个能确定唯一的id。
现在的问题:这些修改后好像是不会在固定时间后自动更新的缓存的,你可以去temp/html_cache/下面删除所有的东西,就会重新生成缓存了。
另外,我会继续研究,会有一个比较完善的生成静态的方案,但是应该是收费的(如果有资源的朋友可以过来互换啊,或者能成为核心交流人员的可以免费提供),但基本都是基于这些代码了。
