阐述 WordPress `wp_delete_post()` 函数源码:删除文章时如何同时删除相关元数据和评论。

各位观众老爷,晚上好!我是你们的老朋友,今天咱们来聊聊WordPress里一个有点狠的角色——wp_delete_post(),这哥们儿专干删除文章的活儿,而且下手挺黑,连带着文章的元数据和评论都给一锅端了。今天咱们就扒一扒它的老底,看看它到底是怎么做到的。

开场白:删除的艺术

在WordPress的世界里,删除文章可不是简简单单地把数据库里的一行数据删掉那么简单。文章就像个大户人家,底下管着一堆人:元数据是他的财务管家,评论是来访的客人,附件是他的家产。要彻底删除一篇文章,就得把这些关系都理清楚,一个都不能放过。wp_delete_post()就是干这事的。

第一幕:wp_delete_post() 的登场

首先,让我们看看wp_delete_post()的庐山真面目:

/**
 * Deletes a post.
 *
 * @since 2.0.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param int  $postid      Post ID.
 * @param bool $force_delete Optional. Whether to bypass Trash and force deletion.
 *                            Default false.
 * @return WP_Post|false WP_Post on success. False on failure.
 */
function wp_delete_post( $postid = 0, $force_delete = false ) {
    global $wpdb;

    $postid = (int) $postid;

    if ( ! $postid ) {
        return false;
    }

    $post = get_post( $postid );

    if ( ! $post ) {
        return false;
    }

    $trashed = false;
    if ( ! $force_delete && EMPTY_TRASH_DAYS && ( 'trash' !== $post->post_status ) ) {
        // ... (省略了垃圾箱相关的代码,后面会提到)
    } else {
        // ... (省略了强制删除相关的代码,后面会提到)
    }

    return $post;
}

简单来说,这个函数接收两个参数:

  • $postid:要删除的文章ID。
  • $force_delete:是否强制删除,跳过垃圾箱。默认是false,也就是先扔到垃圾箱。

第二幕:判断,判断,再判断

函数一开始,先对$postid进行类型转换,确保是个整数。然后,它会用get_post()函数尝试获取文章对象。如果$postid是0或者文章不存在,那就直接打道回府,返回false。毕竟,巧妇难为无米之炊嘛。

第三幕:垃圾箱的诱惑 (如果 $force_deletefalse)

如果$force_deletefalse,并且启用了垃圾箱功能(EMPTY_TRASH_DAYS大于0),并且文章不在垃圾箱里('trash' !== $post->post_status),那么文章会被移动到垃圾箱,而不是直接删除。

        $trashed = wp_trash_post( $postid );
        if ( $trashed ) {
            return $post;
        } else {
            return false;
        }

这里用到了wp_trash_post()函数,这个函数只是简单地把文章的状态改为'trash'。 就像把犯人关进监狱,并没有真正处决。

第四幕:强制删除的铁拳 (如果 $force_deletetrue)

如果$force_deletetrue,或者垃圾箱功能没启用,或者文章已经在垃圾箱里了,那就直接进入强制删除的环节。这才是真正的重头戏。

        /**
         * Fires before a post is deleted, at the start of the 'wp_delete_post' function.
         *
         * @since 4.4.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'before_delete_post', $postid );

        $post = get_post( $postid );

        if ( ! $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) ) ) {
            return false;
        }

        clean_post_cache( $postid );

        // Delete Post Meta
        delete_post_meta( $postid );

        // Delete all attachments
        $children = get_posts(
            array(
                'post_parent' => $postid,
                'post_type'   => 'attachment',
                'fields'      => 'ids',
                'numberposts' => -1,
            )
        );

        if ( ! empty( $children ) ) {
            foreach ( $children as $child ) {
                wp_delete_attachment( $child, $force_delete );
            }
        }

        wp_delete_object_term_relationships( $postid, get_object_taxonomies( $post->post_type ) );

        $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ) );
        if ( $comment_ids ) {
            $wpdb->query( "DELETE FROM $wpdb->comments WHERE comment_ID IN (" . implode( ',', array_map( 'intval', $comment_ids ) ) . ")" );

            $wpdb->query( "DELETE FROM $wpdb->commentmeta WHERE comment_id IN (" . implode( ',', array_map( 'intval', $comment_ids ) ) . ")" );
            wp_update_comment_count_now( $postid );
        }

        $post->post_status = 'deleted';

        /**
         * Fires after a post is deleted.
         *
         * @since 2.0.0
         *
         * @param int $postid Post ID.
         */
        do_action( 'deleted_post', $postid );

