咳咳,各位同学,欢迎来到今天的WordPress源码深度解析小课堂!今天我们要聊的是WordPress里面一个非常重要,但又常常被大家忽略的小可爱 —— post meta
。 更具体地说,我们要扒一扒add_post_meta()
和get_post_meta()
这两个函数背后,跟数据库是如何眉来眼去的。准备好了吗? Let’s dive in!
Post Meta 是个啥?
首先,咱们得明白post meta
到底是干嘛的。简单来说,它就是给你的文章、页面,甚至是自定义文章类型(custom post type)添加额外信息的一种方式。 想象一下,你写了一篇电影影评,除了标题、正文之外,你还想记录这部电影的评分、导演、主演等等信息。这些信息就可以通过post meta
来存储。
与直接修改wp_posts
表不同,post meta
提供了一种更加灵活、可扩展的方式来存储这些额外信息。它把这些信息存在另一个表里,也就是我们今天要重点关注的wp_postmeta
表。
wp_postmeta
表的结构
在我们深入代码之前,先来看看wp_postmeta
表长什么样:
列名 | 数据类型 | 描述 |
---|---|---|
meta_id | bigint(20) unsigned | 唯一标识符,自增主键 |
post_id | bigint(20) unsigned | 关联的文章/页面的ID,外键,指向wp_posts.ID |
meta_key | varchar(255) | 元数据的键名,比如rating 、director 等 |
meta_value | longtext | 元数据的值,可以是字符串、数字、甚至是序列化后的数组或对象 |
add_post_meta()
:小数据的搬运工
add_post_meta()
函数的作用,就是往wp_postmeta
表里插入一条新的记录。 它的基本语法是:
add_post_meta( int $post_id, string $meta_key, mixed $meta_value, bool $unique = false ) : int|false
参数解释:
$post_id
: 文章/页面的ID。$meta_key
: 元数据的键名。$meta_value
: 元数据的值。$unique
: 是否只允许存在一个相同键名的元数据。如果设置为true
,并且已经存在相同键名的元数据,则不会插入新的记录。
让我们来看一段简化的add_post_meta()
源码(删除了部分错误处理和钩子):
function add_post_meta( $post_id, $meta_key, $meta_value, $unique = false ) {
global $wpdb;
$post_id = (int) $post_id;
if ( ! is_string( $meta_key ) ) {
return false;
}
$meta_type = 'post'; //Meta type for post
$meta_key = wp_unslash( $meta_key );
$meta_value = wp_unslash( $meta_value );
if ( $unique ) {
$check = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key ) );
if ( $check ) {
return false; // 已经存在相同的键名,不插入
}
}
$meta_value_serialized = maybe_serialize( $meta_value ); // 序列化复杂数据类型
$result = $wpdb->insert(
$wpdb->postmeta,
array(
'post_id' => $post_id,
'meta_key' => $meta_key,
'meta_value' => $meta_value_serialized,
),
array(
'%d',
'%s',
'%s',
)
);
if ( $result ) {
wp_cache_delete( $post_id, $meta_type . '_meta' ); // 清除缓存
return $wpdb->insert_id; // 返回新插入的meta_id
}
return false;
}
代码解读:
- 参数验证和清理: 函数首先对输入的参数进行类型验证和清理,确保数据的安全性。
unique
检查: 如果设置了$unique
为true
,函数会先查询数据库,看看是否已经存在相同的post_id
和meta_key
的记录。如果存在,则直接返回false
,阻止插入。- 数据序列化:
maybe_serialize()
函数非常重要。因为meta_value
可以存储各种类型的数据,包括数组和对象。为了方便存储到数据库的longtext
字段,需要将这些复杂的数据类型序列化成字符串。 - 数据库插入: 使用
$wpdb->insert()
函数向wp_postmeta
表插入一条新的记录。$wpdb
是WordPress的数据库操作对象,它提供了一系列方法来执行SQL查询。$wpdb->postmeta
: 指定要插入的表名,即wp_postmeta
。array(...)
: 指定要插入的字段和对应的值。array(...)
: 指定字段对应的数据类型,用于防止SQL注入。
- 清除缓存:
wp_cache_delete()
函数用于清除缓存,确保下次获取元数据时,能获取到最新的值。 - 返回结果: 如果插入成功,返回新插入的
meta_id
;否则,返回false
。
举个例子:
$post_id = 123;
$movie_rating = 8.5;
$result = add_post_meta( $post_id, 'movie_rating', $movie_rating, true );
if ( $result ) {
echo "成功添加电影评分,meta_id: " . $result;
} else {
echo "添加电影评分失败";
}
这段代码会在wp_postmeta
表中插入一条记录,post_id
为123,meta_key
为movie_rating
,meta_value
为8.5。由于unique
设置为true
,如果已经存在post_id
为123,meta_key
为movie_rating
的记录,则不会插入新的记录。
get_post_meta()
:从数据库里捞数据
get_post_meta()
函数的作用,就是从wp_postmeta
表里根据给定的post_id
和meta_key
,获取对应的元数据值。 它的基本语法是:
get_post_meta( int $post_id, string $key = '', bool $single = false ) : mixed
参数解释:
$post_id
: 文章/页面的ID。$key
: 元数据的键名。如果为空,则返回所有与post_id
相关的元数据。$single
: 是否只返回单个值。如果设置为true
,则返回第一个匹配的元数据值;否则,返回一个包含所有匹配值的数组。
让我们来看一段简化的get_post_meta()
源码(同样删除了部分错误处理和钩子):
function get_post_meta( $post_id, $key = '', $single = false ) {
global $wpdb, $_wp_post_meta_cache;
$post_id = (int) $post_id;
$meta_type = 'post'; //Meta type for post
if ( isset( $_wp_post_meta_cache[ $post_id ] ) ) {
$meta_cache = $_wp_post_meta_cache[ $post_id ];
} else {
$meta_cache = update_meta_cache( $meta_type, array( $post_id ) );
$meta_cache = isset( $_wp_post_meta_cache[ $post_id ] ) ? $_wp_post_meta_cache[ $post_id ] : array();
}
if ( empty( $key ) ) {
return $meta_cache;
}
if ( isset( $meta_cache[ $key ] ) ) {
$values = $meta_cache[ $key ];
} else {
return $single ? '' : array();
}
if ( $single ) {
return maybe_unserialize( $values[0] ); // 反序列化
} else {
foreach ( $values as $index => $value ) {
$values[ $index ] = maybe_unserialize( $value ); // 反序列化
}
return $values;
}
}
代码解读:
- 参数验证: 对输入的参数进行类型验证,确保数据的安全性。
- 缓存机制: 这是
get_post_meta()
性能优化的关键。 WordPress会将已经获取过的post meta
数据缓存到全局变量$_wp_post_meta_cache
中。 这样,下次获取相同post_id
的元数据时,可以直接从缓存中读取,避免重复查询数据库。isset( $_wp_post_meta_cache[ $post_id ] )
: 检查缓存中是否已经存在post_id
对应的元数据。update_meta_cache()
: 如果缓存中没有,则调用update_meta_cache()
函数从数据库中获取,并更新缓存。
- 获取所有元数据: 如果
$key
为空,则直接返回缓存中post_id
对应的所有元数据。 - 获取指定元数据: 如果
$key
不为空,则从缓存中查找key
对应的元数据值。 - 数据反序列化:
maybe_unserialize()
函数与add_post_meta()
中的maybe_serialize()
相对应。 它会将序列化后的字符串反序列化成原来的数据类型(数组或对象)。 - 返回结果: 如果
$single
为true
,则返回第一个匹配的元数据值;否则,返回一个包含所有匹配值的数组。
update_meta_cache()
函数源码简化如下:
function update_meta_cache( $meta_type, $object_ids ) {
global $wpdb, $_wp_post_meta_cache;
$object_ids = array_map( 'intval', (array) $object_ids );
$object_ids = array_filter( $object_ids );
if ( empty( $object_ids ) ) {
return;
}
switch ( $meta_type ) {
case 'post':
$table = $wpdb->postmeta;
$id_column = 'post_id';
$cache =& $_wp_post_meta_cache;
break;
default:
return false;
}
$id_list = implode( ',', $object_ids );
$query = "SELECT meta_key, meta_value, $id_column FROM $table WHERE $id_column IN ($id_list) ORDER BY meta_key";
$meta_list = $wpdb->get_results( $query, ARRAY_A );
if ( ! empty( $meta_list ) ) {
foreach ( $meta_list as $metarow ) {
$object_id = (int) $metarow[ $id_column ];
$meta_key = $metarow['meta_key'];
$meta_value = $metarow['meta_value'];
$cache[ $object_id ][ $meta_key ][] = $meta_value;
}
} else {
return false;
}
return $cache;
}
代码解读:
- 确定表名和ID列:根据
meta_type
(这里是’post’),确定要查询的表(wp_postmeta
)和ID列(post_id
)。 - 构建SQL查询:构造一个SQL查询,从
wp_postmeta
表中获取所有与给定object_ids
(文章ID)相关的元数据,并按照meta_key
排序。 - 执行查询:使用
$wpdb->get_results()
执行查询,获取结果集。 - 填充缓存:遍历结果集,将每个元数据项添加到全局缓存
$_wp_post_meta_cache
中。
举个例子:
$post_id = 123;
$movie_rating = get_post_meta( $post_id, 'movie_rating', true );
if ( $movie_rating ) {
echo "电影评分: " . $movie_rating;
} else {
echo "未找到电影评分";
}
这段代码会从wp_postmeta
表中获取post_id
为123,meta_key
为movie_rating
的元数据值。由于$single
设置为true
,所以只会返回一个值。
update_post_meta()
:更新数据,旧貌换新颜
除了添加和获取,我们有时候还需要更新post meta
。 这时候就要用到update_post_meta()
函数。它的基本语法是:
update_post_meta( int $post_id, string $meta_key, mixed $meta_value, mixed $prev_value = '' ) : int|bool
参数解释:
$post_id
: 文章/页面的ID。$meta_key
: 元数据的键名。$meta_value
: 元数据的新值。$prev_value
: 可选参数,旧值。如果指定了旧值,则只有在数据库中存在与旧值匹配的记录时,才会更新。
update_post_meta()
的源码可以简化为:
function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) {
global $wpdb;
$post_id = (int) $post_id;
if ( ! is_string( $meta_key ) ) {
return false;
}
$meta_type = 'post'; //Meta type for post
$meta_key = wp_unslash( $meta_key );
$meta_value = wp_unslash( $meta_value );
$meta_value_serialized = maybe_serialize( $meta_value );
$null = null;
if ( is_null( $prev_value ) ) {
$prev_value = $null;
} else {
$prev_value = wp_unslash( $prev_value );
$prev_value = maybe_serialize( $prev_value );
}
$data = array( 'meta_value' => $meta_value_serialized );
$where = array( 'post_id' => $post_id, 'meta_key' => $meta_key );
$format = array( '%s' );
$where_format = array( '%d', '%s' );
if ( '' !== $prev_value ) {
$where['meta_value'] = $prev_value;
$where_format[] = '%s';
}
$result = $wpdb->update( $wpdb->postmeta, $data, $where, $format, $where_format );
if ( ! $result ) {
if ( ! get_post_meta( $post_id, $meta_key, true ) ) {
return add_post_meta( $post_id, $meta_key, $meta_value );
} else {
return false;
}
}
wp_cache_delete( $post_id, $meta_type . '_meta' );
return $result;
}
代码解读:
- 参数验证和清理:类似于
add_post_meta()
,对输入参数进行类型验证和清理。 - 数据序列化:将
$meta_value
和$prev_value
序列化。 - 构建SQL UPDATE语句:使用
$wpdb->update()
函数构造SQL UPDATE语句,更新wp_postmeta
表中符合条件的记录。$data
: 指定要更新的字段和对应的新值(meta_value
)。$where
: 指定更新的条件(post_id
和meta_key
)。 如果提供了$prev_value
,则还会加上meta_value
的条件。$format
: 指定$data
中字段的数据类型。$where_format
: 指定$where
中字段的数据类型。
- 处理更新失败的情况:如果更新失败,且数据库中不存在该
post meta
, 则调用add_post_meta()
添加一条记录,否则返回false - 清除缓存:更新缓存,确保下次获取元数据时能获取到最新的值。
- 返回结果:返回更新的行数。
delete_post_meta()
:删除数据,干净利落
最后,我们再来看看delete_post_meta()
函数,它的作用是从wp_postmeta
表中删除指定的元数据。 它的基本语法是:
delete_post_meta( int $post_id, string $meta_key, mixed $meta_value = '' ) : bool
参数解释:
$post_id
: 文章/页面的ID。$meta_key
: 元数据的键名。$meta_value
: 可选参数,元数据的值。如果指定了值,则只删除与值匹配的记录。
delete_post_meta()
的源码简化如下:
function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) {
global $wpdb;
$post_id = (int) $post_id;
if ( ! is_string( $meta_key ) ) {
return false;
}
$meta_type = 'post'; //Meta type for post
$meta_key = wp_unslash( $meta_key );
$meta_value = wp_unslash( $meta_value );
$null = null;
if ( is_null( $meta_value ) ) {
$meta_value = $null;
} else {
$meta_value = maybe_serialize( $meta_value );
}
$query = "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s";
$args = array( $post_id, $meta_key );
if ( '' !== $meta_value ) {
$query .= ' AND meta_value = %s';
$args[] = $meta_value;
}
$result = $wpdb->query( $wpdb->prepare( $query, $args ) );
wp_cache_delete( $post_id, $meta_type . '_meta' );
return (bool) $result;
}
代码解读:
- 参数验证和清理:对输入参数进行类型验证和清理。
- 数据序列化:如果提供了
$meta_value
,则将其序列化。 - 构建SQL DELETE语句:使用
$wpdb->query()
函数构造SQL DELETE语句,删除wp_postmeta
表中符合条件的记录。- 如果提供了
$meta_value
,则SQL语句会包含meta_value
的条件。
- 如果提供了
- 清除缓存:更新缓存,确保下次获取元数据时不会获取到已删除的值。
- 返回结果:返回
true
表示删除成功,否则返回false
。
总结
好了,今天的post meta
数据库交互之旅就到这里。 我们一起扒了add_post_meta()
、get_post_meta()
、update_post_meta()
和delete_post_meta()
这几个函数的源码, 了解了它们是如何与wp_postmeta
表交互,以及WordPress是如何使用缓存来优化性能的。
记住,post meta
是一个非常强大的工具,可以让你灵活地扩展WordPress的功能。 掌握了它的原理,你就可以更加自信地使用它,并根据自己的需求进行定制。
下课! 希望大家有所收获,下次再见!