大家好,各位码农朋友们,欢迎来到今天的“WordPress源码解密”讲座。今天我们要聊的是WordPress的核心函数之一,wp_insert_post()
,重点剖析它是如何处理post_meta
数据并触发相关action
的。准备好了吗?咱们开车啦!
一、wp_insert_post()
:总指挥的角色
wp_insert_post()
函数,顾名思义,负责在WordPress中插入或者更新一篇post(文章、页面、自定义文章类型等)。别看它名字简单,实际上它是个功能强大的总指挥,协调着各种小弟(函数、钩子)完成任务。
它的基本流程是这样的:
- 数据校验和预处理: 检查传入的数据是否合法,并进行一些必要的转换。
- 数据库操作: 根据传入的数据,插入或更新
wp_posts
表中的记录。 - 处理
post_meta
: 这是我们今天的主角,负责添加、更新或删除文章的自定义字段。 - 触发
action
: 在不同的阶段触发各种action钩子,允许其他插件或主题介入整个过程。
二、post_meta
:文章的“私房钱”
post_meta
,也就是文章的自定义字段,可以理解为文章的“私房钱”,用来存储一些文章本身不包含,但又非常重要的信息。比如:
- 电影的评分
- 书籍的ISBN
- 产品的价格
- 等等…
post_meta
存储在wp_postmeta
表中,主要有四个字段:
字段名 | 类型 | 描述 |
---|---|---|
meta_id |
BIGINT | 自增主键 |
post_id |
BIGINT | 关联的文章ID |
meta_key |
VARCHAR | 自定义字段的键名(key) |
meta_value |
LONGTEXT | 自定义字段的值(value),可以序列化 |
三、wp_insert_post()
中的post_meta
处理:源码深度游
好了,现在让我们深入wp_insert_post()
的源码,看看它是如何处理post_meta
的。 为了方便阅读,我们只截取关键部分的代码,并添加注释。
function wp_insert_post( $postarr = array(), $wp_error = false ) {
// ... 前面的数据校验和处理 ...
$post_ID = (int) $postarr['ID'];
// ... 数据库操作,插入或更新wp_posts表 ...
// 处理post_meta
if ( isset( $postarr['meta_input'] ) && is_array( $postarr['meta_input'] ) ) {
$meta_input = $postarr['meta_input'];
foreach ( $meta_input as $key => $value ) {
update_post_meta( $post_ID, $key, $value );
}
}
// ... 后面的操作,触发action等 ...
return $post_ID;
}
这段代码告诉我们,wp_insert_post()
首先检查$postarr
数组中是否存在meta_input
键,并且它的值是一个数组。如果存在,它会遍历这个数组,然后调用update_post_meta()
函数来更新每一个post_meta
。
四、update_post_meta()
:post_meta
的“管家”
update_post_meta()
函数是处理post_meta
的核心函数,它的职责是:
- 检查
meta_key
是否合法。 - 检查
post_meta
是否存在,如果存在则更新,如果不存在则添加。 - 在更新或添加前后触发相应的
action
。
让我们看看update_post_meta()
的源码:
function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) {
// 1. 数据校验
$post_id = absint( $post_id );
if ( ! is_string( $meta_key ) ) {
return false;
}
$meta_key = wp_unslash( $meta_key );
$passed_value = $meta_value;
$meta_value = wp_unslash( $meta_value );
$meta_key = apply_filters( 'sanitize_key', $meta_key, $meta_key );
if ( ! $post_id || ! $meta_key ) {
return false;
}
// 2. 权限检查 (省略,实际代码中有权限检查)
// 3. 获取现有的meta值
$cur_value = get_post_meta( $post_id, $meta_key, true );
// 4. 如果新的meta_value和旧的meta_value相同,直接返回,不做任何操作
if ( $meta_value === $cur_value ) {
return false;
}
// 5. 如果没有传递$prev_value,则更新所有匹配$meta_key的meta值
if ( '' === $prev_value ) {
return update_metadata( 'post', $post_id, $meta_key, $meta_value );
}
// 6. 如果传递了$prev_value,则只更新匹配$prev_value的meta值
$prev_value = wp_unslash( $prev_value );
return update_metadata( 'post', $post_id, $meta_key, $meta_value, $prev_value );
}
update_post_meta()
函数首先进行了一些数据校验,然后通过get_post_meta()
函数获取现有的meta_value
,如果新的meta_value
和旧的meta_value
相同,则直接返回,不做任何操作。
核心逻辑是通过update_metadata()
来实现的。
五、update_metadata()
:真正的执行者
update_metadata()
是一个通用的元数据更新函数,可以用于更新post_meta
、user_meta
、term_meta
等。让我们看看它的源码(同样只截取关键部分):
function update_metadata( $meta_type, $object_id, $meta_key, $meta_value, $prev_value = '' ) {
global $wpdb;
// 1. 数据校验
if ( ! $meta_type || ! $object_id || ! is_string( $meta_key ) ) {
return false;
}
$table = _get_meta_table( $meta_type );
if ( ! $table ) {
return false;
}
$column = sanitize_key( $meta_type . '_id' );
$object_id = absint( $object_id );
$meta_key = trim( $meta_key );
if ( empty( $meta_key ) ) {
return false;
}
$meta_value = maybe_serialize( $meta_value );
$data = compact( 'meta_key', 'meta_value' );
$where = array( $column => $object_id, 'meta_key' => $meta_key );
// 2. 如果传递了$prev_value,则只更新匹配$prev_value的meta值
if ( '' !== $prev_value ) {
$prev_value = maybe_serialize( $prev_value );
$where['meta_value'] = $prev_value;
$check = $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM {$table} WHERE {$column} = %d AND meta_key = %s AND meta_value = %s", $object_id, $meta_key, $prev_value ) );
if ( ! $check ) {
return false;
}
$result = $wpdb->update( $table, $data, $where );
} else { // 3. 如果没有传递$prev_value,则更新所有匹配$meta_key的meta值
if ( get_metadata( $meta_type, $object_id, $meta_key, true ) ) {
$result = $wpdb->update( $table, $data, $where );
} else { //如果不存在,新增
$result = add_metadata( $meta_type, $object_id, $meta_key, $meta_value );
}
}
if ( $result ) {
wp_cache_delete( $object_id, $meta_type . '_meta' );
do_action( "updated_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $meta_value );
do_action( 'update_metadata', $meta_id, $object_id, $meta_key, $meta_value );
return true;
}
return false;
}
update_metadata()
函数首先进行数据校验,然后根据是否传递了$prev_value
来决定更新策略。
- 如果传递了
$prev_value
: 只更新meta_value
等于$prev_value
的post_meta
。 - 如果没有传递
$prev_value
: 更新所有meta_key
相同的post_meta
。如果不存在,则新增。
最后,update_metadata()
会触发updated_{$meta_type}_meta
和update_metadata
这两个action
钩子,让其他插件或主题有机会介入post_meta
的更新过程。
六、add_post_meta()
:post_meta
的“新家”
如果update_metadata
判断post_meta
不存在,则会调用add_metadata()
来添加新的post_meta
。让我们看看add_metadata()
的源码:
function add_metadata( $meta_type, $object_id, $meta_key, $meta_value, $unique = false ) {
global $wpdb;
// 1. 数据校验
if ( ! $meta_type || ! $object_id || ! is_string( $meta_key ) ) {
return false;
}
$table = _get_meta_table( $meta_type );
if ( ! $table ) {
return false;
}
$column = sanitize_key( $meta_type . '_id' );
$object_id = absint( $object_id );
$meta_key = trim( $meta_key );
if ( empty( $meta_key ) ) {
return false;
}
// 2. 检查是否unique
if ( $unique && get_metadata( $meta_type, $object_id, $meta_key, true ) ) {
return false;
}
$meta_value = maybe_serialize( $meta_value );
$data = array(
$column => $object_id,
'meta_key' => $meta_key,
'meta_value' => $meta_value,
);
$format = array(
'%d',
'%s',
'%s',
);
// 3. 插入数据
$wpdb->insert( $table, $data, $format );
$id = $wpdb->insert_id;
if ( ! $id ) {
return false;
}
wp_cache_delete( $object_id, $meta_type . '_meta' );
do_action( "added_{$meta_type}_meta", $id, $object_id, $meta_key, $meta_value );
do_action( 'add_metadata', $id, $object_id, $meta_key, $meta_value );
return $id;
}
add_metadata()
函数首先进行数据校验,然后检查是否设置了unique
参数,如果设置了,并且已经存在相同的meta_key
,则直接返回。
最后,add_metadata()
会将数据插入到数据库中,并触发added_{$meta_type}_meta
和add_metadata
这两个action
钩子,给其他插件或主题机会介入post_meta
的添加过程。
七、delete_post_meta()
:post_meta
的“搬家公司”
除了添加和更新,wp_insert_post
还可能需要删除post_meta
。虽然wp_insert_post
本身不直接调用delete_post_meta()
, 但是理解它对于整体的post_meta
管理是很重要的。delete_post_meta()
函数的作用就是删除指定的post_meta
。 让我们看看它的源码:
function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) {
return delete_metadata( 'post', $post_id, $meta_key, $meta_value );
}
它仅仅是调用了delete_metadata
函数。我们再看一下delete_metadata
的源码:
function delete_metadata( $meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false ) {
global $wpdb;
// 1. 数据校验
if ( ! $meta_type || ! $object_id || ! is_string( $meta_key ) ) {
return false;
}
$table = _get_meta_table( $meta_type );
if ( ! $table ) {
return false;
}
$column = sanitize_key( $meta_type . '_id' );
$object_id = absint( $object_id );
$meta_key = trim( $meta_key );
if ( empty( $meta_key ) ) {
return false;
}
// 2. 构建SQL语句
$sql = "DELETE FROM {$table} WHERE {$column} = %d AND meta_key = %s";
$args = array( $object_id, $meta_key );
// 3. 是否全部删除
if ( '' !== $meta_value ) {
$meta_value = maybe_serialize( $meta_value );
$sql .= ' AND meta_value = %s';
$args[] = $meta_value;
}
// 4. 是否删除所有
if ( $delete_all ) {
$sql = "DELETE FROM {$table} WHERE {$column} = %d";
$args = array( $object_id );
}
// 5. 查询符合条件的所有meta_id
$ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM {$table} WHERE {$column} = %d AND meta_key = %s", $object_id, $meta_key ) );
if ( ! $ids ) {
return false;
}
// 6. 执行删除操作
$result = $wpdb->query( $wpdb->prepare( $sql, $args ) );
if ( ! $result ) {
return false;
}
// 7. 清除缓存
wp_cache_delete( $object_id, $meta_type . '_meta' );
foreach ( $ids as $meta_id ) {
do_action( "deleted_{$meta_type}_meta", $meta_id, $object_id, $meta_key, $meta_value );
do_action( 'delete_metadata', $meta_id, $object_id, $meta_key, $meta_value );
}
return true;
}
delete_metadata()
函数首先进行数据校验,然后根据是否传递了$meta_value
来决定删除策略。 如果传递了,则只删除meta_value匹配的记录。
和addmetadata类似,最后也会触发`deleted{$meta_type}_meta和
delete_metadata`这两个action。
八、Action钩子:给世界一个拥抱的机会
在wp_insert_post()
以及相关的update_metadata()
和add_metadata()
、delete_metadata()
函数中,都穿插着各种action
钩子。这些钩子就像一个个预留的接口,允许其他插件或主题在特定的时机介入,修改数据,或者执行一些自定义的逻辑。
以下是一些常见的action
钩子:
钩子名称 | 触发时机 | 作用 |
---|---|---|
add_metadata |
在添加meta 数据之后触发 |
允许插件或主题在meta 数据添加后执行一些操作 |
added_{$meta_type}_meta |
在添加特定类型的meta 数据之后触发 |
允许插件或主题在特定类型的meta 数据添加后执行一些操作,例如added_post_meta |
update_metadata |
在更新meta 数据之后触发 |
允许插件或主题在meta 数据更新后执行一些操作 |
updated_{$meta_type}_meta |
在更新特定类型的meta 数据之后触发 |
允许插件或主题在特定类型的meta 数据更新后执行一些操作,例如updated_post_meta |
delete_metadata |
在删除meta 数据之后触发 |
允许插件或主题在meta 数据删除后执行一些操作 |
deleted_{$meta_type}_meta |
在删除特定类型的meta 数据之后触发 |
允许插件或主题在特定类型的meta 数据删除后执行一些操作,例如deleted_post_meta |
通过这些action
钩子,我们可以实现非常灵活的功能,比如:
- 在保存文章时,自动更新相关的
post_meta
。 - 在删除文章时,自动删除相关的
post_meta
。 - 在更新
post_meta
时,自动发送通知邮件。 - 等等…
九、一个简单的例子:拯救强迫症患者
假设我们有一个需求:当文章的标题被更新时,自动将一个名为_last_updated_title
的post_meta
设置为新的标题。我们可以这样实现:
function my_update_last_updated_title( $post_id ) {
$post = get_post( $post_id );
if ( $post && isset( $_POST['post_title'] ) ) {
update_post_meta( $post_id, '_last_updated_title', sanitize_text_field( $_POST['post_title'] ) );
}
}
add_action( 'save_post', 'my_update_last_updated_title' );
这段代码首先定义了一个名为my_update_last_updated_title
的函数,它接受一个$post_id
参数,表示文章的ID。
然后,它使用get_post()
函数获取文章对象,并检查$_POST['post_title']
是否存在,确保是文章标题被更新了。
最后,它使用update_post_meta()
函数将_last_updated_title
设置为新的标题。
通过add_action()
函数,我们将my_update_last_updated_title
函数挂载到save_post
这个action
钩子上。这意味着,每次文章被保存时,my_update_last_updated_title
函数都会被执行,从而实现我们的需求。
十、总结:掌控wp_insert_post()
,掌控全局
通过今天的讲座,我们深入剖析了wp_insert_post()
函数是如何处理post_meta
数据,并触发相关action
的。
wp_insert_post()
是总指挥,负责协调整个文章的插入和更新过程。post_meta
是文章的“私房钱”,用来存储一些文章本身不包含,但又非常重要的信息。update_post_meta()
、add_post_meta()
、delete_post_meta()
是post_meta
的“管家”,负责post_meta
的添加、更新和删除。action
钩子是预留的接口,允许其他插件或主题在特定的时机介入,修改数据,或者执行一些自定义的逻辑。
理解了这些,你就可以更加灵活地使用wp_insert_post()
函数,更好地管理你的WordPress网站。
希望今天的讲座对你有所帮助! 咱们下期再见!