保存所有 post 数据和 post 元数据后会触发哪个 WordPress 挂钩?

Which WordPress hook fires after save all post data and post meta?

我有一个自定义 post 类型的 crm,我需要在每个 crm 保存或更新后发送一封邮件。我将 cmb2 用于一些自定义元,如主题、用户等。我知道 post 保存 (根据 WordPress codex) 在我的情况下 save_post 钩子触发当我使用两个参数 (id 和 post) 调用 save_post 时,post 不包含更新值。这是我的代码:

function send_mail_to_user($id, $post){
    $crm = $post;
    $user_email = array();
    if($crm->vc_all_vc == 'on'){
        $args = array('orderby' => 'display_name');
        $wp_user_query = new WP_User_Query($args);
        $authors = $wp_user_query->get_results();
        if (!empty($authors)) {
            foreach ($authors as $author) {
                array_push($user_email , $author->user_email );
            }
        } 
    }
    else{
        $to_users = $crm->vc_users;
        $to_program = $crm->vc_program;
        $to_group = $crm->vc_group;
        $to_excode = $crm->vc_ex_code;
        foreach ($to_users as $key => $value) {
            $user_data = get_userdata($value);
            array_push($user_email, $user_data->user_email);
        }
        foreach ($to_program as $key => $value) {
            $users = get_users( array('meta_key'     => 'programs'  ) );
            if($users){ 
                foreach ($users as $index => $data) {
                    if(in_array($value , explode('#', $data->programs))){
                        if(! in_array($data->user_email, $user_email)  )
                        {
                            array_push($user_email, $data->user_email);
                        }
                    }
                }
            }
        }
        foreach($to_group as $group) {
            $term = get_term_by('slug', esc_attr($group), 'user-group');
            $user_ids = get_objects_in_term($term->term_id, 'user-group');
            foreach($user_ids as $user_id){
                $fc_user = get_userdata($user_id);
                if(! in_array($fc_user->user_email, $user_email)  )
                {
                    array_push($user_email, $fc_user->user_email);
                }
            }   
        }
        foreach($to_excode as $codes) {
            $value = explode('*',$codes)[1];
            $users = get_users( array('meta_key'     => 'programs'  ) );
            if($users){ 
                foreach ($users as $index => $data) {
                    if(in_array($value , explode('#', $data->programs))){
                        if(! in_array($data->user_email, $user_email)  )
                        {
                            array_push($user_email, $data->user_email);
                        }
                    }
                }
            }   
        }
    }
    foreach($user_email as $index => $email){
        $to      = $email;
        $subject = $crm->vc_subject;
        $body    = $crm->post_content;
        $headers = array(
        'Content-Type: text/html; charset=UTF-8'
        );
        wp_mail($to, $subject, $body, $headers);
    }
}

add_action( 'save_post', 'send_mail_to_user', 10, 2 ); 

我也尝试 publish_post 钩子,在创建新的 post 时工作正常,但在更新时它工作相同。我也试过 edit_postpost_updated 挂钩,但我永远无法检索我的更新数据。

那我该如何解决呢?哪个动作挂钩会给我所有新数据? 提前致谢。

您可以在您的函数中使用此 save_post 挂钩。将你的挂钩优先级更改为 100 它会给你更新 post

add_action( 'save_post', 'send_mail_to_user', 100, 2 );

尝试使用 post_updated 并使用 $post_after 对象。 https://codex.wordpress.org/Plugin_API/Action_Reference/post_updated

你可以使用这样的东西,

function your_custom_function($meta_id, $post_id, $meta_key='', $meta_value='') {
    if($meta_key=='_edit_lock') {
        // if post meta is updated
    }
}
add_action('updated_post_meta', 'your_custom_function', 10, 4); 

您可以使用 rest_after_insert_{$this->post_type} 挂钩(其中 $this->post_type 替换为 post 类型,例如 'post' 或 'myposttype')。

感谢 Florian Brinkmann this link

add_action('rest_after_insert_myposttype', 'myfunction', 10, 3);

function myfunction($post, $request, $creating) {
   // $creating is TRUE if the post is created for the first time,
   // false if it's an update

   //  ...
}

See also here.

一些解决方法是使用 $_POST['meta_field'] 进行卫生处理:

$to_users = $_POST['vc_users'];
$to_program = $_POST['vc_program'];
$to_group = $_POST['vc_group'];
$to_excode = $_POST['vc_ex_code'];

$to_users = sanitize_text_field($to_users);
$to_program = sanitize_text_field($to_program);
$to_group = sanitize_text_field($to_group);
$to_excode = sanitize_text_field($to_excode);

注意字段名,使用ACF会让你使用字段键。

这个问题比第一眼看上去的要复杂:

我们的 'post_updated' hook 在 post 更新之前是 运行,因此每次尝试获取元数据的结果都将是以前的数据。此外,Wordpress (v5.7.2) 在 post 被保存后似乎没有钩子。

此外,'save_post' hook 非常复杂,因为它会针对每个 post 保存或创建(包括修订)运行,并且 $update 布尔值仍然不够可靠。

您可以使用具有更高优先级的 save_post 操作,以便在保存所有元数据后调用您的函数。

add_action( 'save_post',  'action_woocommerce_update_product', 20, 3 );
Here I have used higher priority 20

正确且更简单的答案是使用 wp_insert_post 操作。

https://developer.wordpress.org/reference/hooks/wp_insert_post/

An important distinction of wp_insert_post action is that it is fired after update_post_meta has been called.

有 3 个参数可用 - $update 标志告诉您这是新的还是更新的 post。

do_action( 'wp_insert_post', int $post_ID, WP_Post $post, bool $update )

因此 - 要在所有 post 元更新后实施您的代码,请使用如下内容:

add_action('wp_insert_post', 'run_after_post_updated', 10, 3);    
function run_after_post_updated($post_ID, $post, $update ) {
   //  ...
}

这可能有点旧,但只是想提供一个更新,因为从 5.6.0 版开始有一个新的挂钩可用。钩子是 wp_after_insert_post 你可以找到更多信息 here 。此挂钩在创建或更新 post 并且更新其所有术语和元数据后触发。您可以在下面找到示例:

add_action( 'wp_after_insert_post', 'send_mail_to_user', 90, 4 );

/**
 * Callback to: 'wp_after_insert_post'
 * Fires once a post, its terms and meta data has been saved
 * @param int     $post_id Post ID.
 * @param WP_Post $post    Post object.
 * @param bool    $update  Whether this is an existing post being updated.
 * @param null|WP_Post $post_before Null for new posts, the WP_Post object prior to the update for updated posts.
 *
 */
public static function sync_product_registrations_on_update( $post_id, $post, $update, $post_before ) {

    if ( 'post' !== $post->post_type ) {
        //Only to process the below when post type is 'post' else return
        return;
    }

    if ( ! in_array( $post->post_status, [ 'private', 'publish' ] ) ) {
        //To only process when status is private or publish
        return;
    }

    //The rest of your code goes here


}