Удалить медиа в медиатеке WordPress при редактировании записи через jetformbuilder

Давайте добавим небольшой код в дочернюю тему, чтобы при редактировании записи фронтенд через форму jetformbuilder, удалялись старые медиа.

Кстати удалить медиа при удалении поста фронтенд через форму jetformbuilder очень просто, смотрите тут.

Обязательно! При создании кастомной записи CPT, метаполе с медиа должно быть по id. Иначе не будут удалять медиа при редактировании записи.

snimok jekrana 2026 02 16 v 12.37.46

В моём случае я хочу, чтобы удалялись старые медиа при редактировании записи: Миниатюра записи и две медиагалереи.

В моём примере:
Post Type Slug — doctor
id формы jetformbuilder — 25958
Метаполя галерей — imgdoctor, gallerydoctor

Важно! Старое медиа миниатюра записи удаляется через 60 секунд.

Добавьте код в дочернюю тему и измените в коде id и название метаполей!

/* =======================
   JFB: delete detached media for galleries + featured image (thumbnail)
   - Galleries meta keys: imgdoctor, gallerydoctor
   - Old thumbnail is deleted via WP-Cron with delay (safer for heavy requests)
   ======================= */
const AVS_JFB_FORM_ID   = 25958; /*ИЗЕНИТЬ ТУТ!*/
const AVS_JFB_TARGET_POST_TYPE   = 'doctor'; /*ИЗЕНИТЬ ТУТ!*/
const AVS_JFB_LAST_STATE_META = '_avs_jfb_last_saved_media_state_25958'; /*ИЗЕНИТЬ ТУТ!*/
const AVS_JFB_THUMB_DELETE_HOOK = 'avs_jfb_delete_old_thumb_25958'; /*ИЗЕНИТЬ ТУТ!*/
const AVS_JFB_THUMB_DELAY_SEC   = 60;

/**
 * Две галереи (мета-ключи, где хранятся ID вложений)
 */
function avs_jfb_gallery_meta_keys(): array {
	return [ 'imgdoctor', 'gallerydoctor' ];  /*ИЗЕНИТЬ ТУТ!*/
}

/**
 * Читаем ID вложений из меты. Берём ПОСЛЕДНЮЮ meta-строку как "текущее значение".
 */
function avs_jfb_collect_ids_from_meta_latest( int $post_id, string $meta_key ): array {
	$vals = get_post_meta( $post_id, $meta_key, false );
	if ( empty( $vals ) ) {
		return [];
	}

	$val = end( $vals );
	$ids = [];

	if ( is_array( $val ) ) {
		foreach ( $val as $x ) {
			if ( is_numeric( $x ) && (int) $x > 0 ) {
				$ids[] = (int) $x;
			} elseif ( is_array( $x ) && isset( $x['id'] ) && is_numeric( $x['id'] ) ) {
				$ids[] = (int) $x['id'];
			}
		}
	} elseif ( is_string( $val ) ) {
		foreach ( explode( ',', $val ) as $x ) {
			$x = trim( $x );
			if ( is_numeric( $x ) && (int) $x > 0 ) {
				$ids[] = (int) $x;
			}
		}
	} elseif ( is_numeric( $val ) && (int) $val > 0 ) {
		$ids[] = (int) $val;
	}

	return array_values( array_unique( $ids ) );
}

/**
 * Миниатюра: тоже берём ПОСЛЕДНЮЮ meta-строку _thumbnail_id как "текущее значение".
 */
function avs_jfb_get_current_thumbnail_id_latest( int $post_id ): int {
	$vals = get_post_meta( $post_id, '_thumbnail_id', false );
	if ( empty( $vals ) ) {
		return 0;
	}

	$val = end( $vals );
	return is_numeric( $val ) ? (int) $val : 0;
}

function avs_jfb_build_current_state_by_keys( int $post_id ): array {
	$keys = avs_jfb_gallery_meta_keys();

	$state = [
		'keys'  => $keys,
		'data'  => [],
		'thumb' => avs_jfb_get_current_thumbnail_id_latest( $post_id ),
		'time'  => time(),
	];

	foreach ( $keys as $key ) {
		$state['data'][ $key ] = avs_jfb_collect_ids_from_meta_latest( $post_id, $key );
	}

	return $state;
}

/**
 * Удаляем откреплённые из галерей - сразу.
 * Старую миниатюру - откладываем на WP-Cron (через 60 сек).
 */
function avs_jfb_process_removed( int $post_id, array $prev_state, array $cur_state ): void {
	$keys = isset( $cur_state['keys'] ) && is_array( $cur_state['keys'] ) ? $cur_state['keys'] : [];
	$current_thumb = (int) ( $cur_state['thumb'] ?? 0 );

	$prev_data = ( isset( $prev_state['data'] ) && is_array( $prev_state['data'] ) ) ? $prev_state['data'] : [];
	$cur_data  = ( isset( $cur_state['data'] ) && is_array( $cur_state['data'] ) ) ? $cur_state['data'] : [];

	// 1) Галереи
	foreach ( $keys as $key ) {
		$key = (string) $key;

		$before_ids = isset( $prev_data[ $key ] ) && is_array( $prev_data[ $key ] ) ? $prev_data[ $key ] : [];
		$after_ids  = isset( $cur_data[ $key ] ) && is_array( $cur_data[ $key ] ) ? $cur_data[ $key ] : [];

		$removed = array_values( array_unique( array_diff( $before_ids, $after_ids ) ) );

		foreach ( $removed as $att_id ) {
			$att_id = (int) $att_id;
			if ( $att_id <= 0 ) continue;
			if ( $att_id === $current_thumb ) continue; // не удаляем текущую миниатюру
			if ( get_post_type( $att_id ) !== 'attachment' ) continue;

			$parent = (int) wp_get_post_parent_id( $att_id );

			// разрешаем parent=0 тоже (бывает "не прикреплено")
			if ( $parent !== 0 && $parent !== $post_id ) {
				continue;
			}

			wp_delete_attachment( $att_id, true );
		}
	}

	// 2) Миниатюра (старую удаляем позже)
	$before_thumb = (int) ( $prev_state['thumb'] ?? 0 );
	$after_thumb  = (int) ( $cur_state['thumb'] ?? 0 );

	if ( $before_thumb > 0 && $before_thumb !== $after_thumb && get_post_type( $before_thumb ) === 'attachment' ) {
		$args = [ $post_id, $before_thumb ];

		if ( ! wp_next_scheduled( AVS_JFB_THUMB_DELETE_HOOK, $args ) ) {
			wp_schedule_single_event( time() + AVS_JFB_THUMB_DELAY_SEC, AVS_JFB_THUMB_DELETE_HOOK, $args );
		}
	}
}

