
कुछ दिन पहले, मैंने एक लंबे समय से लंबित कार्य पूरा किया, जिसे करना जरूरी था, लेकिन मैं इसे लंबे समय से टालता रहा था। मैंनेसभीगायब फीचर्ड इमेजेज़को पुनः प्राप्त कियाऔर सभी इमेजेज़ में गायब ऑल्ट टेक्स्ट जोड़ा। मैंने सभी गायब मेटा डिस्क्रिप्शन लिखना भी पूरा किया। कुल मिलाकर, मैंने अपनी तीन एकल स्निपेट्स का उपयोग करके 15,000 से अधिक रिकॉर्ड जोड़े या संपादित किए। आज, मैं यहाँ उन स्निपेट्स में से एक साझा कर रहा हूँ, जिसका उपयोग मैंने गायब फीचर्ड इमेजेज़ प्राप्त करने के लिए किया, और इसी स्निपेट का उपयोग मैंने अन्य साइटों पर भी यह कार्य करने के लिए किया।
यदि आप कुछ समय से ब्लॉगिंग कर रहे हैं, तो बहुत संभव है कि आपके पास कई पुरानी पोस्ट हों जिनके लिए कोई फीचर्ड इमेज नहीं दी गई हो। क्लासिक एडिटर के दिनों में यह अक्सर होता था, जब हम पोस्ट की मुख्य सामग्री में सीधे छवि डाल देते थे लेकिन 'फीचर्ड इमेज' बॉक्स में उसे चुनना भूल जाते थे।
पोस्ट्स को फीचर्ड इमेज के बिना छोड़ना केवल एक सौंदर्य संबंधी समस्या नहीं है; यह सीधे आपकी साइट के अनुकूलन और उपयोगकर्ता अनुभव को प्रभावित करता है:
- सोशल SEO और ओपन ग्राफ़: जब पोस्ट को सोशल मीडिया, मैसेजिंग ऐप्स या कंटेंट एग्रीगेटर्स पर साझा किया जाता है, तो ओपन ग्राफ़ टैग्स (
og:image) प्रदर्शित करने के लिए कोई स्पष्ट संसाधन नहीं ढूंढ पाते, जिससे CTR (क्लिक-थ्रू रेट) में भारी कमी आ जाती है। - थीम में दृश्य सुसंगति: आधुनिक टेम्प्लेट्स (जैसे GeneratePress या GenerateBlocks का उपयोग करके डिज़ाइन किए गए) आर्काइव ग्रिड, संबंधित पोस्ट की सूचियाँ या हेडर बनाने के लिए फीचर्ड इमेज पर निर्भर करते हैं। इसके बिना लेआउट टूट जाता है या असंगत रिक्तियाँ रह जाती हैं।
- डिस्कवरी और फ़ीड्स (RSS / Google Discover): Google Discover या RSS रीडर्स जैसे टूल्स लेख कार्ड को बेहतर बनाने के लिए उच्च-रिज़ॉल्यूशन वाले थंबनेल के साथ जुड़ी सामग्री को प्राथमिकता देते हैं या उसकी आवश्यकता होती है।
प्लगइन्स (जैसे यह वाला) या धीमी प्रक्रियाओं का सहारा लिए बिना इसे सुलझाने के लिए, मैंने यह सरल, हल्का PHP स्निपेट बनाया है जो प्रत्येक पोस्ट की पहली छवि को सीधे वर्डप्रेस डैशबोर्ड से फीचर्ड इमेज के रूप में स्वचालित रूप से सेट करता है। यदि आपकी पोस्ट में कोई छवि नहीं है, तो आप कोड में उसकी URL जोड़कर एक डिफ़ॉल्ट छवि निर्धारित कर सकते हैं।
यह स्निपेट वास्तव में क्या करता है?
यह कोड वर्डप्रेस पोस्ट सूची (wp-admin/edit.php) में एक स्मार्ट, अंतर्निहित बैच प्रोसेसिंग टूल जोड़ता है:
- एक कस्टम कॉलम जोड़ें: पोस्टों की सूची में 'Missing Featured Image' कॉलम बनाएँ ताकि एक नज़र में पता चल सके कि किन पोस्टों में थंबनेल नहीं है।
- 3-परत प्रक्रिया: जब आप किसी पोस्ट के लिए असाइनमेंट सक्षम करते हैं, तो यह इस सटीक क्रम में छवि की खोज करता है:
- जाँचें कि मीडिया लाइब्रेरी में पोस्ट से सीधे कोई छवियाँ संलग्न हैं या नहीं।
- यदि कोई सीधे संलग्नक नहीं हैं (उदाहरण के लिए, यदि उन्हें इमेज ब्लॉक या HTML के माध्यम से डाला गया हो), तो
attachment_url_to_postid()का उपयोग करके इसकी आईडी निकालने के लिए सामग्री में पहले<span>img</span>टैग को स्कैन करें। - यदि प्रविष्टि में बिल्कुल भी कोई छवि नहीं है, तो यह कॉन्फ़िगरेशन में निर्दिष्ट डिफ़ॉल्ट (फॉलबैक) छवि को असाइन करता है।
- AJAX थोक प्रसंस्करण (बैच प्रसंस्करण): हेडर में एक बटन ( "⚡ इस पृष्ठ पर सभी असाइन करें") शामिल है, जो स्क्रीन पर दिखाई देने वाली सभी प्रविष्टियों को क्रमिक रूप से लगातार असिंक्रोनस अनुरोधों का उपयोग करके संसाधित करता है। आप सूची में प्रत्येक पोस्ट के लिए व्यक्तिगत बटन का उपयोग करके इसे मैन्युअल रूप से भी कर सकते हैं।
- ड्राफ्ट फ़िल्टर: स्वचालित रूप से ड्राफ्ट स्थिति वाली प्रविष्टियों को बाहर रखता है ताकि आप केवल प्रकाशित सामग्री पर ध्यान केंद्रित कर सकें।
- सॉर्ट करने योग्य स्तंभ: आपको सूची को इस तरह क्रमबद्ध करने की अनुमति देता है कि फीचर्ड इमेज न होने वाली सभी प्रविष्टियाँ शीर्ष पर एक साथ समूहबद्ध हो जाएँ।
यह इतना हल्का क्यों है और क्या यह सर्वर के प्रदर्शन को प्रभावित नहीं करता?
इस प्रकार के स्वचालित उपकरण का उपयोग करते समय एक आम चिंता यह होती है कि डेटाबेस लॉक हो जाए या हजारों लेखों वाली साइटों पर PHP मेमोरी ओवरलोड हो जाए। यह स्निपेट निम्नलिखित अनुकूलन मानदंडों के अनुरूप तैयार किया गया है:
1. यह केवल एडमिन सेक्शन में ही चलता है (is_admin())
यह कोड विशेष रूप से प्रशासन पैनल की इनपुट स्क्रीन (edit.php) के लिए पैकेज किया गया है। यह सार्वजनिक साइट पर किए गए अनुरोधों में एक भी अतिरिक्त लाइन नहीं जोड़ता। आपके उपयोगकर्ता और गति मेट्रिक्स किसी भी तरह से प्रभावित नहीं होंगे ।
2. मेटा कैश को सीधे क्वेरी करें
यह निर्धारित करने के लिए कि किसी पोस्ट में छवि है या नहीं, यह स्निपेट प्रत्येक पंक्ति के लिए जटिल डेटाबेस खोजें नहीं करता। यह _thumbnail_id मेटाडेटा कुंजी का उपयोग करता है, जिसे वर्डप्रेस सूची रेंडर करते समय मेटाडेटा कैश के माध्यम से मूल रूप से मेमोरी में लोड करता है। तालिका लगभग तुरंत लोड हो जाती है।
3. क्रमिक असिंक्रोनस प्रोसेसिंग (क्रमिक AJAX लूप)
डेटाबेस पर एक ही बार में 700 या 1,000 प्रविष्टियाँ अपडेट करने के लिए एक विशाल क्वेरी चलाने के बजाय (जिससे टाइमआउट हो सकता है या सर्वर की पूरी मेमोरी खर्च हो सकती है), 'Assign All' बटन जावास्क्रिप्ट प्रॉमिस का उपयोग करके प्रविष्टियों को एक-एक करके संसाधित करता है:
POST 1 (AJAX)⟶OK प्रतिक्रिया⟶POST 2 (AJAX)⟶OK प्रतिक्रिया…
यह संसाधनों के स्थिर और सुसंगत उपयोग को सुनिश्चित करता है, जिससे आप पंक्ति-दर-पंक्ति वास्तविक समय में प्रगति देख सकते हैं।
4. फ़ाइल की नकल न करें
यह स्निपेट आपकी हार्ड ड्राइव पर छवियों को डाउनलोड, पुनः संयोजित या डुप्लिकेट नहीं करता है। यह केवल आपकी मीडिया लाइब्रेरी में पहले से मौजूद छवि की आईडी लेता है और wp_postmeta तालिका में संबंधित meta_value असाइन करके उसे पोस्ट से लिंक करता है।
कोड (PHP + JavaScript)
आप इस कोड को अपने चाइल्ड थीम की functions.php फ़ाइल में, एक कस्टम प्लगइन में, या Code Snippets जैसे कस्टम फ़ंक्शंस प्लगइन के माध्यम से जोड़ सकते हैं।
नोट: याद रखें कि लाइन 56 पर
$default_image_urlचर को उस सामान्य छवि के पूर्ण URL पर सेट करें जिसे आप उन पोस्टों को असाइन करना चाहते हैं जिनमें टेक्स्ट में कोई छवि नहीं है।
पीएचपी
// 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 में बदलें। (छोटी संख्याओं से शुरू करें और यदि आपका सर्वर अच्छी प्रतिक्रिया देता है तो धीरे-धीरे संख्या बढ़ाएं।)

