WP DEVELOP

EP173. “把插件模板改成 Block Theme 占位 Block”

首页 WordPress 开发课程 插件开发 CH3:数据库存储 · EP173
约 19 分钟· #EP173#插件开发 CH3:数据库存储
🔒 登录后可标记已读

这是一篇纯文字讲义(没有配套视频),补一个之前没考虑到的兼容问题:这整个数据库章节的插件示例是照着传统主题的假设写的(用 template_include 过滤器把 /pet-adoption 整个页面模板替换掉),如果站点用的是Block Theme(现代化主题),页面的头部/底部就不会是 Fictional University 主题该有的样子,而是 WordPress 默认的头尾——因为 Block Theme 压根不认 template_include 这一套。解法是把插件原本「替换整个页面模板」的做法,改成「注册一个可以在编辑器里插入到任意页面的占位 Block」,让页面本身留给 Block Theme 的编辑器搭建,宠物列表只是页面里的一个 Block 而已。


涉及文件

  • wp-content/plugins/new-database-table/our-blocks/petslist.js (新建)
  • wp-content/plugins/new-database-table/our-blocks/petslist.php (新建,原 inc/template-pets.php 内容搬过来改造)
  • wp-content/plugins/new-database-table/inc/template-pets.php (删除)
  • wp-content/plugins/new-database-table/new-database-table.php (修改)

代码实现

our-blocks/petslist.js(新建,跟主题章节学过的占位 Block 写法一致)

wp.blocks.registerBlockType("ourdatabaseplugin/petslist", {
  title: "Fictional University Pets List",
  edit: function () {
    return wp.element.createElement("div", { className: "our-placeholder-block" }, "Pets List Placeholder")
  },
  save: function () {
    return null
  }
})

our-blocks/petslist.php(新建,内容是原 inc/template-pets.php 去掉页面外壳后剩下的部分)

<?php

require_once( NEWDATABASETABLEPATH . 'inc/GetPets.php' );
$getPets = new GetPets();

?>

  <p>This page took <strong><?php echo timer_stop();?></strong> seconds to prepare. Found <strong><?php echo number_format($getPets->count); ?></strong> results (showing the first <?php echo count($getPets->pets) ?>).</p>
  
  <table class="pet-adoption-table">
    <!-- ...表格结构不变,见 EP165/EP171-172... -->
  </table>

  <?php 
    if (current_user_can('administrator')) { ?>
      <form action="<?php echo esc_url(admin_url('admin-post.php')) ?>" class="create-pet-form" method="POST">
        <!-- ...新增宠物表单不变... -->
      </form>
    <?php }
  ?>

new-database-table.php:加一行 define,删掉模板替换逻辑,新增占位 Block 注册类

require_once plugin_dir_path(__FILE__) . 'inc/generatePet.php';

define( 'NEWDATABASETABLEPATH', plugin_dir_path( __FILE__ ));

class PetAdoptionTablePlugin {
  function __construct() {
    global $wpdb;
    $this->charset = $wpdb->get_charset_collate();
    $this->tablename = $wpdb->prefix . "pets";

    add_action('activate_new-database-table/new-database-table.php', array($this, 'onActivate'));
    add_action('admin_post_createpet', array($this, 'createPet'));
    add_action('admin_post_nopriv_createpet', array($this, 'createPet'));
    add_action('admin_post_deletepet', array($this, 'deletePet'));
    add_action('admin_post_nopriv_deletepet', array($this, 'deletePet'));
    add_action('wp_enqueue_scripts', array($this, 'loadAssets'));
    // template_include 这个 add_filter 整行删除
  }

  // ...deletePet() / createPet() / onActivate() 等方法不变...

}

$petAdoptionTablePlugin = new PetAdoptionTablePlugin();

class OurPluginPlaceholderBlock {
  function __construct($name) {
    $this->name = $name;
    add_action('init', [$this, 'onInit']);
  }

  function ourRenderCallback($attributes, $content) {
    ob_start();
    require plugin_dir_path(__FILE__) . 'our-blocks/' . $this->name . '.php';
    return ob_get_clean();
  }

  function onInit() {
    wp_register_script($this->name, plugin_dir_url(__FILE__) . "/our-blocks/{$this->name}.js", array('wp-blocks', 'wp-editor'));
    
    register_block_type("ourdatabaseplugin/{$this->name}", array(
      'editor_script' => $this->name,
      'render_callback' => [$this, 'ourRenderCallback']
    ));
  }
}

