Title: bulk_edit_posts
Published: April 25, 2014
Last modified: August 20, 2026

---

# bulk_edit_posts( array|null $post_data = null ): array

## In this article

 * [Description](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#description)
 * [Parameters](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#parameters)
 * [Return](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#return)
 * [Source](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#source)
 * [Hooks](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#hooks)
 * [Related](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#related)
 * [Changelog](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#changelog)

[ Back to top](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#wp--skip-link--target)

Processes the post data for the bulk editing of posts.

## 󠀁[Description](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#description)󠁿

Updates all bulk edited posts/pages, adding (but not removing) tags and categories.
Skips pages when they would be their own parent or child.

## 󠀁[Parameters](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#parameters)󠁿

 `$post_data`array|nulloptional

The array of post data to process.
 Defaults to the `$_POST` superglobal.

Default:`null`

## 󠀁[Return](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#return)󠁿

 array An array of updated, skipped, and locked post IDs.

 * `updated` int[]
 * An array of updated post IDs.
 * `skipped` int[]
 * An array of skipped post IDs.
 * `locked` int[]
 * An array of locked post IDs.

## 󠀁[Source](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#source)󠁿

    ```php
    function bulk_edit_posts( $post_data = null ) {
    	global $wpdb;

    	if ( empty( $post_data ) ) {
    		$post_data = &$_POST;
    	}

    	if ( isset( $post_data['post_type'] ) ) {
    		$ptype = get_post_type_object( $post_data['post_type'] );
    	} else {
    		$ptype = get_post_type_object( 'post' );
    	}

    	if ( ! current_user_can( $ptype->cap->edit_posts ) ) {
    		if ( 'page' === $ptype->name ) {
    			wp_die( __( 'Sorry, you are not allowed to edit pages.' ) );
    		} else {
    			wp_die( __( 'Sorry, you are not allowed to edit posts.' ) );
    		}
    	}

    	if ( '-1' === $post_data['_status'] ) {
    		$post_data['post_status'] = null;
    		unset( $post_data['post_status'] );
    	} else {
    		$post_data['post_status'] = $post_data['_status'];
    	}
    	unset( $post_data['_status'] );

    	if ( ! empty( $post_data['post_status'] ) ) {
    		$post_data['post_status'] = sanitize_key( $post_data['post_status'] );

    		if ( 'inherit' === $post_data['post_status'] ) {
    			unset( $post_data['post_status'] );
    		}
    	}

    	$post_ids = array_map( 'intval', (array) $post_data['post'] );

    	$reset = array(
    		'post_author',
    		'post_status',
    		'post_password',
    		'post_parent',
    		'page_template',
    		'comment_status',
    		'ping_status',
    		'keep_private',
    		'tax_input',
    		'post_category',
    		'sticky',
    		'post_format',
    	);

    	foreach ( $reset as $field ) {
    		if ( isset( $post_data[ $field ] ) && ( '' === $post_data[ $field ] || '-1' === $post_data[ $field ] ) ) {
    			unset( $post_data[ $field ] );
    		}
    	}

    	if ( isset( $post_data['post_category'] ) ) {
    		if ( is_array( $post_data['post_category'] ) && ! empty( $post_data['post_category'] ) ) {
    			$new_cats = array_map( 'absint', $post_data['post_category'] );
    		} else {
    			unset( $post_data['post_category'] );
    		}
    	}

    	$tax_input = array();
    	if ( isset( $post_data['tax_input'] ) ) {
    		foreach ( $post_data['tax_input'] as $tax_name => $terms ) {
    			if ( empty( $terms ) ) {
    				continue;
    			}

    			if ( is_taxonomy_hierarchical( $tax_name ) ) {
    				$tax_input[ $tax_name ] = array_map( 'absint', $terms );
    			} else {
    				$comma = _x( ',', 'tag delimiter' );
    				if ( ',' !== $comma ) {
    					$terms = str_replace( $comma, ',', $terms );
    				}
    				$tax_input[ $tax_name ] = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) );
    			}
    		}
    	}

    	if ( isset( $post_data['post_parent'] ) && (int) $post_data['post_parent'] ) {
    		$parent   = (int) $post_data['post_parent'];
    		$pages    = $wpdb->get_results( "SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'" );
    		$children = array();

    		for ( $i = 0; $i < 50 && $parent > 0; $i++ ) {
    			$children[] = $parent;

    			foreach ( $pages as $page ) {
    				if ( (int) $page->ID === $parent ) {
    					$parent = (int) $page->post_parent;
    					break;
    				}
    			}
    		}
    	}

    	$updated          = array();
    	$skipped          = array();
    	$locked           = array();
    	$shared_post_data = $post_data;

    	foreach ( $post_ids as $post_id ) {
    		// Start with fresh post data with each iteration.
    		$post_data = $shared_post_data;

    		$post_type_object = get_post_type_object( get_post_type( $post_id ) );

    		if ( ! isset( $post_type_object )
    			|| ( isset( $children ) && in_array( $post_id, $children, true ) )
    			|| ! current_user_can( 'edit_post', $post_id )
    		) {
    			$skipped[] = $post_id;
    			continue;
    		}

    		if ( wp_check_post_lock( $post_id ) ) {
    			$locked[] = $post_id;
    			continue;
    		}

    		$post      = get_post( $post_id );
    		$tax_names = get_object_taxonomies( $post );

    		foreach ( $tax_names as $tax_name ) {
    			$taxonomy_obj = get_taxonomy( $tax_name );

    			if ( ! $taxonomy_obj->show_in_quick_edit ) {
    				continue;
    			}

    			if ( isset( $tax_input[ $tax_name ] ) && current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
    				$new_terms = $tax_input[ $tax_name ];
    			} else {
    				$new_terms = array();
    			}

    			if ( $taxonomy_obj->hierarchical ) {
    				$current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'ids' ) );
    			} else {
    				$current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'names' ) );
    			}

    			$post_data['tax_input'][ $tax_name ] = array_merge( $current_terms, $new_terms );
    		}

    		if ( isset( $new_cats ) && in_array( 'category', $tax_names, true ) ) {
    			$cats = (array) wp_get_post_categories( $post_id );

    			if (
    				isset( $post_data['indeterminate_post_category'] )
    				&& is_array( $post_data['indeterminate_post_category'] )
    			) {
    				$indeterminate_post_category = $post_data['indeterminate_post_category'];
    			} else {
    				$indeterminate_post_category = array();
    			}

    			$indeterminate_cats         = array_intersect( $cats, $indeterminate_post_category );
    			$determinate_cats           = array_diff( $new_cats, $indeterminate_post_category );
    			$post_data['post_category'] = array_unique( array_merge( $indeterminate_cats, $determinate_cats ) );

    			unset( $post_data['tax_input']['category'] );
    		}

    		$post_data['post_ID']        = $post_id;
    		$post_data['post_type']      = $post->post_type;
    		$post_data['post_mime_type'] = $post->post_mime_type;

    		foreach ( array( 'comment_status', 'ping_status', 'post_author' ) as $field ) {
    			if ( ! isset( $post_data[ $field ] ) ) {
    				$post_data[ $field ] = $post->$field;
    			}
    		}

    		$post_data = _wp_translate_postdata( true, $post_data );
    		if ( is_wp_error( $post_data ) ) {
    			$skipped[] = $post_id;
    			continue;
    		}
    		$post_data = _wp_get_allowed_postdata( $post_data );

    		if ( isset( $shared_post_data['post_format'] ) ) {
    			set_post_format( $post_id, $shared_post_data['post_format'] );
    		}

    		// Prevent wp_insert_post() from overwriting post format with the old data.
    		unset( $post_data['tax_input']['post_format'] );

    		// Reset post date of scheduled post to be published.
    		if (
    			in_array( $post->post_status, array( 'future', 'draft' ), true ) &&
    			'publish' === $post_data['post_status']
    		) {
    			$post_data['post_date']     = current_time( 'mysql' );
    			$post_data['post_date_gmt'] = '';
    		}

    		$post_id = wp_update_post( $post_data );
    		update_post_meta( $post_id, '_edit_last', get_current_user_id() );
    		$updated[] = $post_id;

    		if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) {
    			if ( 'sticky' === $post_data['sticky'] ) {
    				stick_post( $post_id );
    			} else {
    				unstick_post( $post_id );
    			}
    		}
    	}

    	/**
    	 * Fires after processing the post data for bulk edit.
    	 *
    	 * @since 6.3.0
    	 *
    	 * @param int[] $updated          An array of updated post IDs.
    	 * @param array $shared_post_data Associative array containing the post data.
    	 */
    	do_action( 'bulk_edit_posts', $updated, $shared_post_data );

    	return array(
    		'updated' => $updated,
    		'skipped' => $skipped,
    		'locked'  => $locked,
    	);
    }
    ```

[View all references](https://developer.wordpress.org/reference/files/wp-admin/includes/post.php/)
[View on Trac](https://core.trac.wordpress.org/browser/tags/7.1/src/wp-admin/includes/post.php#L500)
[View on GitHub](https://github.com/WordPress/wordpress-develop/blob/7.1/src/wp-admin/includes/post.php#L500-L733)

## 󠀁[Hooks](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#hooks)󠁿

 [do_action( ‘bulk_edit_posts’, int[] $updated, array $shared_post_data )](https://developer.wordpress.org/reference/hooks/bulk_edit_posts/)

Fires after processing the post data for bulk edit.

## 󠀁[Related](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#related)󠁿

| Uses | Description | 
| [_wp_get_allowed_postdata()](https://developer.wordpress.org/reference/functions/_wp_get_allowed_postdata/)`wp-admin/includes/post.php` |

Returns only allowed post data fields.

  | 
| [wp_check_post_lock()](https://developer.wordpress.org/reference/functions/wp_check_post_lock/)`wp-admin/includes/post.php` |

Determines whether the post is currently being edited by another user.

  | 
| [_wp_translate_postdata()](https://developer.wordpress.org/reference/functions/_wp_translate_postdata/)`wp-admin/includes/post.php` |

Renames `$_POST` data from form names to DB post columns.

  | 
| [current_time()](https://developer.wordpress.org/reference/functions/current_time/)`wp-includes/functions.php` |

Retrieves the current time based on specified type.

  | 
| [wp_get_object_terms()](https://developer.wordpress.org/reference/functions/wp_get_object_terms/)`wp-includes/taxonomy.php` |

Retrieves the terms associated with the given object(s), in the supplied taxonomies.

  | 
| [is_taxonomy_hierarchical()](https://developer.wordpress.org/reference/functions/is_taxonomy_hierarchical/)`wp-includes/taxonomy.php` |

Determines whether the taxonomy object is hierarchical.

  | 
| [get_object_taxonomies()](https://developer.wordpress.org/reference/functions/get_object_taxonomies/)`wp-includes/taxonomy.php` |

Returns the names or objects of the taxonomies which are registered for the requested object or object type, such as a post object or post type name.

  | 
| [wp_get_post_categories()](https://developer.wordpress.org/reference/functions/wp_get_post_categories/)`wp-includes/post.php` |

Retrieves the list of categories for a post.

  | 
| [wp_update_post()](https://developer.wordpress.org/reference/functions/wp_update_post/)`wp-includes/post.php` |

Updates a post with new post data.

  | 
| [stick_post()](https://developer.wordpress.org/reference/functions/stick_post/)`wp-includes/post.php` |

Makes a post sticky.

  | 
| [unstick_post()](https://developer.wordpress.org/reference/functions/unstick_post/)`wp-includes/post.php` |

Un-sticks a post.

  | 
| [update_post_meta()](https://developer.wordpress.org/reference/functions/update_post_meta/)`wp-includes/post.php` |

Updates a post meta field based on the given post ID.

  | 
| [get_post_type()](https://developer.wordpress.org/reference/functions/get_post_type/)`wp-includes/post.php` |

Retrieves the post type of the current post or of a given post.

  | 
| [set_post_format()](https://developer.wordpress.org/reference/functions/set_post_format/)`wp-includes/post-formats.php` |

Assign a format to a post

  | 
| [current_user_can()](https://developer.wordpress.org/reference/functions/current_user_can/)`wp-includes/capabilities.php` |

Returns whether the current user has the specified capability.

  | 
| [__()](https://developer.wordpress.org/reference/functions/__/)`wp-includes/l10n.php` |

Retrieves the translation of $text.

  | 
| [_x()](https://developer.wordpress.org/reference/functions/_x/)`wp-includes/l10n.php` |

Retrieves translated string with gettext context.

  | 
| [sanitize_key()](https://developer.wordpress.org/reference/functions/sanitize_key/)`wp-includes/formatting.php` |

Sanitizes a string key.

  | 
| [wp_die()](https://developer.wordpress.org/reference/functions/wp_die/)`wp-includes/functions.php` |

Kills WordPress execution and displays HTML page with an error message.

  | 
| [get_taxonomy()](https://developer.wordpress.org/reference/functions/get_taxonomy/)`wp-includes/taxonomy.php` |

Retrieves the taxonomy object of $taxonomy.

  | 
| [do_action()](https://developer.wordpress.org/reference/functions/do_action/)`wp-includes/plugin.php` |

Calls the callback functions that have been added to an action hook.

  | 
| [get_current_user_id()](https://developer.wordpress.org/reference/functions/get_current_user_id/)`wp-includes/user.php` |

Gets the current user’s ID.

  | 
| [get_post_type_object()](https://developer.wordpress.org/reference/functions/get_post_type_object/)`wp-includes/post.php` |

Retrieves a post type object by name.

  | 
| [get_post()](https://developer.wordpress.org/reference/functions/get_post/)`wp-includes/post.php` |

Retrieves post data given a post ID or post object.

  | 
| [wpdb::get_results()](https://developer.wordpress.org/reference/classes/wpdb/get_results/)`wp-includes/class-wpdb.php` |

Retrieves an entire SQL result set from the database (i.e., many rows).

  | 
| [is_wp_error()](https://developer.wordpress.org/reference/functions/is_wp_error/)`wp-includes/load.php` |

Checks whether the given variable is a WordPress Error.

  |

[Show 21 more](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#)
[Show less](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#)

## 󠀁[Changelog](https://developer.wordpress.org/reference/functions/bulk_edit_posts/?output_format=md#changelog)󠁿

| Version | Description | 
| [2.7.0](https://developer.wordpress.org/reference/since/2.7.0/) | Introduced. |

## User Contributed Notes

You must [log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Ffunctions%2Fbulk_edit_posts%2F)
before being able to contribute a note or feedback.