
Кілька днів тому я завершив давно назріле завдання, яке потрібно було виконати, але я відкладав його вже дуже давно. Явідновив усівідсутні зображення-ілюстрації та додав до них весь відсутній альтернативний текст. Також я дописав усі відсутні метаописи. Загалом я додав або відредагував понад 15 000 записів , використовуючи три власні одноразові фрагменти коду. Сьогодні я ділюся тут фрагментом коду для відновлення відсутніх зображень, який я також використовував для виконання цього завдання на інших сайтах.
Якщо ви вже деякий час ведете блог, цілком ймовірно, що у вас є чимало старих публікацій, для яких не призначено зображення для попереднього перегляду. Таке часто траплялося в часи класичного редактора, коли ми вставляли зображення безпосередньо в текст публікації, але забували вибрати його у полі «Головне зображення».
Публікація дописів без ілюстрації — це не лише естетична проблема, серед іншого; це безпосередньо впливає на оптимізацію вашого сайту та зручність користування ним:
- Соціальне SEO та Open Graph: під час поширення публікації в соціальних мережах, месенджерах або агрегаторах контенту теги Open Graph (
og:image) не можуть знайти чіткого ресурсу для відображення, що суттєво знижує CTR (коефіцієнт кліків). - Візуальна узгодженість теми: Сучасні шаблони (наприклад, створені за допомогою GeneratePress або GenerateBlocks) використовують зображення-візитівку для формування сіток архівів, списків пов’язаних публікацій або заголовків. Без нього макет порушується або з’являються нерівномірні пробіли.
- Пошук та канали (RSS / Google Discover): Такі інструменти, як Google Discover або RSS-рідери, надають пріоритет або вимагають наявності контенту з мініатюрою високої роздільної здатності для покращення візуального представлення статті.
Щоб вирішити цю проблему без використання плагінів (таких як цей) або повільних процесів, я створив цей простий і компактний фрагмент коду на PHP, який автоматично призначає перше зображення в кожній публікації як головне зображення безпосередньо з панелі управління WordPress. Якщо у вашій публікації немає зображень, ви можете призначити зображення за замовчуванням, додавши його URL-адресу до коду.
Що саме робить цей фрагмент коду?
Цей код додає до списку публікацій WordPress (wp-admin/edit.php) зручний вбудований інструмент пакетної обробки:
- Додати власний стовпець: створіть стовпець «Відсутнє зображення для попереднього перегляду » у списку публікацій, щоб одразу бачити, у яких публікаціях відсутнє мініатюрне зображення.
- 3-етапна обробка: коли ви вмикаєте призначення для публікації, система шукає зображення саме в такому порядку:
- Перевірте, чи є у Медіабібліотеці зображення, прикріплені безпосередньо до публікації.
- Якщо прямих вкладень немає (наприклад, якщо вони були вставлені за допомогою блоку зображень або HTML), проскануйте вміст у пошуках першого тегу
<img>, щоб витягти його ідентифікатор за допомогоюфункції attachment_url_to_postid(). - Якщо запис не містить жодних зображень, система використовує зображення за замовчуванням (резервне), вказане в налаштуваннях.
- Масова обробка за допомогою AJAX (пакетна обробка): у верхній частині сторінки розміщена кнопка («⚡ Призначити всі на цій сторінці»), яка послідовно обробляє всі записи, видимі на екрані, за допомогою послідовних асинхронних запитів. Це також можна зробити вручну, скориставшись окремою кнопкою для кожного допису у списку.
- Фільтр чернеток: автоматично виключає записи зі статусом «чернетка», щоб ви могли зосередитися виключно на опублікованому контенті.
- Стовпець із можливістю сортування: дозволяє відсортувати список таким чином, щоб усі записи без зображення для попереднього перегляду були згруповані разом у верхній частині списку.
Чому він такий легкий і чи не впливає це на продуктивність сервера?
Поширеною проблемою при використанні такого роду автоматизованих інструментів є побоювання, що база даних може «зависнути» або пам’ять PHP може бути перевантажена на сайтах, що містять тисячі статей. Цей фрагмент коду розроблено з урахуванням таких критеріїв оптимізації:
1. Працює лише в розділі «Адміністрація» (is_admin())
Цей код призначений виключно для екрану введення даних в панелі адміністратора (edit.php). Він не додає жодного рядка зайвого коду до запитів на публічному сайті. Це жодним чином не вплине на ваших користувачів та показники швидкості роботи сайту.
2. Направляти запити до метакешу
Щоб визначити, чи містить публікація зображення, фрагмент коду не виконує складних пошуків у базі даних для кожного рядка. Він використовує ключ метаданих _thumbnail_id, який WordPress завантажує в пам’ять безпосередньо через кеш метаданих під час відображення списку. Таблиця завантажується майже миттєво.
3. Каскадна асинхронна обробка (послідовний цикл AJAX)
Замість того, щоб виконувати великий запит до бази даних для оновлення 700 або 1 000 записів за один раз (що могло б призвести до перевищення ліміту часу або вичерпання всієї пам’яті сервера), кнопка «Призначити все» обробляє записи по одному за допомогою обіцянок JavaScript:
POST 1 (AJAX)⟶Відповідь «OK»⟶POST 2 (AJAX)⟶Відповідь «OK»…
Це забезпечує стабільне та послідовне використання ресурсів, що дозволяє відстежувати прогрес у режимі реального часу, рядок за рядком.
4. Відсутність дублікатів файлів
Цей фрагмент коду не завантажує, не об’єднує та не дублює зображення на вашому жорсткому диску. Він просто бере ідентифікатор зображення, яке вже є у вашій медіабібліотеці, і пов’язує його з публікацією, присвоюючи відповідне значення meta_value у таблиці wp_postmeta.
Код (PHP + JavaScript)
Ви можете додати цей код до файлу functions.php вашої дочірньої теми, до власного плагіна або за допомогою плагіна для користувацьких функцій, наприклад Code Snippets чи подібного.
Примітка: Не забудьте встановити для змінної
$default_image_urlу рядку 56 повну URL-адресу загального зображення, яке ви хочете призначити для публікацій, текст яких не містить жодних зображень.
PHP
// 1. Añadir columna personalizada en el listado de entradas
add_filter( 'manage_posts_columns', 'custom_featured_status_column' );
function custom_featured_status_column( $columns )
$columns['featured_missing'] = 'Destacada Faltante';
return $columns;
}
// 2. Hacer la columna ordenable al hacer clic en el encabezado
add_filter( 'manage_edit-post_sortable_columns', 'custom_featured_sortable_column' );
function custom_featured_sortable_column( $columns ) {
$columns['featured_missing'] = 'featured_missing';
return $columns;
}
// 3. Modificar la consulta para ordenar por presencia de _thumbnail_id
add_action( 'pre_get_posts', 'custom_featured_column_orderby' );
function custom_featured_column_orderby( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) return;
if ( $query->get( 'orderby' ) === 'featured_missing' ) {
$query->set( 'meta_key', '_thumbnail_id' );
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_compare', 'NOT EXISTS' );
}
}
// 3.1. Añadir desplegable de filtro nativo en el listado de entradas
add_action( 'restrict_manage_posts', 'custom_featured_filter_dropdown' );
function custom_featured_filter_dropdown( $post_type ) {
if ( $post_type !== 'post' ) return;
$selected = isset( $_GET['filter_featured_status'] ) ? sanitize_text_field( $_GET['filter_featured_status'] ) : '';
?>
<select name="filter_featured_status" id="filter_featured_status">
<option value=""><?php _e( 'Todas las destacadas', 'textdomain' ); ?></option>
<option value="has_img" <?php selected( $selected, 'has_img' ); ?>>Tiene</option>
<option value="missing_img" <?php selected( $selected, 'missing_img' ); ?>>Falta</option>
</select>
<?php
}
// 3.2. Aplicar el filtro del desplegable a la consulta de WP_Query
add_filter( 'parse_query', 'custom_featured_filter_apply' );
function custom_featured_filter_apply( $query ) {
global $pagenow;
if ( ! is_admin() || $pagenow !== 'edit.php' || ! $query->is_main_query() ) return;
if ( isset( $_GET['filter_featured_status'] ) && $_GET['filter_featured_status'] !== '' ) {
$filter_value = sanitize_text_field( $_GET['filter_featured_status'] );
$meta_query = (array) $query->get( 'meta_query' );
if ( $filter_value === 'has_img' ) {
$meta_query[] = array(
'key' => '_thumbnail_id',
'compare' => 'EXISTS',
);
} elseif ( $filter_value === 'missing_img' ) {
$meta_query[] = array(
'key' => '_thumbnail_id',
'compare' => 'NOT EXISTS',
);
}
$query->set( 'meta_query', $meta_query );
}
}
// 4. Mostrar el estado y el botón rápido en la columna (Excluyendo borradores)
add_action( 'manage_posts_custom_column', 'custom_featured_status_column_content', 10, 2 );
function custom_featured_status_column_content( $column, $post_id ) {
if ( $column === 'featured_missing' ) {
if ( get_post_type( $post_id ) !== 'post' || get_post_status( $post_id ) === 'draft' ) {
echo '<span style="color: #999;">—</span>';
return;
}
$has_thumbnail = get_post_meta( $post_id, '_thumbnail_id', true );
if ( $has_thumbnail ) {
echo '<span style="color: #46b450; font-weight: bold;">✔ Tiene</span>';
} else {
echo '<span style="color: #dc3232; font-weight: bold;">❌ Falta</span> ';
echo '<button type="button" class="button button-small set-first-img-btn" data-postid="' . esc_attr( $post_id ) . '">Asignar imagen</button>';
echo '<span class="set-img-status" style="margin-left:5px;"></span>';
}
}
}
// 5. Procesar la asignación por AJAX (con fallback a la imagen por defecto)
add_action( 'wp_ajax_set_first_image_as_featured', 'ajax_set_first_image_as_featured' );
function ajax_set_first_image_as_featured() {
check_ajax_referer( 'set_first_image_nonce', 'nonce' );
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( 'Sin permisos o ID inválido' );
}
if ( get_post_status( $post_id ) === 'draft' ) {
wp_send_json_error( 'La entrada es un borrador' );
}
// =========================================================================
// CONFIGURACIÓN: Reemplaza esta URL por la URL completa de tu imagen por defecto
// =========================================================================
$default_image_url = 'https://tu-dominio.com/wp-content/uploads/imagen-por-defecto.jpg';
// A. Buscar adjuntos directos al post
$attachments = get_posts( array(
'post_type' => 'attachment',
'posts_per_page' => 1,
'post_parent' => $post_id,
'post_mime_type' => 'image',
'orderby' => 'menu_order',
'order' => 'ASC',
'fields' => 'ids'
) );
if ( ! empty( $attachments ) ) {
set_post_thumbnail( $post_id, $attachments[0] );
wp_send_json_success( '¡Asignada primera!' );
}
// B. Si no hay adjuntos, buscar <img> en el contenido
$post = get_post( $post_id );
preg_match_all( '/<img.+?src=[\'"]([^\'"]+)[\'"]/i', $post->post_content, $matches );
if ( isset( $matches[1][0] ) ) {
$attachment_id = attachment_url_to_postid( $matches[1][0] );
if ( $attachment_id ) {
set_post_thumbnail( $post_id, $attachment_id );
wp_send_json_success( '¡Asignada primera!' );
}
}
// C. Si no hay ninguna imagen en el post, asignar la imagen por defecto
if ( ! empty( $default_image_url ) ) {
$default_id = attachment_url_to_postid( $default_image_url );
if ( $default_id ) {
set_post_thumbnail( $post_id, $default_id );
wp_send_json_success( '¡Asignada por defecto!' );
}
}
wp_send_json_error( 'No se encontró ninguna imagen en el post ni ID de fallback' );
}
// 6. Cargar JS y botón masivo en el admin (edit.php)
add_action( 'admin_footer-edit.php', 'custom_featured_ajax_script' );
function custom_featured_ajax_script() {
$screen = get_current_screen();
if ( $screen->post_type !== 'post' ) return;
$nonce = wp_create_nonce( 'set_first_image_nonce' );
?>
<script>
jQuery(document).ready(function($) {
$('<button type="button" id="process-all-page-btn" class="page-title-action" style="margin-left:10px; background:#2271b1; color:#fff; border-color:#2271b1;">⚡ Asignar todas en esta página</button>')
.insertAfter('.wp-heading-inline');
function processButton($btn) {
return new Promise(function(resolve) {
var $status = $btn.siblings('.set-img-status');
var postId = $btn.data('postid');
$btn.prop('disabled', true).text('Procesando...');
$.post(ajaxurl, {
action: 'set_first_image_as_featured',
post_id: postId,
nonce: '<?php echo $nonce; ?>'
}, function(response) {
if (response.success) {
$btn.parent().html('<span style="color: #46b450; font-weight: bold;">✔ ' + response.data + '</span>');
} else {
$status.css('color', '#dc3232').text(response.data);
$btn.prop('disabled', false).text('Asignar imagen');
}
resolve();
}).fail(function() {
$btn.prop('disabled', false).text('Error');
resolve();
});
});
}
$(document).on('click', '.set-first-img-btn', function(e) {
e.preventDefault();
processButton($(this));
});
$('#process-all-page-btn').on('click', async function(e) {
e.preventDefault();
var $batchBtn = $(this);
var $pendingButtons = $('.set-first-img-btn:not(:disabled)');
if ($pendingButtons.length === 0) {
alert('No hay entradas pendientes de asignar en esta página.');
return;
}
$batchBtn.prop('disabled', true).text('Procesando por lote...');
for (var i = 0; i < $pendingButtons.length; i++) {
await processButton($($pendingButtons[i]));
}
$batchBtn.text('¡Lote completado!').css('background', '#46b450');
setTimeout(function() {
$batchBtn.prop('disabled', false).text('⚡ Asignar todas en esta página').css('background', '#2271b1');
}, 3000);
});
});
</script>
<?php
}
Як ним користуватися?
- Перейдіть до списку «Публікації» у вашій адміністративній панелі.
- Відкрийте вкладку «Параметри відображення» у верхній частині екрана та змініть кількість елементів, що відображаються на сторінці, на 50 або 100. (Почніть із менших значень і поступово збільшуйте їх, якщо сервер добре реагує.)