new OurPluginPlaceholderBlock("petslist");

关键改动点:

  • inc/template-pets.php 整个删除,内容搬到新建的 our-blocks/petslist.php,但要做减法:删掉 get_header()/get_footer()(Block Theme 下页面外壳交给编辑器搭建的页面模板处理,插件的 Block 只负责自己那一小块内容)、删掉外层 page-banner 那个 <div>、删掉最外层 container 包裹的 <div>(表格本身保留,只是不再需要这层包装 div)
  • define('NEWDATABASETABLEPATH', plugin_dir_path(__FILE__)):在主插件文件顶部定义一个全局常量,存住插件根目录的绝对路径——因为 our-blocks/petslist.php 现在需要引入 inc/GetPets.php,而这个文件不再位于原来的 inc/ 文件夹里,用这个常量能不管调用来源在哪里都能正确拼出 inc/GetPets.php 的路径
  • petslist.php 顶部的 require_once 从相对路径改成用这个常量require_once(NEWDATABASETABLEPATH . 'inc/GetPets.php'),而不是原来模板文件里用的 plugin_dir_path(__FILE__) . 'GetPets.php'(因为当前文件所在目录已经变了)
  • 删掉 template_include 这个 add_filter:既然不再需要替换整个页面模板,这行代码整个可以移除,插件唯一要做的就是让新 Block 类型可用
  • OurPluginPlaceholderBlock:这段代码基本就是这门课在主题/Block Theme 章节学过的占位 Block 写法照搬过来,唯一区别是这里改成从「当前插件目录」而不是「当前主题目录」找文件
    • onInit():用 wp_register_script() 注册编辑器要用的 JS(our-blocks/petslist.js),register_block_type() 注册这个 Block,render_callback 指向 ourRenderCallback()
    • ourRenderCallback($attributes, $content):用 ob_start()/ob_get_clean() 的套路(这门课反复用过的技巧),把 our-blocks/petslist.php 的输出收集成字符串返回——require 的文件路径是拼出来的:'our-blocks/' . $this->name . '.php'
    • new OurPluginPlaceholderBlock("petslist"):实例化时传入的字符串 "petslist" 同时决定了要去 our-blocks/ 目录找的 JS 和 PHP 文件名——这个名字必须跟 our-blocks/ 目录下实际的文件名完全一致,如果文件叫 petlist.php(少一个 s)却传了 "petslist",就会找不到文件
  • 在 WordPress 后台的操作步骤(不是代码):创建一个标题为「Pet Adoption」的页面,在正文区域插入新出现的「Fictional University Pets List」这个 Block,保存后前台就能看到跟 Block Theme 头尾风格一致的宠物列表页面
  • 如果想让这个 Block 在编辑器里的预览也是真正的动态内容(而不是像这里一样只显示占位文字),可以参考这门课「Are You Paying Attention」和「Featured Professor」这两个 Block 类型插件章节学到的做法(useSelect/自定义 REST 端点等)

Hook / Function 速查

名称类型用途
define($常量名, $值)PHP 内建语法定义一个全局常量,跨文件调用同一个值时避免重复计算/硬编码
register_block_type($名字, array('editor_script' => ..., 'render_callback' => ...))WP 内建 function注册一个用 PHP 渲染实际内容、JS 只负责编辑器占位显示的 Block 类型

常见坑

  • 用 Block Theme 时插件依然用 template_include 过滤器整个替换页面模板——页面的头部/底部会变成 WordPress 默认外观,跟主题风格不一致
  • our-blocks/ 目录下的文件名跟实例化 OurPluginPlaceholderBlock 时传入的字符串对不上——找不到对应的 PHP/JS 文件,Block 无法正常工作
  • inc/template-pets.php 的内容搬到新文件后忘记同步调整 require_once 的路径——因为文件所在目录变了,原来基于 plugin_dir_path(__FILE__) 拼出来的相对路径不再指向正确位置

[截图:Site Editor 编辑页面时,在正文区域插入"Fictional University Pets List"占位 Block 后的编辑器画面]

[截图:改用占位 Block 后的前台 Pet Adoption 页面,头部/底部已恢复成 Fictional University 主题该有的样子]


Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 27, EP173