WordPress как на ладони
wordpress jino

save_post хук-событие . WP 1.5.2

Событие срабатывающее всякий раз, когда запись (пост, страница) создается или обновляется, даже при публикации через импорт, xmlrpc или по email.

Кроме того, что данные записи передаются во втором параметре $post, их также как правило можно получить через $_POST, $_GET или глобальную переменную global $post_data. Когда что используется, зависит от того, как запись редактируется. Например, "быстрое редактирование" (quick edits) использует $_POST.

С версии 3.7 появился точно такой же хук - save_post_(post_type). Вместо post_type нужно указать название типа записи и событие будет срабатывать только при сохранении или добавлении записи этого типа. Выглядит хук так:

do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );

Еще одни аналог save_post - это wp_insert_post.

Использование

add_action( 'save_post', 'action_function_name_85245', 10, 3 );
function action_function_name_85245( $post_ID, $post, $update ) {
	// Действие...
}
$post_ID(строка)
ID записи, которая обновляется.
$post(объект)
Объект записи, которая обновляется. Объект такой же как обычно в глобальной переменной $post.
$update(логический)
true - то это обновление записи. false - это добавление новой записи.

Примеры

#1 Отправка email при обновлении записи

Этот демонстративный пример, показывает как, всякий раз когда запись обновляется отправлять письмо администратору сайта с сообщением об обновлении:

add_action( 'save_post', 'my_project_updated_send_email' );
function my_project_updated_send_email( $post_id ) {

	// Если это ревизия, то не отправляем письмо
	if ( wp_is_post_revision( $post_id ) || get_post($post_id)->post_status != 'publish' )
		return;

	$post_title = get_the_title( $post_id );
	$post_url = get_permalink( $post_id );
	$subject = 'Запись была обновлена';

	$message = "На вашем сайте следующая запись была обновлена:\n\n";
	$message .= $post_title . ": " . $post_url;

	// Отправляем письмо.
	wp_mail( get_option('admin_email'), $subject, $message );
}

#2 Произвольный тип записи "book"

Предположим у вас есть тип поста book и вам нужно добавить к нему информацию об авторе, издателе и о печатной копии. Этот код показывает как можно сохранить эти данные в метаданные записи:

/**
 * Сохраняем метаданные записи при сохранении поста.
 */
add_action( 'save_post', 'save_book_meta' );
function save_book_meta( $post_id ) {
	// слаг лучше указать единожды и использовать во всех кодах 
	// связанных с типом записи, как это принято в классах
	$slug = 'book';

	// Проверяем тип записи, если не boo то выходим.
	if ( $slug != $_POST['post_type'] )
		return;

	// Обновляем метаданные записи.

	if ( isset( $_REQUEST['book_author'] ) ) {
		update_post_meta( $post_id, 'book_author', sanitize_text_field( $_REQUEST['book_author'] ) );
	}

	if ( isset( $_REQUEST['publisher'] ) ) {
		update_post_meta( $post_id, 'publisher', sanitize_text_field( $_REQUEST['publisher'] ) );
	}

	// inprint - это чекбокс.
	if ( isset( $_REQUEST['inprint'] ) ) {
		update_post_meta( $post_id, 'inprint', TRUE );
	} else {
		update_post_meta( $post_id, 'inprint', FALSE );
	}
}

В этом примере, проверку if ( $slug != $_POST['post_type'] ), можно заменить: вместо события save_post, можно использовать save_post_book.

#3 Как избежать зацикливания

Если вы вызываете функцию wp_update_post() внутри события save_post, то вы наткнетесь на зацикливание, потому что wp_update_post() вызывает save_post. Чтобы этого избежать, удалите добавленный хук, перед тем как вызывать wp_update_post(), а затем добавьте его снова:

// Эта функция делает все записи в дефолтной категории частными
add_action( 'save_post', 'set_private_categories' );
function set_private_categories( $post_id ){
	// Получим реальный ID поста, если это ревизия
	if ( $parent_id = wp_is_post_revision( $post_id ) ) 
		$post_id = $parent_id;

	// Получим ID категории по умолчанию из опций
	$defaultcat = get_option( 'default_category' );

	// Проверим находится ли пост в категории по умолчанию
	if ( in_category( $defaultcat, $post_id ) ) {
		// Удаляем хук, чтобы не было зацикливания
		remove_action( 'save_post', 'set_private_categories' );

		// обновляем запись. В это время срабатывает событие save_post
		wp_update_post( array( 'ID' => $post_id, 'post_status' => 'private' ) );

		// Ставим хук обратно
		add_action( 'save_post', 'set_private_categories' );
	}
}

Эта часть кода, должна быть обязательно:

        // Удаляем хук, чтобы не было зацикливания
		remove_action( 'save_post', 'set_private_categories' );

		// обновляем запись. В это время срабатывает событие save_post
		wp_update_post( array( 'ID' => $post_id, 'post_status' => 'private' ) );

		// Ставим хук обратно
		add_action( 'save_post', 'set_private_categories' );

Связи хука

Используется в: wp_insert_post(), wp_publish_post(), _wp_customize_publish_changeset()

Остальные хуки из: wp_insert_post():

Остальные хуки из: wp_publish_post():

Остальные хуки из: _wp_customize_publish_changeset():

Фрагменты кода хука save_post

Фрагмент из: wp-includes/post.php WP 4.8.2
...
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated or not.
	 */
	do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );

	/**
	 * Fires once a post has been saved.
	 *
	 * @since 1.5.0
	 *
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated or not.
	 */
	do_action( 'save_post', $post_ID, $post, $update );

	/**
	 * Fires once a post has been saved.
	 *
	 * @since 2.0.0
	 *
	 * @param int     $post_ID Post ID.
	 * @param WP_Post $post    Post object.
	 * @param bool    $update  Whether this is an existing post being updated or not.
	 */
	do_action( 'wp_insert_post', $post_ID, $post, $update );

	return $post_ID;
}

...
Фрагмент из: wp-includes/post.php WP 4.8.2
...
	$wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post->ID ) );

	clean_post_cache( $post->ID );

	$old_status = $post->post_status;
	$post->post_status = 'publish';
	wp_transition_post_status( 'publish', $old_status, $post );

	/** This action is documented in wp-includes/post.php */
	do_action( 'edit_post', $post->ID, $post );

	/** This action is documented in wp-includes/post.php */
	do_action( "save_post_{$post->post_type}", $post->ID, $post, true );

	/** This action is documented in wp-includes/post.php */
	do_action( 'save_post', $post->ID, $post, true );

	/** This action is documented in wp-includes/post.php */
	do_action( 'wp_insert_post', $post->ID, $post, true );
}

/**
 * Publish future post and make sure post ID has future post status.
 *
 * Invoked by cron 'publish_future_post' event. This safeguard prevents cron
 * from publishing drafts, etc.
 *
 * @since 2.5.0
 *
 * @param int|WP_Post $post_id Post ID or post object.
 */
...
Фрагмент из: wp-includes/theme.php WP 4.8.2
...
			$old_status = $post->post_status;
			$new_status = 'trash';
			$wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) );
			clean_post_cache( $post->ID );

			$post->post_status = $new_status;
			wp_transition_post_status( $new_status, $old_status, $post );

			/** This action is documented in wp-includes/post.php */
			do_action( 'edit_post', $post->ID, $post );

			/** This action is documented in wp-includes/post.php */
			do_action( "save_post_{$post->post_type}", $post->ID, $post, true );

			/** This action is documented in wp-includes/post.php */
			do_action( 'save_post', $post->ID, $post, true );

			/** This action is documented in wp-includes/post.php */
			do_action( 'wp_insert_post', $post->ID, $post, true );

			/** This action is documented in wp-includes/post.php */
			do_action( 'trashed_post', $post_id );
		}
	}
}

