WordPress’te eksik öne çıkan görselleri toplu olarak atama

No comments

05.09.2026|

No comments

Tiempo de lectura Lectura: 13 min, 33 s
Número de palabras Palabras: 2508
Número de visitas Visitas: 22
Icono de traducción
WordPress’te eksik öne çıkan resimleri toplu olarak atayın. Hem eksik hem de mevcut resimleri gösteren bir liste.

Birkaç gün önce, uzun zamandır yapılması gereken ancak yıllardır ertelediğim bir işi tamamladım. Eksik olantümöne çıkan görselleribuldumve görsellere eksik olan tüm alternatif metinleri ekledim. Ayrıca eksik olan tüm meta açıklamalarını da yazmayı bitirdim. Toplamda, kendi hazırladığım üç tek seferlik snippet’i kullanarak 15.000’den fazla kaydı ekledim veya düzenledim. Bugün, diğer sitelerde de bu görevi yerine getirmek için kullandığım, eksik öne çıkan görselleri geri getirme snippet’ini burada paylaşıyorum.

Bir süredir blog yazıyorsanız, öne çıkan resim atanmamış pek çok eski yazınız olması oldukça muhtemeldir. Bu durum, klasik düzenleyiciyi kullandığımız günlerde sık sık yaşanırdı; o zamanlar görseli doğrudan yazının gövdesine eklerdik, ancak "Öne Çıkan Görsel" kutucuğunda onu seçmeyi unuturduk.

Öne çıkan resim içermeyen yazılar yayınlamak, diğer hususların yanı sıra sadece estetik bir sorun değildir; bu durum, sitenizin arama motoru optimizasyonunu ve kullanıcı deneyimini doğrudan etkiler:

  1. Sosyal SEO ve Open Graph: Bir gönderiyi sosyal medyada, mesajlaşma uygulamalarında veya içerik toplayıcılarında paylaşırken, Open Graph etiketleri (og:image) görüntülenecek net bir kaynak bulamıyor ve bu da tıklama oranını (CTR) önemli ölçüde düşürüyor.
  2. Temadaki görsel tutarlılık: Modern şablonlar (GeneratePress veya GenerateBlocks kullanılarak tasarlananlar gibi), arşiv ızgaralarını, ilgili yazı listelerini veya başlıkları oluştururken öne çıkan resme dayanır. Bu resim olmadığında, mizanpaj bozulur veya hizasız boşluklar oluşur.
  3. Keşif ve beslemeler (RSS / Google Discover): Google Discover veya RSS okuyucuları gibi araçlar, makale kartını daha çekici hale getirmek için yüksek çözünürlüklü bir küçük resme sahip içeriğe öncelik verir veya bunu zorunlu kılar.

Eklentilere (örneğin buna) veya yavaş işlemlere başvurmadan bu sorunu çözmek için, her yazının ilk resmini doğrudan WordPress kontrol panelinden otomatik olarak öne çıkan resim olarak ayarlayan bu basit ve hafif PHP kod parçasını oluşturdum. Yazınızda hiç resim yoksa, kodun içine URL’sini ekleyerek varsayılan bir resim atayabilirsiniz.

Bu kod parçacığı tam olarak ne işe yarıyor?

Bu kod, WordPress yazı listesine (wp-admin/edit.php) akıllı ve yerleşik bir toplu işleme aracı ekler:

  • Özel bir sütun ekleyin: Hangi yazılarda küçük resim bulunmadığını bir bakışta görebilmek için yazı listesine “Eksik Öne Çıkan Resim” sütununu oluşturun.
  • 3 aşamalı işleme: Bir gönderinin atamasını etkinleştirdiğinizde, resim tam olarak şu sırayla aranır:
    1. Medya Kitaplığı’nda gönderiye doğrudan eklenmiş herhangi bir resim olup olmadığını kontrol edin.
    2. Doğrudan ekler yoksa (örneğin, bir resim bloğu veya HTML aracılığıyla eklenmişlerse), içeriği tarayarak ilk <img> etiketini bulun ve attachment_url_to_postid() işlevini kullanarak bu etiketin kimliğini çıkarın.
    3. Girişte hiç resim bulunmuyorsa, yapılandırmada belirtilen varsayılan (yedek) resim atanır.
  • AJAX toplu işleme (toplu işleme): Başlık kısmında (“⚡ Bu sayfadaki tümünü atama”) adlı bir düğme bulunur; bu düğme, ekranda görünen tüm girişleri ardışık asenkron istekler kullanarak sırayla işler. Ayrıca, listedeki her bir gönderi için ayrı ayrı bulunan düğmeleri kullanarak bunu manuel olarak da yapabilirsiniz.
  • Taslakları filtrele: Taslak durumundaki girişleri otomatik olarak hariç tutar; böylece yalnızca yayınlanmış içeriğe odaklanabilirsiniz.
  • Sıralanabilir sütun: Listeyi, öne çıkan resmi olmayan tüm girişlerin en üstte bir arada toplanacak şekilde sıralamanızı sağlar.

