最近我在帮一个朋友理清一些代码,并研究怎么改才能很好地配合对后端的异步调用。如果还能顺便写得更模块化、更可复用,那就更好了。
这些代码是一个 WordPress 插件,用的是非常老派的写法:业务逻辑和前端逻辑混在一起,数据处理就夹在模板代码中间,诸如此类。
基本上就是你在 21 世纪初的 PHP 站点里见过的那种东西,那时候这门语言还没有那些很棒的 OOP 特性,也没有一堆成熟的框架。
我今天啃的这个场景是这样的:有一个表格,展示一堆条目。我们需要给表格加几个筛选器,并且通过 AJAX 来工作,这样就能快速刷新 UI,而不用重新加载整个 WordPress 后台页面。
筛选数据很简单:写一个内联的 array_filter,在数据传给模板代码之前先过滤一下(为了保持同样的代码风格,这段也写在模板文件里 😇)。
问题出在我们开始让表格能通过 AJAX 渲染的时候。
表格夹在一大堆其他模板代码和业务代码中间。如果想让 AJAX 只输出表格,我们只有几种办法。
第一种办法,是把表格前后的 HTML 都包在巨大的 if 语句里,确保它们不会被渲染出来。
另一种办法,是把模板抽到单独的文件里,然后在我们于插件代码中定义的 AJAX action 里 include 它。
匿名函数来救场!
我琢磨着怎么用最简单的方式搞定这件事,就试着定义一个普通的 PHP 函数,把表格代码放进去,但这里有个问题。
这段代码运行在一个类的方法里,表格模板中有对 $this 的引用。如果不想大改代码,我们就得把 $this 当作函数参数传进去。可惜这行不通,因为你没法把 $this 当参数传 🤷♂️。
我有点失望,因为这意味着模板里要重写一堆东西,而且有不少引用我们都得顾及到,才能保证它继续正常工作。
然后我想起来,这个站点跑的不是 PHP5.2,而是 PHP7,支持匿名函数。
匿名函数还有个很好用的特性:你可以指定调用时,从调用方作用域里把哪些变量传给这个函数。比如:
$table_function = function() use ( $pages, $current_page, $items ) {
// ... Table render code
}这样你就可以在函数体里直接引用 $pages 或 $items。
最棒的是,你还可以把 $this 也作为变量传进函数作用域!
于是我们就能把表格渲染代码完整地抽到一个单独的函数里,在 AJAX 调用时单独调用它了!
当然,函数体里用到的变量必须是已定义的,所以一定要小心 AJAX 函数能在哪里被调用。
对我们来说,这意味着会在主类里加一个很小的 action,它仍然会 require 那个文件,但我们能更好地控制并关掉其他所有会产生的输出。