/**
 * WP-Cron: удаляем старую миниатюру, если она больше не текущая.
 */
add_action( AVS_JFB_THUMB_DELETE_HOOK, function ( $post_id, $thumb_id ) {
	$post_id  = (int) $post_id;
	$thumb_id = (int) $thumb_id;

	if ( ! $post_id || get_post_type( $post_id ) !== AVS_JFB_TARGET_POST_TYPE ) return;
	if ( $thumb_id <= 0 || get_post_type( $thumb_id ) !== 'attachment' ) return;

	$current_thumb = avs_jfb_get_current_thumbnail_id_latest( $post_id );
	if ( $current_thumb === $thumb_id ) {
		return; // всё ещё текущая миниатюра - не удаляем
	}

	$parent = (int) wp_get_post_parent_id( $thumb_id );
	if ( $parent !== 0 && $parent !== $post_id ) {
		return;
	}

	wp_delete_attachment( $thumb_id, true );
}, 10, 2 );

/**
 * Главный хук: после сохранения формы (update/insert) - на shutdown сравниваем состояния.
 */
$avs_jfb_register_cleanup = function ( $action, $handler ) {

	$form_id = (int) ( $handler->form_id ?? 0 );
	if ( $form_id !== AVS_JFB_FORM_ID ) return;

	$post_id = method_exists( $handler, 'get_inserted_post_id' )
		? (int) $handler->get_inserted_post_id( $action->_id )
		: 0;

	if ( ! $post_id || get_post_type( $post_id ) !== AVS_JFB_TARGET_POST_TYPE ) return;

	add_action( 'shutdown', function () use ( $post_id ) {

		$cur_state  = avs_jfb_build_current_state_by_keys( $post_id );
		$prev_state = get_post_meta( $post_id, AVS_JFB_LAST_STATE_META, true );

		// Первый запуск для конкретной записи: просто сохраняем baseline
		if ( ! is_array( $prev_state ) || empty( $prev_state ) ) {
			update_post_meta( $post_id, AVS_JFB_LAST_STATE_META, $cur_state );
			return;
		}

		avs_jfb_process_removed( $post_id, $prev_state, $cur_state );

		// обновляем baseline
		update_post_meta( $post_id, AVS_JFB_LAST_STATE_META, $cur_state );
	}, 999 );
};

add_action( 'jet-form-builder/action/after-post-update', $avs_jfb_register_cleanup, 50, 2 );
add_action( 'jet-form-builder/action/after-post-insert', $avs_jfb_register_cleanup, 50, 2 );

Как работает удаление старых медиа при редактировании записи через форму

Когда запись сохраняется через форму, скрипт делает следующее:

1) Считывает текущее состояние медиа в записи:

  • какие изображения сейчас указаны в полях галерей (imgdoctor и gallerydoctor)
  • какая картинка стоит как миниатюра записи (featured image)

2) Сравнивает это состояние с тем, что было при предыдущем сохранении.
Для этого в записи автоматически создаётся и обновляется служебное метаполе:
_avs_jfb_last_saved_media_state_25958
В нём хранится список ID изображений, которые были прикреплены к записи на момент прошлого сохранения.

3) Если после редактирования какие-то изображения были удалены из галереи или заменена миниатюра, скрипт считает такие изображения “откреплёнными” и удаляет их из медиабиблиотеки:

  • удалённые из галереи — удаляются сразу
  • старая миниатюра — удаляется с небольшой задержкой (примерно 60 секунд) через WP-Cron, чтобы не нагружать сохранение формы.

Важно:

  • При первом сохранении записи через форму ничего не удаляется — сначала создаётся “базовое состояние”, с которым дальше идёт сравнение.
Picture of Автор: Александра

Автор: Александра

@avsalexandra
Занимаюсь натуральным питанием собак и кошек BARF. Wordpress для души ☺️

Crocoblock
Elementor
Gutenberg
Jetengine
Jetformbuilder
profile builder
Woocommerce
Wordpress
WYSIWYG
Лейка
#автосохранение
#доменная почта
#рассылка
#бейдж
#благотворительность
#заказ ожидает
#подарок
#подчёркивание
#публикация постов
#видео
#пожертвования
#мультивыбор
#роли
#drag and drop
#изображения товаров
#подписки
#распродажа
#личный кабинет
#пагинация
#alt text
#галерея товара
#аватар
#возврат
#видео товара
#купон
#отменить заказ
Комментарии:

Добавить комментарий