Neden bu kadar hafif ve bu durum sunucu performansını etkilemiyor mu?

Bu tür otomatik araçları kullanırken sıkça dile getirilen bir endişe, binlerce makale içeren sitelerde veritabanının kilitlenmesi veya PHP belleğinin aşırı yüklenmesi korkusudur. Bu kod parçacığı, aşağıdaki optimizasyon kriterlerine uygun olarak tasarlanmıştır:

1. Yalnızca Yönetici bölümünde çalışır (is_admin())

Bu kod, yalnızca yönetim panelinin giriş ekranı (edit.php) için hazırlanmıştır. Genel sitedeki isteklerde tek bir satırlık ek yük bile oluşturmaz. Kullanıcılarınız ve hız ölçümleriniz hiçbir şekilde etkilenmeyecektir.

2. Sorguları Meta Önbelleğine yönlendirin

Bir gönderinin resim içerip içermediğini belirlemek için, bu kod parçacığı her satır için karmaşık veritabanı aramaları yapmaz. Bunun yerine, WordPress’in listeyi oluştururken meta veri önbelleği aracılığıyla doğrudan belleğe yüklediği _thumbnail_id meta veri anahtarını kullanır. Tablo neredeyse anında yüklenir.

3. Basamaklı asenkron işleme (sıralı AJAX döngüsü)

Veritabanında tek seferde 700 veya 1.000 kaydı güncellemek için devasa bir sorgu çalıştırmak yerine (ki bu, zaman aşımına neden olur veya sunucunun tüm belleğini tüketir), “Hepsini Atama” düğmesi, JavaScript vaatlerini kullanarak kayıtları tek tek işler:

POST 1 (AJAX)⟶"OK" yanıtı⟶POST 2 (AJAX)⟶"OK" yanıtı…

Bu, kaynakların istikrarlı ve tutarlı bir şekilde kullanılmasını sağlar ve ilerlemeyi satır satır, gerçek zamanlı olarak takip etmenize olanak tanır.

4. Dosya tekrarı yok

Bu kod parçacığı, sabit diskinizdeki resimleri indirmez, yeniden birleştirmez veya kopyalamaz. Yalnızca Medya Kitaplığınızda zaten bulunan resmin kimliğini alır ve wp_postmeta tablosunda ilgili meta_value değerini atayarak bu resmi yazıya bağlar.

Kod (PHP + JavaScript)

Bu kodu, alt temanızın functions.php dosyasına, özel bir eklentiye ya da Code Snippets veya benzeri bir özel işlev eklentisi aracılığıyla ekleyebilirsiniz.

Not: 56. satırdaki $default_image_url değişkenini, metinlerinde resim bulunmayan yazılara atamak istediğiniz genel resmin tam URL’sine ayarlamayı unutmayın.

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
}

Nasıl kullanılır?

  • Yönetici panelinizdeki “Yayınlar” listesine gidin.
  • En üstteki “Görüntüleme Seçenekleri” sekmesini açın ve sayfa başına görüntülenecek öğe sayısını 50 veya 100 olarak değiştirin. (Önce daha küçük sayılarla başlayın ve sunucunuz iyi yanıt veriyorsa bu sayıları kademeli olarak artırın.)

WordPress’te eksik öne çıkan resimleri toplu olarak atama. Görüntüleme seçenekleri

  • "Eksik Önemli Noktalar" sütun başlığına tıklayarak, henüz tamamlanmamış girişleri sıralayabilir ve gruplandırabilirsiniz. Filtrelerde "İçerir", "Eksik" veya "Tümü" seçeneklerinden birini seçebilirsiniz.
  • Eksik öne çıkan resimleri toplu olarak atamaya hazır olduğunuzdan emin olduğunuzda, “Bu sayfadaki tümünü atama” etiketli mavi düğmesine tıklayın ve basamaklı işlemin o toplu iş için küçük resimleri atamayı tamamlamasını bekleyin. İsterseniz, bunları tek tek de atayabilirsiniz.
  • Bir sonraki sayfaya geçin ve kitaplığınızı düzenlemeyi bitirene kadar bu işlemi tekrarlayın.

İsteğe bağlı: otomatik atama kod parçacığı

Gelecekte bu işlemi tekrarlamak zorunda kalmamak için bu kısa kod parçasını ekleyebilirsiniz. Bu kod, yalnızca bir yazıyı kaydettiğinizde veya yayınladığınızda arka planda çalışır: Öne çıkan bir resim seçmediğinizi algılarsa, içeriğin içindeki ilk resmi (veya $default_image_url = içinde eklemeniz gereken varsayılan yedek resmi) otomatik olarak atar.

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 );
        }
    }
}

Yorum yapın

Söyleyeceğin bir şey var mı?

Este blog se aloja en LucusHost

LucusHost, el mejor hosting