/**
 * Filters changeset post data upon insert to ensure post_name is intact.
 *
 * This is needed to prevent the post_name from being dropped when the post is
 * transitioned into pending status by a contributor.
...
save_post 27 комментариев
Полезные 1 Вопросы 1 Все
  • Влад

    // обновляем запись, во время которой вызывается save_post

    Что?

    Ответить1.2 года назад #
    • Kama4464

      Имелось ввиду - // обновляем запись. В это время срабатывает событие save_post smile

      Ответить1.2 года назад #
  • campusboy1844 cайт: wp-plus.ru

    Интересная штука, если прописать вот так:

    function save_postratings_for_post( $post_id ) {
    	var_dump($post_id);
    	exit;
    }
    add_action( 'save_post', 'save_postratings_for_post' );

    То мы сможем наблюдать, что при заходе на форму создания поста, он уже имеет ID. Если честно, я удивлен. По идее, зачем это делать?

    Если использовать первый пример, то при заходе на /wp-admin/post-new.php будет сразу отослано письмо.

    P.S.: В базе также появляется auto-draft

    • Kama4464

      Там сложная система на самом деле! При создании записи, сразу создается черновик. А нужно это чтобы была запись в БД, и был ID к которому сразу можно прилепить метаполя и т.д. А иначе придется куда-то это все сохранять, а там система то гибкая. Получается для этого нужно очень многое учесть и по сути нужно написать огромный скрипт, который будет хранить все что может быть привязано к записи. А таким подходом решается сразу все! Потом при публикации остается только изменить статус с draft на publish и готово!

      П.С. Первый пример дополнил, спасибо!

      • campusboy1844 cайт: wp-plus.ru

        Это я понимаю smile Я не понимаю зачем создавать в БД черновик, который потом не виден. К примеру, я захожу, чтобы написать пост. Открыл страницу, потом подумал - нет вдохновение, закрыл страницу. Я не написал ни 1 символа, просто зашёл на страницу и ушёл, а в БД запись есть под статусом auto-draft. И в списке записей нет поста "Черновик" или ещё что-либо, чтобы можно было удалить или отредактировать. То есть эта запись в БД осталась мёртвым грузом, никому не нужная, мусором. Меня не устраивает лишь это и больше ничего smile Это можно почистить только зайдя в БД, через админку добраться до неё уже никак.

        P.S.: Спасибо за правки в коде!

        • Kama4464

          Ну так вот... Потом можно почистить раз в 5 лет. По факту таких пустых заходов очень мало получается... Зато это решает кучу проблем и избавляет от разных конфликтов.

          1
  • ILYUHA

    а save_post_(post_type) работает только с do_action? или с add_action тоже?

  • Дмитрий

    Добрый день, а как быть с отложенными записями? Как я понимаю тут уже этот хук не работает?

    • campusboy1844 cайт: wp-plus.ru

      Привет. Он сработает во время публикации такой записи.

      • Дмитрий

        Сделал функцию которая берет все постмета записи и кладет в нужную мне таблицу, при обычных манипуляциях все работает, но если я создаю пост, делаю его отложенным, то данные не попадают в таблицу

    • Kama4464

      Если нужно делать что-то именно при публикации записи, то читай wp_transition_post_status(). save_post срабатывает только при обновлении записи или публикации так сказать вручную. Но если ВП автоматом меняет статус поста с, например, «черновик» на «опубликован», то save_post не сработает. Поэтому тут нужны другие хуки основанные на функции что я дал выше, там все написано...

  • campusboy1844 cайт: wp-plus.ru

    А во втором примере есть очистка sanitize_text_field. Есть ли в ней смысл? Вроде как в add_metadata есть строка:

    $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
    • Kama4464

      Она не очищает, а включает фильтры, а в том примере мы именно чистим... Т.е. результат если их убрать будет другой...

  • Следующий код работает:

    /**
     * Ищем дубли
     */
    // Эта функция делает все записи в дефолтной категории частными
    function set_private_categories( $post_id ) {
    	// Получим реальный ID поста, если это ревизия
    	if ( $parent_id = wp_is_post_revision( $post_id ) )
    		$post_id = $parent_id;
    
    	// Получим ID категории по умолчанию из опций
    	$defaultcat = get_option( 'default_category' );
    
    	// Удаляем хук, чтобы не было зацикливания
    	remove_action( 'save_post', 'set_private_categories' );
    
    	// обновляем запись. В это время срабатывает событие save_post
    	wp_update_post( array( 'ID' => $post_id, 'post_status' => 'private' ) );
    
    	// Ставим хук обранто
    	add_action( 'save_post', 'set_private_categories' );
    }
    
    add_action( 'save_post', 'set_private_categories' );
    

    Если же сменить хук с save_post на save_post_post , то код запнётся на wp_update_post. Почему?

    • campusboy1844 cайт: wp-plus.ru

      А вы в 3 местах кода сменили с save_post на save_post_post?

      1
      • А вот, похоже, и причина, действительно, невнимательно отнёсся) В 3 местах надо было менять) Спасибо за решение

      • Kama4464

        Да у тебя чутье на чужие ошибки acute

    • Kama4464

      Чет не то делаешь... Должно работать, вроде как. Почему $defaultcat после получения никак не используется?

      • Я взял за образец код из статьи. Причина оказалась в том, что save_post_post надо было менять во всех трёх местах, а не только в одном

  • avense5 cайт: videovegas.ru

    А какой хук будет при сохранении(например изменении статуса) записи в админке на странице записей через быстрое редактирование --> Свойства --> Обновить ?

    • Сергей

      save_post срабатывает при быстром редактировании

      1
      Ответить2 месяца назад #
  • Сергей

    Тимур. Создаю метаполя для записи произвольного типа с помощью вашего класса Kama_Post_Meta_Box. Далее при создании или обновлении записи необходимо проверить(получить) эти метаполя. Для этого во время срабатывания хука save_post_[post-type] вызываю get_post_meta. Но получить эти метаполя могу только если сохранить/обновить запись два раза. То есть запись в метаполе происходит уже после срабатывания хука save_post_[post-type]. Как можно решить эту проблему?

    Ответить2 месяца назад #
    • Сергей

      Вопрос еще актуален!!!

      Ответить2 месяца назад #
    • Сергей

      Тимур. Скажите - мой вопрос глупый или этого сделать нельзя в принципе? Мне нужно при сохранении записи каким то образом получить уже новые данные метаполей, только что измененные и сохраняемые в этом сеансе, а при save_post и save_post_[post-type] получаю данные из предыдущего сохранения.

      Ответить2 месяца назад #
    • Kama4464

      Попробуй поставить приоритет хуку побольше например 9999999. Или просто получай данные из POST данных... Так сложно сказать, много факторов: когда там эти поля обновляются и т.д. Один из двух вариантов выше, по идее должен помочь.

      Ответить2 месяца назад #
  • Алексей @

    так вышло, что админ сайта дал мне на редактирование только 1 файл - шаблон страницы темы, которая сама, в свою очередь, наследуется из ТвэнтиТен.
    я пишу по сути экспорт материалов сайта в спец. формат для моб. приложения, и весь алгоритм уже готов, осталось только прицепить хук на кнопку сохранить, но он не срабатывает.
    я так пишу в своём шаблоне страницы:
    add_action('save_post_scriptures', 'theBookIsUpdated', 10, 3);

    если я его позову в своём же файле, так:
    do_action('save_post_scriptures', $bk->ID, $bk, 0);
    то всё работает.

    но по нажатию кнопки в админке, ни в базу(мою, не сайта), ни в лог, ничего не пишется, т.е хук не срабатывает. я новичок в программировании для ВП, и тут какой-то нюанс, которого мне не хватает. знающие люди, помогите, подскажите, куда рыть, как можно проверить где затык?

    Ответить2 месяца назад #

Здравствуйте, !

Ваш комментарий