如何在插件激活时创建多个数据库表?

时间:2019-06-29 作者:bigdaveygeorge

我有一个激活钩子来创建2个不存在的新数据库表,只有第二个表是通过以下代码创建的:

    public function add_tables() {

        // Global $wpdb

        global $wpdb;
        $wpdb->hide_errors();

        // Require upgrade

        require_once( ABSPATH . \'wp-admin/includes/upgrade.php\' );

        // Set charset

        $collate = \'\';
        if ( $wpdb->has_cap( \'collation\' ) ) {
            $collate = $wpdb->get_charset_collate();
        }

        // SQL query

        $sql = "
        CREATE TABLE IF NOT EXISTS " . $wpdb->prefix . "test1 (
            test_id bigint(20) NOT NULL AUTO_INCREMENT,
            test_key char(64) NOT NULL,
        ) $collate;
        CREATE TABLE IF NOT EXISTS " . $wpdb->prefix . "test2 (
            test_id bigint(20) NOT NULL AUTO_INCREMENT,
            test_key char(64) NOT NULL,
        ) $collate;
        ";

        // Do SQL

        dbDelta( $sql );

    }
为什么只有第二个?如果我打印出$sql 变量获取SQL语句,如果在phpMyAdmin中运行该语句,它将创建两个表。

我研究了像WooCommerce这样的插件是如何做到这一点的(https://github.com/woocommerce/woocommerce/blob/c04f7b79f972ee854e5f5d726eb78ac04a726b32/includes/class-wc-install.php#L687) 看来我也在做同样的事情。

3 个回复
最合适的回答,由SO网友:Sally CJ 整理而成

为什么只有第二个?

因为dbDelta() 支架CREATE TABLE table_name 仅限格式。一、 e.精确地“创建表”,后跟(一个空格和)表名。

更具体地说,dbDelta() 使用此正则表达式模式:CREATE TABLE ([^ ]*) 将查询解析为按表名索引的数组时;i、 e。array( \'table_1\' => \'query\', \'table_2\' => \'query\', ... ).

这是相关的code:

$cqueries   = array(); // Creation Queries
...

// Create a tablename index for an array ($cqueries) of queries
foreach ( $queries as $qry ) {
    if ( preg_match( \'|CREATE TABLE ([^ ]*)|\', $qry, $matches ) ) {
        $cqueries[ trim( $matches[1], \'`\' ) ] = $qry;
        $for_update[ $matches[1] ]            = \'Created table \' . $matches[1];
    }
    ...
}
所以在CREATE TABLE IF NOT EXISTS table_name, 表名为(视为)IF 而不是table_name 由于正则表达式模式:

preg_match( \'|CREATE TABLE ([^ ]*)|\', \'CREATE TABLE IF NOT EXISTS table_name\', $matches );
var_dump( $matches );
/* Output:
array(2) {
  [0]=>
  string(15) "CREATE TABLE IF"
  [1]=>
  string(2) "IF"
}
*/

preg_match( \'|CREATE TABLE ([^ ]*)|\', \'CREATE TABLE table_name\', $matches );
var_dump( $matches );
/* Output:
array(2) {
  [0]=>
  string(23) "CREATE TABLE table_name"
  [1]=>
  string(10) "table_name"
}
*/
在您的例子中,两个查询都与该模式匹配,但由于表名都是(视为)IF, 第一个/上一个数组项($queries[\'IF\']) 然后被覆盖。因此只有第二个表(在$queries[\'IF\']) 已创建。

而WooCommerce实际上做到了notCREATE TABLE IF NOT EXISTS 在其代码中:

https://github.com/woocommerce/woocommerce/blob/c04f7b79f972ee854e5f5d726eb78ac04a726b32/includes/class-wc-install.php#L687

Possible Solution when using the CREATE TABLE IF NOT EXISTS table_name format

呼叫dbDelta() 对于每个问题,如@FahamShaikh的回答:

$queries = [ // array of queries
    "CREATE TABLE IF NOT EXISTS " . $wpdb->prefix . "test1 ...",
    "CREATE TABLE IF NOT EXISTS " . $wpdb->prefix . "test2 ...",
];

foreach ( $queries as $sql ) {
    dbDelta( $sql );
}
或:

dbDelta( "CREATE TABLE IF NOT EXISTS " . $wpdb->prefix . "test1 ..." );

dbDelta( "CREATE TABLE IF NOT EXISTS " . $wpdb->prefix . "test2 ..." );

SO网友:Faham Shaikh

让它发挥作用的一种方法是这样做:

global $wpdb;
$wpdb->hide_errors();
// Require upgrade
// Set charset
$collate = \'\';
if ( $wpdb->has_cap( \'collation\' ) ) {
    $collate = $wpdb->get_charset_collate();
}
require_once( ABSPATH . \'wp-admin/includes/upgrade.php\' );
$queries = array();
array_push($queries, "
CREATE TABLE IF NOT EXISTS {$wpdb->prefix}test1 (
       `test_id1` bigint(20) NOT NULL AUTO_INCREMENT,
       `test_key1` char(64) NOT NULL,
       `test_key21` char(64) NULL, PRIMARY KEY  (`test_id1`)
    ) {$collate}");
array_push($queries, "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}test2 (
        `test_id2` bigint(20) NOT NULL AUTO_INCREMENT,
        `test_key2` char(64) NOT NULL,
        `test_key12` char(64) NULL, PRIMARY KEY  (`test_id2`)
    ) {$collate}");
foreach ($queries as $key => $sql) {
    dbDelta( $sql );
}
我同意这并不能解释为什么dbdelta 不起作用,但这似乎至少起作用了。

SO网友:lonelioness

这是我找到的另一种方法here:

require_once( ABSPATH . \'wp-admin/includes/upgrade.php\' );

$sql = \'CREATE TABLE table1...\';
dbDelta ($sql);

$sql = \'CREATE TABLE table2...\';
dbDelta ($sql);

$sql = \'CREATE TABLE table3...\';
dbDelta ($sql);
下面是我在插件中使用它的方式:

/**
 * register_activation_hook implementation
 */
if (!function_exists(\'custom_install\')) {
    function custom_install()
    {
        global $wpdb;
        global $custom_db_version;

        $table_name = $wpdb->prefix . \'customers\';

        $table_name2 = $wpdb->prefix . \'favorite\';

        // sql to create your table
        $sql = "CREATE TABLE " . $table_name . " (
            id int(11) NOT NULL AUTO_INCREMENT,
            name VARCHAR(255) NOT NULL,
            email VARCHAR(255) NOT NULL,
            subject VARCHAR(255) NOT NULL,
            message text,
            amount VARCHAR(255),
            status VARCHAR(10),
            PRIMARY KEY (id)
        );";

        $sql2 = "CREATE TABLE " . $table_name2 . " (
            `color` bigint(12) NOT NULL,
            `size` varchar(20) NOT NULL,
          ) ENGINE=InnoDB DEFAULT CHARSET=latin1";

        require_once(ABSPATH . \'wp-admin/includes/upgrade.php\');
        dbDelta($sql);
        dbDelta($sql2);

        // save current database version 
        add_option(\'custom_db_version\', $custom_db_version);
    }

    register_activation_hook(__FILE__, \'custom_install\');
}