- Натисніть на заголовок стовпця «Відсутні виділені елементи», щоб відсортувати та згрупувати незавершені записи. У фільтрах можна вибрати один із варіантів: «Є», «Відсутні» або «Усі».
- Коли ви переконаєтеся, що готові розпочати пакетне призначення відсутніх зображень для попереднього перегляду, натисніть синю кнопку « » з написом «Призначити всі на цій сторінці» і дочекайтеся, поки каскадний процес завершить призначення мініатюр для цього пакета. Якщо бажаєте, ви можете призначати їх поодинці.
- Перейдіть на наступну сторінку і повторюйте ці дії, доки не завершите впорядкування своєї бібліотеки.
Необов’язково: фрагмент коду для автоматичного присвоєння
Щоб у майбутньому не доводилося повторювати цю дію, ви можете додати цей короткий фрагмент коду. Він виконується у фоновому режимі лише під час збереження або публікації допису: якщо він виявляє, що ви не вибрали зображення для обкладинки вручну, він автоматично призначає перше зображення у вмісті (або запасне зображення за замовчуванням, яке вам потрібно додати у $default_image_url =).
PHP
// Asignar imagen destacada automáticamente al publicar/guardar nuevas entradas
add_action( 'save_post_post', 'auto_set_featured_image_on_save', 10, 3 );
function auto_set_featured_image_on_save( $post_id, $post, $update ) {
// Evitar autoguardados, revisiones o peticiones AJAX/REST innecesarias
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( wp_is_post_revision( $post_id ) || $post->post_status === 'auto-draft' ) return;
// Si la entrada ya tiene imagen destacada marcada, no hacemos nada
if ( has_post_thumbnail( $post_id ) ) return;
$default_image_url = 'https://tu-dominio.com/wp-content/uploads/imagen-por-defecto.jpg';
// 1. Buscar primera imagen en el contenido
preg_match_all( '/<img.+?src=[\'"]([^\'"]+)[\'"]/i', $post->post_content, $matches );
if ( isset( $matches[1][0] ) ) {
$attachment_id = attachment_url_to_postid( $matches[1][0] );
if ( $attachment_id ) {
set_post_thumbnail( $post_id, $attachment_id );
return;
}
}
// 2. Si el post no tiene imágenes, asignar la imagen por defecto
if ( ! empty( $default_image_url ) ) {
$default_id = attachment_url_to_postid( $default_image_url );
if ( $default_id ) {
set_post_thumbnail( $post_id, $default_id );
}
}
}