- बाकी प्रविष्टियों को क्रमबद्ध और समूहित करने के लिए 'Missing Highlights' कॉलम हेडर पर क्लिक करें। फ़िल्टर में आप 'Has', 'Missing' या 'All' में से चुन सकते हैं।
- एक बार जब आप सुनिश्चित हो जाएं कि आप बैचों में अनुपस्थित फीचर्ड इमेज असाइन करना शुरू करने के लिए तैयार हैं, तो नीले ' ' बटन पर क्लिक करें, जिस पर 'इस पृष्ठ पर सभी असाइन करें' लिखा है, और उस बैच के थंबनेल असाइन करने की कैस्केडिंग प्रक्रिया को पूरा होने दें। यदि आप चाहें तो इन्हें एक-एक करके भी असाइन कर सकते हैं।
- अगले पृष्ठ पर जाएँ और अपनी लाइब्रेरी की सफाई पूरी होने तक दोहराएँ।
वैकल्पिक: स्वचालित असाइनमेंट स्निपेट
भविष्य में इस कार्य को दोहराने से बचने के लिए, आप यह छोटा स्निपेट जोड़ सकते हैं। यह केवल तभी पृष्ठभूमि में चलता है जब आप कोई पोस्ट सहेजते या प्रकाशित करते हैं: यदि यह पता लगाता है कि आपने मैन्युअल रूप से कोई फीचर्ड इमेज नहीं चुना है, तो यह स्वचालित रूप से सामग्री में मौजूद पहली इमेज (या डिफ़ॉल्ट फॉलबैक इमेज, जिसे आपको $default_image_url = में जोड़ना होगा) को असाइन कर देता है।
पीएचपी
// 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 );
}
}
}








