|
|
WordPress 的 Widget 功能凭借其容易定制和排版灵活的特点,我在做项目的时候特别喜欢用。最近在给雷锋网改版的时候除了首页的分类推荐使用 Widget 来排版以外,iOS 榜单也做成了 Widget,由于榜单是通过 iTunes 接口来获取数据的,因此数据必须缓存一段时间,才能避免每次访问都需要从接口获取数据的问题。嗯,今天就说说怎么给 Widget 设置缓存。
先简单讲解一下 WordPress 默认的两种缓存机制,一种是 Transient Cache,Transient Cache通过数据库的 options 表进行缓存,会产生两行记录,一行用于记录缓存内容,一行用于记录缓存时间,在调用的时候,至少产生两次数据库查询(没有任何数据库缓存的情况下);另一种是 Object Cache,在没有安装任何缓存插件的情况下,WP_Object_Cache 会把数据缓存到 GLOBAL 变量的一个元素里,缓存内容只对当前页面有效,正是因为这一特点,在这个项目里应该选择 Transient Cache 来缓存 Widget 的数据。
接下来先写一个最简单的 Widget:
add_action('widgets_init', array('Test_Widget', 'register')); class Test_Widget extends WP_Widget { // 注册 Widget function register() { register_widget('Test_Widget'); } // 构造函数 function __construct() { $widget_ops = array('classname' => 'test_widget', 'description' => __( '测试挂件') ); parent::__construct('test_widget', __('Test Widget'), $widget_ops); } // Widget 主体输出 function widget( $args, $instance ) { extract( $args ); //...... } // 保存 Widget 参数 function update( $new_instance, $old_instance ) { $instance = $old_instance; //...... return $instance; } // Widget 设置面板 function form( $instance ) { //...... } } 要给 Widget 建立缓存的话,我必须得同时实现这些要求:缓存定期自动更新,更新 Widget 参数的时候清除已有缓存,删除 Widget 的时候把相关的缓存也删除掉。前两点我们可以通过修改 Test_Widget::update() 和 Test_Widget::widget() 实现,修改后的 class Test_Widget 为:
class Test_Widget extends WP_Widget { // 注册 Widget function register() { register_widget('Test_Widget'); } // 构造函数 function __construct() { $widget_ops = array('classname' => 'test_widget', 'description' => __( '测试挂件') ); parent::__construct('test_widget', __('Test Widget'), $widget_ops); } // Widget 主体输出 function widget( $args, $instance ) { extract( $args ); // 如果缓存已经过期,则重新生成结果 if(false === get_transient($args['widget_id'])) { /* ...... 执行 Widget 主体,在输出的同时把结果保存到 $data 变量里 ...... */ // 运行结果缓存 1 小时 set_transient($args['widget_id'], $data, 3600); } else { echo get_transient($args['widget_id']); } //...... } // 保存 Widget 参数 function update( $new_instance, $old_instance ) { $instance = $old_instance; // 更新参数时删除已有的缓存 delete_transient($this->id); //...... return $instance; } // Widget 设置面板 function form( $instance ) { //...... } } 最后要在删除 Widget 的时候把缓存删除,目前我使用的是一个比较暴力的方法:
add_action('sidebar_admin_setup', 'delete_test_widget_cache')); function delete_test_widget_cache() { if(isset($_POST['delete_widget']) && $_POST['delete_widget'] && isset($_POST['widget-id']) && $_POST['widget-id']) { delete_transient($_POST['widget-id']); } } 至此,Widget 缓存机制几经完成,但在删除插件时删除已有缓存这一步里,使用了一个暴力的方法,该方法缺点是:你删除其他的 Widget 也会尝试删除缓存。经过分析 WordPress 源代码,发现删除 Widget 里会调用 Widget Class 里的 update_callback() 方法,利用这个方法去删除 Widget 的缓存,是一个比较文艺的做法。我重新改写后的 Class 如下:
class Test_Widget extends WP_Widget { // 注册 Widget function register() { register_widget('Test_Widget'); } // 构造函数 function __construct() { $widget_ops = array('classname' => 'test_widget', 'description' => __( '测试挂件') ); parent::__construct('test_widget', __('Test Widget'), $widget_ops); } // Widget 主体输出 function widget( $args, $instance ) { extract( $args ); // 如果缓存已经过期,则重新生成结果 if(false === get_transient($args['widget_id'])) { /* ...... 执行 Widget 主体,在输出的同时把结果保存到 $data 变量里 ...... */ // 运行结果缓存 1 小时 set_transient($args['widget_id'], $data, 3600); } else { echo get_transient($args['widget_id']); } //...... } // 保存 Widget 参数 function update( $new_instance, $old_instance ) { $instance = $old_instance; // 更新参数时删除已有的缓存 delete_transient($this->id); //...... return $instance; } // Widget 设置面板 function form( $instance ) { //...... } // 下面这个函数是新增加的,用来删除缓存 function update_callback($widget_args = 1) { // 删除缓存 delete_transient($this->id); delete_transient($this->id); // 调用原来的 update_callback() 方法,防止出错 parent::update_callback($widget_args); } }PS一句:PHP Class 真的很好用,能减少大量代码,以后要多多学习。 |
|