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

Fires once a post has been saved.


Parameters

$post_ID

(int) Post ID.

$post

(WP_Post) Post object.

$update

(bool) Whether this is an existing post being updated.


Top ↑

More Information

save_post is an action triggered whenever a post or page is created or updated, which could be from an import, post/page edit form, xmlrpc, or post by email. The data for the post is stored in $_POST, $_GET or the global $post_data, depending on how the post was edited. For example, quick edits use $_POST.

Since this action is triggered right after the post has been saved, you can easily access this post object by using get_post($post_id);.

NOTE: As of WP 3.7, an alternative action has been introduced, which is called for specific post types: save_post_{post_type}. Hooking to this action prevents your callback to be unnecessarily triggered.

Top ↑

Avoiding infinite loops

If you are calling a function such as wp_update_post that includes the save_post hook, your hooked function will create an infinite loop. To avoid this, unhook your function before calling the function you need, then re-hook it afterward.

// this function makes all posts in the default category private

function set_private_categories($post_id) {
	// If this is a revision, get real post ID
	if ( $parent_id = wp_is_post_revision( $post_id ) ) 
		$post_id = $parent_id;

	// Get default category ID from options
	$defaultcat = get_option( 'default_category' );

	// Check if this post is in default category
	if ( in_category( $defaultcat, $post_id ) ) {
		// unhook this function so it doesn't loop infinitely
		remove_action( 'save_post', 'set_private_categories' );

		// update the post, which calls save_post again
		wp_update_post( array( 'ID' => $post_id, 'post_status' => 'private' ) );

		// re-hook this function
		add_action( 'save_post', 'set_private_categories' );
	}
}
add_action( 'save_post', 'set_private_categories' );

Top ↑

Source

File: wp-includes/post.php

View on Trac



Top ↑

Changelog

Changelog
Version Description
1.5.0 Introduced.

Top ↑

User Contributed Notes

  1. Skip to note 1 content
    Contributed by Jason Adams

    When using WordPress 3.7 or later, it’s a good idea to use the save_post_{$post->post_type} hook when it makes sense to in order to reduce code and fire less hooks overall when posts are created and updated.

    Documentation can be found here: https://developer.wordpress.org/reference/hooks/save_post_post-post_type/

  2. Skip to note 2 content
    Contributed by Aurovrata Venet

    Force a new post of have specific category term,

    add_action( 'save_post', 'set_post_default_category', 10,3 );
    
    function set_post_default_category( $post_id, $post, $update ) {
    	// Only want to set if this is a new post!
    	if ( $update ){
    		return;
    	}
    	
    	// Only set for post_type = post!
    	if ( 'post' !== $post->post_type ) {
    		return;
    	}
    	
    	// Get the default term using the slug, its more portable!
    	$term = get_term_by( 'slug', 'my-custom-term', 'category' );
    
    	wp_set_post_terms( $post_id, $term->term_id, 'category', true );
    }
    
  3. Skip to note 3 content
    Contributed by leogermani

    Below is a basic example that will send an email every time a post or page is updated on your website.

    function my_project_updated_send_email( $post_id ) {
    
    	// If this is just a revision, don't send the email.
    	if ( wp_is_post_revision( $post_id ) ) {
    		return;
            }
    
    	$post_title = get_the_title( $post_id );
    	$post_url = get_permalink( $post_id );
    	$subject = 'A post has been updated';
    
    	$message = "A post has been updated on your website:\n\n";
    	$message .= $post_title . ": " . $post_url;
    
    	// Send email to admin.
    	wp_mail( 'admin@example.com', $subject, $message );
    }
    add_action( 'save_post', 'my_project_updated_send_email' );
    
  4. Skip to note 4 content
    Contributed by camparry

    The save_post_{post_type} hook fires BEFORE the general save_post hook, meaning that save_post will override any meta updates made with save_post_{post_type}. Many plugins like ACF and Pods use the save post action hook, so if you are trying to update a meta field and you are using one of these plugins, you must use the save_post hook instead.

    // does not work
    function my_save_meta_function( $post_id, $post, $update )
    {
    	// fires but can be overridden by plugins, regardless of priority number
    	update_post_meta( $post_id, 'address', '123 Test St' );
    }
    add_action( 'save_post_event', 'my_save_meta_function', 99, 3 );
    
    
    // does work
    function my_save_meta_function( $post_id, $post, $update )
    {
    	if ( get_post_type( $post_id ) !== 'event' ) return;
    	update_post_meta( $post_id, 'address', '123 Test St' );
    }
    add_action( 'save_post', 'my_save_meta_function', 99, 3 );
    
  5. Skip to note 5 content
    Contributed by Ali Akbar Reyad

    To trigger for specific post type, assume we have a post type name ‘book’

    function wpdocs_book_meta( $post_id ) {
    	// Check the logged in user has permission to edit this post
    	if ( ! current_user_can( 'manage_options' ) ) {
    		return $post_id;
    	}
    
    	if ( isset( $_POST['website'] ) ) {
    		$website = esc_url_raw( $_POST['website'] );
    		update_post_meta( $post_id, 'website', $website );
    	}
    }
    add_action( 'save_post_book', 'wpdocs_book_meta' );
    
  6. Skip to note 6 content
    Contributed by Arun Basil Lal

    The $post_ID passed to the action is the ID of the revision while updating a post. To find the ID of the parent post, use wp_get_post_parent_id.

    function my_function_on_save_post( $post_id ) {
    
    	// Find parent post_id.
    	if ( $post_parent_id = wp_get_post_parent_id( $post_id ) ) {
    		$post_id = $post_parent_id;
    	}
    
    	// Do something.
    
    }
    add_action( 'save_post', 'my_function_on_save_post' );

You must log in before being able to contribute a note or feedback.