让我们一步一步来看:

  1. do_action( 'before_delete_post', $postid );: 在删除文章之前,先触发一个before_delete_post的动作钩子。这允许其他插件或者主题在文章被删除之前做一些事情,比如备份数据或者发送通知。这是一种解耦的设计思想,让不同的模块可以协同工作,而不需要直接修改wp_delete_post()函数的代码。
  2. $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) ): 这是真正删除文章的SQL语句。$wpdb是WordPress的数据库操作对象,delete()方法用于删除数据库表中的数据。这里删除的是$wpdb->posts表(也就是文章表)中ID等于$postid的记录。
  3. clean_post_cache( $postid );: 删除文章后,需要清理文章缓存,避免下次访问时仍然显示旧的数据。clean_post_cache()函数就是干这个的。
  4. delete_post_meta( $postid );: 这行代码调用了delete_post_meta()函数,用于删除与文章相关的元数据。
  5. $children = get_posts(...): 这部分代码用于获取文章的所有附件。附件也是一种文章类型(post_typeattachment),并且它们的post_parent字段指向父文章的ID。
  6. foreach ( $children as $child ) { wp_delete_attachment( $child, $force_delete ); }: 遍历所有附件,并调用wp_delete_attachment()函数删除它们。注意,这里也使用了$force_delete参数,确保附件也被强制删除。
  7. wp_delete_object_term_relationships( $postid, get_object_taxonomies( $post->post_type ) ): 这行代码用于删除文章与分类、标签等分类法的关联关系。wp_delete_object_term_relationships()函数负责删除这些关联关系。
  8. 删除评论: 接下来,就是删除评论的环节。

    • $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ) ):首先,查询所有与该文章相关的评论ID。
    • $wpdb->query( "DELETE FROM $wpdb->comments WHERE comment_ID IN (" . implode( ',', array_map( 'intval', $comment_ids ) ) . ")" ):然后,根据评论ID删除wp_comments表中的评论。
    • $wpdb->query( "DELETE FROM $wpdb->commentmeta WHERE comment_id IN (" . implode( ',', array_map( 'intval', $comment_ids ) ) . ")" ):最后,删除wp_commentmeta表中与这些评论相关的元数据。
    • wp_update_comment_count_now( $postid ):更新文章的评论计数。
  9. $post->post_status = 'deleted';: 将文章的状态设置为'deleted'。这并不是一个标准的文章状态,可能是为了标记文章已经被删除,方便后续处理。
  10. do_action( 'deleted_post', $postid );: 在文章被删除后,触发一个deleted_post的动作钩子。这允许其他插件或者主题在文章被删除之后做一些事情,比如更新索引或者发送通知。

第五幕:元数据的清理

delete_post_meta()函数是删除元数据的关键,让我们看看它的代码:

/**
 * Delete all meta data matching the specified post ID.
 *
 * @since 2.9.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param int $post_id Post ID.
 * @return bool Whether the post meta was successfully deleted.
 */
function delete_post_meta( $post_id ) {
    global $wpdb;

    if ( ! $post_id = absint( $post_id ) ) {
        return false;
    }

    /**
     * Fires before post meta is deleted.
     *
     * @since 3.0.0
     *
     * @param int $post_id Post ID.
     */
    do_action( 'delete_post_meta', $post_id );

    $result = $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $post_id ) );

    clean_post_cache( $post_id );

    /**
     * Fires after post meta is deleted.
     *
     * @since 3.0.0
     *
     * @param int $post_id Post ID.
     */
    do_action( 'deleted_post_meta', $post_id );

    return (bool) $result;
}

这个函数也很简单:

  1. do_action( 'delete_post_meta', $post_id );: 在删除元数据之前,触发一个delete_post_meta动作钩子。
  2. $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $post_id ) ): 使用SQL语句删除wp_postmeta表中post_id等于$post_id的所有记录。
  3. clean_post_cache( $post_id );: 清理文章缓存。
  4. do_action( 'deleted_post_meta', $post_id );: 在删除元数据之后,触发一个deleted_post_meta动作钩子。

第六幕:评论的覆灭

删除评论的代码在wp_delete_post()函数中已经展示过了,这里再简单回顾一下:

  1. 查询评论ID: 首先,查询所有与该文章相关的评论ID。
  2. 删除评论: 然后,根据评论ID删除wp_comments表中的评论。
  3. 删除评论元数据: 最后,删除wp_commentmeta表中与这些评论相关的元数据。

