如何自定义 wpdb 类并添加新方法

已发表: 2018-07-10

WordPress的数据库抽象层,俗称wpdb,是一个基于ezSQL的类,负责与数据库交互。 wpdb 类也是一个插件——一段核心 WP 代码,可以很容易地被另一段具有类似功能的代码替换(WP 代码参考中提供了最新的插件列表)。

修改默认的#wpdb 类并不复杂。 您可以轻松地添加、删除或修改几个方法。 无需更换整个班级。 #wordpress

点击推文

这是一个很大的误解发挥作用的地方。 人们将“替换”视为“编写我自己的、完整的数据库类”。 幸运的是,您不必编写整个数据库类。 您可以使用现有的类并仅添加、删除或修改您需要的功能。 这就是我们要做的。

为什么要修改 wpdb 类?

当然,wpdb 有你需要的一切!? 整个 WP 都建立在它之上。 从技术上讲,这是正确的。 通过使用$wpdb->query()方法,您可以执行任何 SQL 命令。 但是应用了这种逻辑,我们将永远不会拥有或需要像$wpdb->insert()$wpdb->get_var()的方法。 但我们这样做了,因为它们可以节省时间、减少错误并避免错误查询。 那么,缺少哪些功能呢? 这取决于您正在从事的项目。 我们需要对这两个查询的支持:

INSERT IGNORE INTO tbl_name ...
INSERT INTO tbl_name ... ON DUPLICATE KEY UPDATE ...

步骤 #1 – 创建插件

创建数据库插件的过程很简单,最终结果可以无缝融入 WP。
创建一个名为db.php的文件并将其放在/wp-content/文件夹中。 给文件一个标准的插件头并创建一个扩展 wpdb 的类。 像这样的东西:

<?php
/*
  Plugin Name: Extended wpdb
  Description: A few extra functions for wpdb
  Version: 1.0
  Author: WebFactory Ltd
*/

class wpdb_extended extends wpdb {

  public function __construct(){
    parent::__construct( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
  }

  public function test() {
    echo 'Extended wpdb is running.';
  }
}


global $wpdb;
$wpdb = new wpdb_extended();

标题是不言自明的。 构造方法确保我们在构造对象后创建一个新的数据库连接,并为此使用原始的 wpdb 构造函数。 这些 DB 常量在wp-config.php中定义。 正如我们在开始时所说的那样——我们只是添加了一些功能并重用了其他所有内容。 第二种方法test()纯粹是为了看看一切是否正常。

最后两行至关重要。 由于这是一个自定义类,WP 不会自动为我们创建$wpdb对象。 我们必须自己做。 请记住使用global关键字,否则,您将创建一个局部变量。

步骤 #2 – 在深入挖掘之前进行测试

打开您的 WP 仪表板并转到插件。 除了通常的 All、Active 和 Inactive(插件)选项卡之外,您还会看到一个 Drop-ins 选项卡,其中,通过某种魔术,我们的自定义数据库类。

由于管理员工作正常,我们知道我们做得很好。 我们新的、自定义的、扩展的类在里面,没有任何改变。 伟大的! 但是,为了更好的衡量,将这三行代码放入主题的functions.php (或插件内的某个地方,它将立即运行):

global $wpdb;
$wpdb->test();
die;

运行任何管理或前端页面,你会看到什么? “扩展的 wpdb 正在运行。 “ 完美的。 现在为我们的现实世界的功能。

步骤 #3 – 创建、修改或替换函数

INSERT IGNORE查询似乎很容易做到。 我们已经有了 $wpdb->insert() 所以我们只是少了一个单词。 不幸的是,由于在正确的地方缺少过滤器,它不会是两行代码。

如果您打开 wp-db.php 并前往 #2100 行的insert()方法,您会看到它使用了 #2151 行的_insert_replace_helper()方法。 而且该功能没有单个过滤器或操作。 我们也不能包装该函数,因为它会立即返回查询结果,而不是我们可以修改的查询本身。 真可惜。 我们必须重写insert()_insert_replace_helper()方法。 我们将添加一个额外的参数 - $ignore 。 默认值为false的布尔值。

public function insert( $table, $data, $format = null, $ignore = false ) {
  return $this->_insert_replace_helper( $table, $data, $format, 'INSERT', $ignore = false );
}

在任何其他情况下,具有五个参数的函数都可以很好地将它们转换为关联数组。 但是我们想要向后兼容,所以我们不这样做。 对其他功能的更改也很小:

function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT', $ignore = false ) {
  $this->insert_id = 0;

  if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {
    return false;
  }

  $data = $this->process_fields( $table, $data, $format );
  if ( false === $data ) {
    return false;
  }

  $formats = $values = array();
  foreach ( $data as $value ) {
    if ( is_null( $value['value'] ) ) {
      $formats[] = 'NULL';
      continue;
    }

    $formats[] = $value['format'];
    $values[]  = $value['value'];
  }

  $fields  = '`' . implode( '`, `', array_keys( $data ) ) . '`';
  $formats = implode( ', ', $formats );
  
  // modification for IGNORE keyword
  if (true == $ignore && 'INSERT' == $type) {
    $type = 'INSERT IGNORE';
  }

  $sql = "$type INTO `$table` ($fields) VALUES ($formats)";

  $this->check_current_query = false;
  return $this->query( $this->prepare( $sql, $values ) );
}

为什么开发人员不经常这样做呢?

如果您只需要几次自定义查询,您可以轻松使用$wpdb->query() 。 无需替换默认类。 您还可以编写一个扩展默认类但不覆盖 wpdb 对象的自定义类。 在这种情况下,您将使用$mycustomclass->method()但这不是世界末日。

除了扩展默认 wpdb 类的明显酷炫因素之外,这是在 WP 中完成的事情,而且不是 hackish,所以请随意使用它。 请记住两件事:

  • 当您将项目发送给某人时,不要忘记包含db.php文件; 它不会出现在任何主题或插件文件夹中
  • 密切关注wp-db.php中的变化,因为它们可能会影响您的自定义类(尽管这极不可能)