WordPress 数据库操作
WordPress 数据库概述
数据库表结构
WordPress 默认表:
┌────────────────────────────────────────────────────────────┐
│ wp_posts - 所有文章、页面、附件内容 │
│ wp_postmeta - 文章/页面的元数据(自定义字段) │
│ wp_comments - 评论数据 │
│ wp_commentmeta - 评论的元数据 │
│ wp_terms - 分类和标签 │
│ wp_term_taxonomy - 分类法(分类目录、标签等) │
│ wp_term_relationships - 文章与分类/标签的关联 │
│ wp_termmeta - 分类/标签的元数据 │
│ wp_users - 用户数据 │
│ wp_usermeta - 用户的元数据 │
│ wp_options - 站点设置和选项 │
└────────────────────────────────────────────────────────────┘表关系图
posts ─────────────┬─────────────── postmeta
│ │ │
│ │ │
▼ ▼ │
posts ────────────▼─── comments ──────── commentmeta
│
│
▼
term_relationships ──── term_taxonomy ──── terms ──── termeta
users ──────────────── usermeta全局对象
$wpdb 对象
php
<?php
global $wpdb;
// 直接执行 SQL
$results = $wpdb->get_results("SELECT * FROM {$wpdb->posts} WHERE post_type = 'post'");
// 获取一行
$row = $wpdb->get_row("SELECT * FROM {$wpdb->posts} WHERE ID = 1");
// 获取一个值
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts}");
// 获取一列
$titles = $wpdb->get_col("SELECT post_title FROM {$wpdb->posts}");
?>表前缀
php
<?php
// 安全地引用表
$wpdb->posts // wp_posts
$wpdb->postmeta // wp_postmeta
$wpdb->comments // wp_comments
$wpdb->commentmeta
$wpdb->terms
$wpdb->term_taxonomy
$wpdb->term_relationships
$wpdb->termmeta
$wpdb->users
$wpdb->usermeta
$wpdb->options
// 自定义表(需要 $wpdb->prefix)
$table_name = $wpdb->prefix . 'my_custom_table';
?>基础查询
SELECT 查询
php
<?php
// get_results - 获取多行
$posts = $wpdb->get_results(
"SELECT ID, post_title, post_content
FROM {$wpdb->posts}
WHERE post_type = 'post'
AND post_status = 'publish'
ORDER BY post_date DESC
LIMIT 10"
);
foreach ($posts as $post) {
echo $post->post_title;
}
// 使用占位符(防注入)
$post_type = 'post';
$posts = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s",
$post_type,
'publish'
)
);
// 数字占位符 %d, 字符串 %s, 浮点数 %f
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d AND post_type = %s",
$id, // %d 整数
$post_type // %s 字符串
);
?>插入数据
php
<?php
// insert() 方法
$wpdb->insert(
$wpdb->prefix . 'my_table',
array(
'name' => '张三',
'email' => '[email protected]',
'created' => current_time('mysql'),
),
array(
'%s', // name - 字符串
'%s', // email - 字符串
'%s', // created - 字符串
)
);
// 获取插入的 ID
$insert_id = $wpdb->insert_id;
// 完整示例:记录访问日志
function log_visit($post_id, $visitor_ip) {
global $wpdb;
$table = $wpdb->prefix . 'visit_logs';
$result = $wpdb->insert(
$table,
array(
'post_id' => $post_id,
'visitor_ip' => $visitor_ip,
'visit_date' => current_time('mysql'),
),
array('%d', '%s', '%s')
);
return $result !== false;
}
?>更新数据
php
<?php
// update() 方法
$wpdb->update(
$wpdb->prefix . 'my_table',
array(
'name' => '李四',
'email' => '[email protected]',
),
array('id' => 1),
array('%s', '%s'), // 新值格式
array('%d') // WHERE 条件格式
);
// 示例:更新文章阅读数
function increment_views($post_id) {
global $wpdb;
// 使用 %d 占位符
$table = $wpdb->prefix . 'post_stats';
return $wpdb->query(
$wpdb->prepare(
"UPDATE {$table} SET views = views + 1 WHERE post_id = %d",
$post_id
)
);
}
?>删除数据
php
<?php
// delete() 方法
$wpdb->delete(
$wpdb->prefix . 'my_table',
array('id' => 5),
array('%d')
);
// 示例:清理过期数据
function cleanup_expired_data() {
global $wpdb;
return $wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}transient_logs
WHERE expires < %s",
current_time('mysql')
)
);
}
?>自定义数据库表
创建表
php
<?php
/**
* 创建自定义表
*/
function create_custom_table() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'portfolio_clients';
$sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
email varchar(255) NOT NULL,
company varchar(255),
phone varchar(50),
notes text,
status varchar(20) DEFAULT 'active',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY status (status),
KEY email (email)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
// 激活插件时创建
register_activation_hook(__FILE__, 'create_custom_table');
?>完整 CRUD 示例
php
<?php
/**
* 客户管理类
*/
class Portfolio_Client_Manager {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'portfolio_clients';
}
/**
* 创建客户
*/
public function create($data) {
global $wpdb;
$result = $wpdb->insert(
$this->table_name,
array(
'name' => sanitize_text_field($data['name']),
'email' => sanitize_email($data['email']),
'company' => sanitize_text_field($data['company'] ?? ''),
'phone' => sanitize_text_field($data['phone'] ?? ''),
'notes' => wp_kses_post($data['notes'] ?? ''),
'status' => sanitize_key($data['status'] ?? 'active'),
),
array('%s', '%s', '%s', '%s', '%s', '%s')
);
if ($result === false) {
return new WP_Error('db_error', '创建客户失败');
}
return $wpdb->insert_id;
}
/**
* 获取客户
*/
public function get($id) {
global $wpdb;
return $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$this->table_name} WHERE id = %d",
$id
)
);
}
/**
* 获取所有客户
*/
public function get_all($args = array()) {
global $wpdb;
$defaults = array(
'status' => '',
'orderby' => 'id',
'order' => 'DESC',
'limit' => 20,
'offset' => 0,
);
$args = wp_parse_args($args, $defaults);
$sql = "SELECT * FROM {$this->table_name}";
$where = array();
$values = array();
if (!empty($args['status'])) {
$where[] = 'status = %s';
$values[] = $args['status'];
}
if (!empty($where)) {
$sql .= ' WHERE ' . implode(' AND ', $where);
}
$sql .= " ORDER BY {$args['orderby']} {$args['order']}";
$sql .= " LIMIT {$args['limit']} OFFSET {$args['offset']}";
if (!empty($values)) {
$sql = $wpdb->prepare($sql, $values);
}
return $wpdb->get_results($sql);
}
/**
* 更新客户
*/
public function update($id, $data) {
global $wpdb;
$update_data = array();
$format = array();
foreach ($data as $key => $value) {
if (in_array($key, array('name', 'email', 'company', 'phone', 'notes', 'status'))) {
$update_data[$key] = sanitize_text_field($value);
$format[] = '%s';
}
}
if (empty($update_data)) {
return false;
}
return $wpdb->update(
$this->table_name,
$update_data,
array('id' => $id),
$format,
array('%d')
);
}
/**
* 删除客户
*/
public function delete($id) {
global $wpdb;
return $wpdb->delete(
$this->table_name,
array('id' => $id),
array('%d')
);
}
/**
* 搜索客户
*/
public function search($keyword) {
global $wpdb;
return $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$this->table_name}
WHERE name LIKE %s
OR email LIKE %s
OR company LIKE %s
ORDER BY name ASC",
'%' . $wpdb->esc_like($keyword) . '%',
'%' . $wpdb->esc_like($keyword) . '%',
'%' . $wpdb->esc_like($keyword) . '%'
)
);
}
/**
* 统计客户数
*/
public function count($status = '') {
global $wpdb;
if (empty($status)) {
return (int) $wpdb->get_var(
"SELECT COUNT(*) FROM {$this->table_name}"
);
}
return (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM {$this->table_name} WHERE status = %s",
$status
)
);
}
}
?>WordPress Meta API
Post Meta
php
<?php
// 添加 meta
add_post_meta($post_id, 'key', $value, $unique = false);
// 获取 meta
$value = get_post_meta($post_id, 'key', $single = false);
// $single = true 返回字符串/false
// $single = false 返回数组
// 更新 meta
update_post_meta($post_id, 'key', $new_value, $old_value = '');
// 删除 meta
delete_post_meta($post_id, $key, $value = '');
// 示例
$views = get_post_meta($post_id, 'views', true); // 获取阅读数
$views = (int) $views + 1;
update_post_meta($post_id, 'views', $views); // 更新阅读数
?>User Meta
php
<?php
// 添加用户 meta
add_user_meta($user_id, 'twitter', '@username');
// 获取用户 meta
$twitter = get_user_meta($user_id, 'twitter', true);
// 更新用户 meta
update_user_meta($user_id, 'twitter', '@new_username');
// 删除用户 meta
delete_user_meta($user_id, 'twitter');
// 示例:用户配置
$preferences = get_user_meta($user_id, 'preferences', true);
if (!$preferences) {
$preferences = array(
'theme' => 'light',
'email_notifications' => true,
);
}
update_user_meta($user_id, 'preferences', $preferences);
?>Term Meta
php
<?php
// 添加术语 meta
add_term_meta($term_id, 'color', '#21759b');
// 获取术语 meta
$color = get_term_meta($term_id, 'color', true);
// 更新术语 meta
update_term_meta($term_id, 'color', '#f1851a');
// 删除术语 meta
delete_term_meta($term_id, 'color');
?>数据库安全
防 SQL 注入
php
<?php
// ✅ 使用 prepare() 方法
$sql = $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d AND post_type = %s",
$post_id, // 整数
$post_type // 字符串
);
$results = $wpdb->get_results($sql);
// ✅ 使用 wpdb->esc_like() 转义搜索
$search = $wpdb->esc_like($_GET['s']);
$sql = $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_title LIKE %s",
'%' . $search . '%'
);
// ❌ 不安全:直接拼接
$sql = "SELECT * FROM {$wpdb->posts} WHERE ID = " . $_GET['id']; // 危险!
// ❌ 不安全:虽然简单,但不要用
$sql = "SELECT * FROM {$wpdb->posts} WHERE ID = " . intval($_GET['id']); // 不推荐
?>数据验证和清理
php
<?php
// 验证整数
$id = absint($_GET['id']); // 正整数
$id = (int) $_GET['id']; // 有符号整数
$id = intval($_GET['id']); // 同上
// 验证浮点数
$price = (float) $_POST['price'];
// 清理 URL
$url = esc_url_raw($_GET['url']); // 清理用于数据库
$url = esc_url($_GET['url']); // 清理用于显示
// 清理 HTML
$html = wp_kses_post($_POST['content']); // 允许基本 HTML
$html = wp_kses($_POST['content'], $allowed_tags); // 自定义允许标签
// 清理邮箱
$email = sanitize_email($_POST['email']);
// 清理文本
$text = sanitize_text_field($_POST['name']);
$text = sanitize_textarea_field($_POST['message']);
$text = sanitize_title($_POST['title']); // 用于 slug
// 清理 Key
$key = sanitize_key($_GET['key']);
// 清理文件路径
$file = sanitize_file($_GET['file']);
// 清理选择
$choice = sanitize_html_class($_GET['class']);
// 转义输出
echo esc_html($text);
echo esc_attr($class);
echo esc_url($url);
echo esc_js($string);
echo wp_kses_post($html);
?>数据库性能优化
索引使用
php
<?php
// 创建索引以优化查询
$sql = "CREATE INDEX idx_post_status_date
ON {$wpdb->posts}(post_status, post_date)";
// 复合索引示例
$sql = "CREATE INDEX idx_user_status
ON {$wpdb->usermeta}(user_id, meta_key)";
?>查询优化
php
<?php
// ✅ 使用 SELECT 只需要的字段
$wpdb->get_row(
"SELECT ID, post_title FROM {$wpdb->posts} WHERE ID = %d",
$id
);
// ✅ 使用缓存
$cache_key = 'post_' . $post_id;
$cached = wp_cache_get($cache_key);
if ($cached === false) {
$cached = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE ID = %d",
$post_id
)
);
wp_cache_set($cache_key, $cached, 'posts', HOUR_IN_SECONDS);
}
// ✅ 使用 transients 存储临时数据
set_transient('my_temp_data', $data, HOUR_IN_SECONDS);
$data = get_transient('my_temp_data');
delete_transient('my_temp_data');
// ✅ 批量操作
$wpdb->query("BEGIN");
foreach ($ids as $id) {
$wpdb->update(...);
}
$wpdb->query("COMMIT");
?>数据库迁移
升级表结构
php
<?php
/**
* 插件升级时更新数据库
*/
function my_plugin_update_db_check() {
$saved_version = get_option('my_plugin_version');
if ($saved_version !== MY_PLUGIN_VERSION) {
// 执行升级
update_database_structure();
// 更新版本记录
update_option('my_plugin_version', MY_PLUGIN_VERSION);
}
}
add_action('plugins_loaded', 'my_plugin_update_db_check');
function update_database_structure() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'my_plugin_table';
// 添加新列
$column_exists = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = 'new_column'",
DB_NAME,
$table_name
)
);
if (!$column_exists) {
$wpdb->query(
"ALTER TABLE {$table_name} ADD COLUMN new_column VARCHAR(255)"
);
}
}
?>调试数据库查询
php
<?php
// 保存所有查询
add_filter('query', 'log_queries');
function log_queries($query) {
global $wpdb;
$wpdb->queries[] = $query;
return $query;
}
// 输出查询日志
function debug_queries() {
global $wpdb;
if (current_user_can('manage_options') && isset($_GET['debug_queries'])) {
echo '<pre>';
foreach ($wpdb->queries as $query) {
echo esc_html($query) . "\n\n";
}
echo '</pre>';
}
}
add_action('wp_footer', 'debug_queries');
// 使用 Query Monitor 插件(推荐)
// 提供图形界面和详细分析
?>