总结:wp_delete_post()的生死簿

让我们用一个表格来总结一下wp_delete_post()的工作流程:

步骤 描述 涉及函数/SQL
1. 参数校验 检查$postid是否有效,获取文章对象。 get_post()
2. 垃圾箱判断 如果$force_deletefalse,且启用垃圾箱,则将文章移动到垃圾箱。 wp_trash_post()
3. 删除前钩子 触发before_delete_post动作钩子。 do_action( 'before_delete_post', $postid )
4. 删除文章 wp_posts表中删除文章记录。 $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) )
5. 清理缓存 清理文章缓存。 clean_post_cache( $postid )
6. 删除元数据 删除与文章相关的元数据。 delete_post_meta( $postid ), $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $postid ) )
7. 删除附件 获取并删除文章的所有附件。 get_posts( array( 'post_parent' => $postid, 'post_type' => 'attachment' ) ), wp_delete_attachment( $child, $force_delete )
8. 删除分类/标签关联关系 删除文章与分类、标签等分类法的关联关系。 wp_delete_object_term_relationships( $postid, get_object_taxonomies( $post->post_type ) )
9. 删除评论 删除与文章相关的评论和评论元数据。 $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d" ), $wpdb->query( "DELETE FROM $wpdb->comments WHERE comment_ID IN (...)" ), $wpdb->query( "DELETE FROM $wpdb->commentmeta WHERE comment_id IN (...)" ), wp_update_comment_count_now( $postid )
10. 设置文章状态 将文章状态设置为'deleted' $post->post_status = 'deleted'
11. 删除后钩子 触发deleted_post动作钩子。 do_action( 'deleted_post', $postid )

补充说明:附件的删除

wp_delete_attachment()函数用于删除附件,它的代码也值得一看:

/**
 * Deletes an attachment.
 *
 * @since 2.0.0
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @param int  $post_id      Attachment ID.
 * @param bool $force_delete Whether to bypass Trash and force deletion. Default false.
 * @return WP_Post|false WP_Post on success. False on failure.
 */
function wp_delete_attachment( $post_id = 0, $force_delete = false ) {
    global $wpdb;

    $post_id = (int) $post_id;
    if ( ! $post_id ) {
        return false;
    }

    $post = get_post( $post_id );

    if ( ! $post ) {
        return false;
    }

    if ( 'attachment' !== $post->post_type ) {
        return false;
    }

    /**
     * Fires before an attachment is deleted, at the start of the 'wp_delete_attachment' function.
     *
     * @since 4.4.0
     *
     * @param int $post_id Attachment ID.
     */
    do_action( 'before_delete_attachment', $post_id );

    if ( ! $force_delete && EMPTY_TRASH_DAYS && ( 'trash' !== $post->post_status ) ) {
        return wp_trash_post( $post_id );
    }

    $file = get_attached_file( $post_id );

    if ( $file ) {
        /**
         * Filters whether to run wp_delete_file() when deleting an attachment.
         *
         * @since 3.6.0
         *
         * @param bool $delete  Whether to delete the file. Default true.
         * @param int  $post_id Attachment ID.
         */
        if ( apply_filters( 'wp_delete_file', true, $post_id ) ) {
            $delete_result = wp_delete_file( $file );
            if ( ! empty( $delete_result['error'] ) ) {
                return false;
            }
        }
    }

    $result = wp_delete_post( $post_id, true );

    /**
     * Fires after an attachment is deleted.
     *
     * @since 2.0.0
     *
     * @param int $post_id Attachment ID.
     */
    do_action( 'deleted_attachment', $post_id );

    return $result;
}

这个函数和wp_delete_post()类似,也是先进行参数校验,然后判断是否需要放入垃圾箱。如果需要强制删除,它会先删除附件对应的文件(使用wp_delete_file()函数),然后调用wp_delete_post()函数删除附件的文章记录。

总结

wp_delete_post()是一个功能强大的函数,它不仅可以删除文章,还可以删除与文章相关的元数据、评论和附件。它通过一系列的SQL查询和钩子函数,确保了删除操作的完整性和灵活性。理解wp_delete_post()的工作原理,可以帮助我们更好地管理WordPress网站的数据,并且可以为自定义的删除操作提供参考。

好了,今天的讲座就到这里。希望大家有所收获!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注