wp數據庫之自定義建立數據表php
WordPress提供的兩個API使建立和使用自定義表沒有多麼麻煩!最值得注意的是 $wpdb類和dbDelta()函數。雖然如此,但建立自定義表,意味着給Wordpress建立一個陌生的環境,這是相對於Wordpress自身的表來講的,你做爲插件(或者主題,本系列文章以插件爲例)做者,就有責任讓你的代碼可以與數據庫進行安全高效的交互。因此,你在決定以前,先考慮下是否是必須使用自定義表。
1.在Wordpress中使用自定義表的缺點
如前所述,自定義表在正常的WordPress的框架以外,Wordpress不熟悉它,並且在大多數狀況下這一點就是使用自定義表的缺點的根本緣由:
1.1 沒有Wordpress原生的添加、刪除、更新或查詢函數,能夠在你的自定義表和不鏽鋼轉子泵代碼之間交互;
1.2 你須要徹底自定義UI;
1.3 你須要本身來考慮禁止和緩存;
1.4 其它插件/主題也不知道這個表的存在,除非你本身寫單獨的插件或者使用自定義表的插件的附加組件;
1.5 你須要謹慎選擇備份插件,由於有些備份插件不支持自定義表的備份,因此我以爲最佳的備份方案是本身去數據庫管理界面(一般是phpMyadmin)導出數據庫以備份;
1.6 你有責任讓你的表的結構最優化、讓你的包括SQL語句在內的所有代碼沒有任何BUG,以不鏽鋼轉子泵高效運行;
2.何時適合使用自定義表
這個問題貌似沒有正確答案。上一小節已經列出了建立自定義表的一些缺點,因此,若是你以爲沒有十分的必要,就不要使用自定義數據表,僅在你的數據有大量的讀寫或者數據量很大或者數據結構很複雜的時候,才建議你使用自定義數據表。
3.建立自定義表
一旦肯定使用自定義表是必要的,咱們就要來建立它,建立它以前,咱們須要將自定義表的代表存儲於$wpdb,這個全局變量包含了與當前Wordpress網站相關的所的信息,若是你啓用了Wordpress的多站架構,在切換網站的時候,這個變量纔會改變,咱們把自定義表名添加到這個變量不是必需的,可是能讓咱們的代碼更高效:
add_action( 'init', 'coolwp_register_activity_log_table', 1 );
add_action( 'switch_blog', 'coolwp_register_activity_log_table' );
function coolwp_register_activity_log_table() {
global $wpdb;
$wpdb->coolwp_activity_log = "{$wpdb->prefix}coolwp_activity_log";
}
上述代碼中的$wpdb->prefix給自定義表名添加一個前綴,若是你在安裝Wordpress的時候,沒有修改的話,這個前綴默認是「wp_」,該前綴能夠在wp-config.php中被修改,修改數據庫中表的前綴這是爲了方便你幾個Wordpress網站使用一個數據庫,也就是時候,這多個網站的數據在數據庫中能夠經過表名前綴而加以區分。
在本系列文章中,咱們將假設咱們要建立一個插件,這個插件用來記錄用戶的動做(更新或刪除的帖子、更改設置、上傳圖片等) 。
3.1 列命名約定
也就是說,在你肯定了自定義表的名稱以後,就要考慮各個列的名稱了。Wordpress核心生成的數據庫的表中的列名是小寫的,在mysql中,列名是大小寫敏感的,因此,我建議你以小寫字母來寫列名,並用下劃線將各個單詞(單詞數少些好)分隔開,以提升可讀性和準確性。在考慮列名的時候,你應該避免使用Mysql的保留字符串 ,若是你的自定義表中有若干列的數據與外部表(特別是Wordpress核心生成的表)相關聯,你最好之外部表中的列名來命名你的列。
在咱們的例子中,咱們將使用如下列名:
log_id - 記錄ID.
user_id - 被記錄的用戶的用戶ID.
activity - 發生的動做.
object_id - 對象的ID,例如 post ID、 user ID、 comment ID 等.
object_type - 對象類型,例如'post', 'user', 'comment' 等.
activity_date - 動做的日期/時間.
3.2肯定自定義表中各個列的類型
下一步就是肯定列的類型了,列的類型可分大體爲三種:字符(串)型、數字型、日期時間型。你能夠從這個頁面 瞭解更多信息。
正確的選擇各個列的數據類型能讓你的數據查詢更高效,有些數據類型容許你設置一個大小限制,例如:varchar(40)容許你最多存儲40個字符(至關於20箇中文字符或符號),這個限制是可選的,可是適當使用,是能提升數據查詢效率的,因此,你須要預估一下你的自定義表中各個列的最大字符長度,數字類型的列的長度指的是數字的位數而不是數字自己的大小,例如,INT(10)容許存放最多10位的非負整數 - 因此最大是到4,294,967,295 。存儲日期時間的話,你須要使用DATETIME類型,該類型的會存儲相似於2014-03-05 14:55:10的值,但你能夠用一個Wordpress函數 mysql2date() 來格式化日期的顯示。
咱們的例子中,各個列的數據類型肯定以下:
log_id - bigint(20)
user_id - bigint(20)
activity - varchar(20)
object_id - bigint(20)
object_type - varchar(20)
date - datetime
3.3 自定義表的索引
接下來你須要肯定這個表的主鍵(PRIMARY KEY)用於索引這個表了。主鍵是一個列,每行都有一個惟一的值 - 一般它只是一個自動遞增的整數,本質上是'行號' 。
其餘索引列的值沒必要是惟一的,但各個行的這個字段的值應該肯定是惟一的。做爲主鍵的索引用來提升查詢效率,若是沒有索引的搜索就必須經過讀整個表來查找匹配的行。主鍵至關於一本書的頁碼,若是說表是一本書的話,各個行是每一頁的話,雖然這個例子不是太合適。關於索引,你能夠經過這個頁面瞭解更多:Mysql如何使用索引 。
3.4 建立自定義表
下面的函數結構用於在插件激活/啓用時建立數據庫的表:
function coolwp_create_my_table() {
// 建立表的代碼放這裏
}
// 激活插件時建立表
register_activation_hook( __FILE__, 'coolwp_create_my_table' );
在這個函數內部,咱們須要包含wp-admin/includes/upgrade.php這個文件,緣由與這篇文章描述的類似:Call to undefined function is_user_logged_in() ,不然,Wordpress的數據庫相關函數: dbDelta()將不會爲你工做。請注意,當插件啓用的時候,你最好就別用init這個HOOK(這是WP內置的掛鉤,翻譯爲鉤子實在難聽)了,因此,調用coolwp_register_activity_log_table()前,須要手工包含這個文件:
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
global $wpdb; http://www.swufe.edu.cn/
global $charset_collate;
// 手工調用這個函數吧
coolwp_register_activity_log_table();
全局變量$charset_collate 包含了字符集和Wordpress原生的數據庫整理,這是I18n所需的。由於你的插件可能會隨Wordpress被用於多種語種。
實例中,咱們的SQL語句是這樣的:
$sql_create_table = "CREATE TABLE {$wpdb->wptuts_activity_log} (
log_id bigint(20) unsigned NOT NULL auto_increment,
user_id bigint(20) unsigned NOT NULL default '0',
activity varchar(20) NOT NULL default 'updated',
object_id bigint(20) unsigned NOT NULL default '0',
object_type varchar(20) NOT NULL default 'post',
activity_date datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (log_id),
KEY user_id (user_id)
) $charset_collate; ";
dbDelta( $sql_create_table );
dbDelta()函數是WP內置的,用來建立這個示例的表。
3.5 調試
若是你啓用了你的插件,發現有以下報錯信息:"你有X個字符的意外輸出...",這一般是由SQL語句中的錯誤引發的,你就須要從新審查下你的SQL語句了。若是你在dbDelta()以後添加wp_die()函數,將不會有報錯,可是錯誤是多是存在的,因此,儘可能不要在這個函數以後添加那個函數。
4. 小結
本文簡述了在Wordpress環境中使用自定義數據表的缺點以及如何建立自定義表。本系列的下一篇文章將會介紹如何防止SQL注入,以提升安全性。
本系列文章所用示例的完整代碼:
<?php
/*
Plugin Name: WP User Activity Log
Version: 1.0
Description: This code complements a series at wp.tutsplus.com on Custom Database Tables
Author: Stephen Harris
Author URI: http://www.stephenharris.info
*/
/**
* Store our table name in $wpdb with correct prefix
* Prefix will vary between sites so hook onto switch_blog too
* @since 1.0
*/
function coolwp_register_activity_log_table(){
global $wpdb;
$wpdb->coolwp_activity_log = "{$wpdb->prefix}coolwp_activity_log";
}
add_action( 'init', 'coolwp_register_activity_log_table',1);
add_action( 'switch_blog', 'coolwp_register_activity_log_table');
/**
* Creates our table
* Hooked onto activate_[plugin] (via register_activation_hook)
* @since 1.0
*/
function coolwp_create_activity_log_table(){
global $wpdb;
global $charset_collate;
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
//Call this manually as we may have missed the init hook
coolwp_register_activity_log_table();
$sql_create_table = "CREATE TABLE {$wpdb->coolwp_activity_log} (
log_id bigint(20) unsigned NOT NULL auto_increment,
user_id bigint(20) unsigned NOT NULL default '0',
activity varchar(30) NOT NULL default 'updated',
object_id bigint(20) unsigned NOT NULL default '0',
object_type varchar(20) NOT NULL default 'post',
activity_date datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (log_id),
KEY abc (user_id)
) $charset_collate; ";
dbDelta($sql_create_table);
}
register_activation_hook(__FILE__,'coolwp_create_activity_log_table');
function coolwp_get_log_table_columns(){
return array(
'log_id'=> '%d',
'user_id'=> '%d',
'activity'=>'%s',
'object_id'=>'%d',
'object_type'=>'%s',
'activity_date'=>'%s',
);
}
/**
* Inserts a log into the database
*
*@param $data array An array of key => value pairs to be inserted
*@return int The log ID of the created activity log. Or WP_Error or false on failure.
*/
function coolwp_insert_log( $data=array() ){
global $wpdb;
//Set default values
$data = wp_parse_args($data, array(
'user_id'=> get_current_user_id(),
'date'=> current_time('timestamp'),
));
//Check date validity
if( !is_float($data['date']) || $data['date'] <= 0 )
return 0;
//Convert activity date from local timestamp to GMT mysql format
$data['activity_date'] = date_i18n( 'Y-m-d H:i:s', $data['date'], true );
//Initialise column format array
$column_formats = coolwp_get_log_table_columns();
//Force fields to lower case
$data = array_change_key_case ( $data );
//White list columns
$data = array_intersect_key($data, $column_formats);
//Reorder $column_formats to match the order of columns given in $data
$data_keys = array_keys($data);
$column_formats = array_merge(array_flip($data_keys), $column_formats);
$wpdb->insert($wpdb->coolwp_activity_log, $data, $column_formats);
return $wpdb->insert_id;
}
/**
* Updates an activity log with supplied data
*
*@param $log_id int ID of the activity log to be updated
*@param $data array An array of column=>value pairs to be updated
*@return bool Whether the log was successfully updated.
*/
function coolwp_update_log( $log_id, $data=array() ){
global $wpdb;
//Log ID must be positive integer
$log_id = absint($log_id);
if( emptyempty($log_id) )
return false;
//Convert activity date from local timestamp to GMT mysql format
if( isset($data['activity_date']) )
$data['activity_date'] = date_i18n( 'Y-m-d H:i:s', $data['date'], true );
//Initialise column format array
$column_formats = coolwp_get_log_table_columns();
//Force fields to lower case
$data = array_change_key_case ( $data );
//White list columns
$data = array_intersect_key($data, $column_formats);
//Reorder $column_formats to match the order of columns given in $data
$data_keys = array_keys($data);
$column_formats = array_merge(array_flip($data_keys), $column_formats);
if ( false === $wpdb->update($wpdb->coolwp_activity_log, $data, array('log_id'=>$log_id), $column_formats) ) {
return false;
}
return true;
}
/**
* Retrieves activity logs from the database matching $query.
* $query is an array which can contain the following keys:
*
* 'fields' - an array of columns to include in returned roles. Or 'count' to count rows. Default: empty (all fields).
* 'orderby' - datetime, user_id or log_id. Default: datetime.
* 'order' - asc or desc
* 'user_id' - user ID to match, or an array of user IDs
* 'since' - timestamp. Return only activities after this date. Default false, no restriction.
* 'until' - timestamp. Return only activities up to this date. Default false, no restriction.
*
*@param $query Query array
*@return array Array of matching logs. False on error.
*/
function coolwp_get_logs( $query=array() ){
global $wpdb;
/* Parse defaults */
$defaults = array(
'fields'=>array(),'orderby'=>'datetime','order'=>'desc', 'user_id'=>false,
'since'=>false,'until'=>false,'number'=>10,'offset'=>0
);
$query = wp_parse_args($query, $defaults);
/* Form a cache key from the query */
$cache_key = 'coolwp_logs:'.md5( serialize($query));
$cache = wp_cache_get( $cache_key );
if ( false !== $cache ) {
$cache = apply_filters('coolwp_get_logs', $cache, $query);
return $cache;
}
extract($query);
/* SQL Select */
//Whitelist of allowed fields
$allowed_fields = coolwp_get_log_table_columns();
if( is_array($fields) ){
//Convert fields to lowercase (as our column names are all lower case - see part 1)
$fields = array_map('strtolower',$fields);
//Sanitize by white listing
$fields = array_intersect($fields, $allowed_fields);
}else{
$fields = strtolower($fields);
}
//Return only selected fields. Empty is interpreted as all
if( emptyempty($fields) ){
$select_sql = "SELECT* FROM {$wpdb->coolwp_activity_log}";
}elseif( 'count' == $fields ) {
$select_sql = "SELECT COUNT(*) FROM {$wpdb->coolwp_activity_log}";
}else{
$select_sql = "SELECT ".implode(',',$fields)." FROM {$wpdb->coolwp_activity_log}";
}
/*SQL Join */
//We don't need this, but we'll allow it be filtered (see 'coolwp_logs_clauses' )
$join_sql='';
/* SQL Where */
//Initialise WHERE
$where_sql = 'WHERE 1=1';
if( !emptyempty($log_id) )
$where_sql .= $wpdb->prepare(' AND log_id=%d', $log_id);
if( !emptyempty($user_id) ){
//Force $user_id to be an array
if( !is_array( $user_id) )
$user_id = array($user_id);
$user_id = array_map('absint',$user_id); //Cast as positive integers
$user_id__in = implode(',',$user_id);
$where_sql .= " AND user_id IN($user_id__in)";
}
$since = absint($since);
$until = absint($until);
if( !emptyempty($since) )
$where_sql .= $wpdb->prepare(' AND activity_date >= %s', date_i18n( 'Y-m-d H:i:s', $since,true));
if( !emptyempty($until) )
$where_sql .= $wpdb->prepare(' AND activity_date <= %s', date_i18n( 'Y-m-d H:i:s', $until,true));
/* SQL Order */
//Whitelist order
$order = strtoupper($order);
$order = ( 'ASC' == $order ? 'ASC' : 'DESC' );
switch( $orderby ){
case 'log_id':
$order_sql = "ORDER BY log_id $order";
break;
case 'user_id':
$order_sql = "ORDER BY user_id $order";
break;
case 'datetime':
$order_sql = "ORDER BY activity_date $order";
default:
break;
}
/* SQL Limit */
$offset = absint($offset); //Positive integer
if( $number == -1 ){
$limit_sql = "";
}else{
$number = absint($number); //Positive integer
$limit_sql = "LIMIT $offset, $number";
}
/* Filter SQL */
$pieces = array( 'select_sql', 'join_sql', 'where_sql', 'order_sql', 'limit_sql' );
$clauses = apply_filters( 'coolwp_logs_clauses', compact( $pieces ), $query );
foreach ( $pieces as $piece )
$$piece = isset( $clauses[ $piece ] ) ? $clauses[ $piece ] : '';
/* Form SQL statement */
$sql = "$select_sql $where_sql $order_sql $limit_sql";
if( 'count' == $fields ){
return $wpdb->get_var($sql);
}
/* Perform query */
$logs = $wpdb->get_results($sql);
/* Add to cache and filter */
wp_cache_add( $cache_key, $logs, 24*60*60 );
$logs = apply_filters('coolwp_get_logs', $logs, $query);
return $logs;
}
/**
* Deletes an activity log from the database
*
*@param $log_id int ID of the activity log to be deleted
*@return bool Whether the log was successfully deleted.
*/
function coolwp_delete_log( $log_id ){
global $wpdb;
//Log ID must be positive integer
$log_id = absint($log_id);
if( emptyempty($log_id) )
return false;
do_action('coolwp_delete_log',$log_id);
$sql = $wpdb->prepare("DELETE from {$wpdb->coolwp_activity_log} WHERE log_id = %d", $log_id);
if( !$wpdb->query( $sql ) )
return false;
do_action('coolwp_deleted_log',$log_id);
return true;
} html