Masowe przypisywanie brakujących obrazów wyróżniających w WordPressie

No comments

05.09.2026|

No comments

Tiempo de lectura Lectura: 13 min, 19 s
Número de palabras Palabras: 2466
Número de visitas Visitas: 11
Icono de traducción
Zbiorcze przypisywanie brakujących obrazów wyróżnionych w WordPressie. Lista zawierająca zarówno brakujące, jak i istniejące obrazy.

Kilka dni temu zakończyłem realizację zadania, które od dawna wymagało wykonania, ale odkładałem je na później.Uzupełniłem wszystkiebrakujące zdjęcia w wyróżnieniu oraz dodałem brakujące teksty alternatywne do wszystkich obrazów. Ukończyłem również pisanie wszystkich brakujących opisów meta. W sumie dodałem lub edytowałem ponad 15 000 wpisów, korzystając z trzech własnych , jednorazowych skryptów. Dzisiaj udostępniam tutaj skrypt służący do odzyskiwania brakujących obrazów wyróżniających, którego użyłem również do wykonania tego zadania na innych stronach.

Jeśli blogujesz już od jakiegoś czasu, jest całkiem prawdopodobne, że masz sporo starszych wpisów, do których nie przypisano obrazu wyróżniającego. Często zdarzało się to w czasach klasycznego edytora, kiedy to wstawialiśmy obrazek bezpośrednio do treści wpisu, ale zapominaliśmy zaznaczyć go w polu „Obrazek wyróżniający”.

Publikowanie wpisów bez obrazu wyróżniającego to nie tylko kwestia estetyki – ma to bezpośredni wpływ na optymalizację Twojej witryny oraz komfort użytkowania:

  1. SEO w mediach społecznościowych i Open Graph: Podczas udostępniania wpisu w mediach społecznościowych, aplikacjach do przesyłania wiadomości lub serwisach agregujących treści tagi Open Graph (og:image) nie mogą znaleźć jednoznacznego zasobu do wyświetlenia, co znacznie obniża CTR (współczynnik klikalności).
  2. Spójność wizualna motywu: Nowoczesne szablony (takie jak te zaprojektowane przy użyciu GeneratePress lub GenerateBlocks) wykorzystują obraz wyróżniony do tworzenia siatek archiwum, list powiązanych wpisów lub nagłówków. Bez niego układ ulega zniekształceniu lub pojawiają się w nim nierównomiernie rozmieszczone odstępy.
  3. Wyszukiwanie i kanały (RSS / Google Discover): Narzędzia takie jak Google Discover czy czytniki RSS nadają priorytet treściom opatrzonym miniaturką w wysokiej rozdzielczości lub wymagają jej obecności, aby uatrakcyjnić wygląd karty artykułu.

Aby rozwiązać ten problem bez uciekania się do wtyczek (takich jak ta) lub powolnych procesów, stworzyłem ten prosty, lekki fragment kodu PHP, który automatycznie ustawia pierwszy obrazek w każdym wpisie jako obrazek wyróżniony – bezpośrednio z panelu administracyjnego WordPressa. Jeśli wpis nie zawiera żadnych obrazków, można przypisać obrazek domyślny, dodając jego adres URL do kodu.

Co dokładnie robi ten fragment kodu?

Kod ten dodaje do listy wpisów w WordPressie (wp-admin/edit.php) inteligentne, wbudowane narzędzie do przetwarzania zbiorczego:

  • Dodaj kolumnę niestandardową: Utwórz kolumnę „Brak obrazuwyróżniającego na liście wpisów, aby od razu widzieć, w których wpisach brakuje miniatury.
  • Przetwarzanie w trzech etapach: Po włączeniu przypisania dla wpisu system wyszukuje obraz dokładnie w następującej kolejności:
    1. Sprawdź, czy w bibliotece multimediów znajdują się jakieś zdjęcia dołączone bezpośrednio do wpisu.
    2. Jeśli nie ma żadnych bezpośrednich załączników (na przykład jeśli zostały one wstawione za pomocą bloku obrazu lub kodu HTML), należy przeszukać treść w poszukiwaniu pierwszego tagu <img>, aby wyodrębnić jego identyfikator za pomocą funkcji attachment_url_to_postid().
    3. Jeśli wpis nie zawiera absolutnie żadnych obrazów, system przypisuje obraz domyślny (zapasowy) określony w konfiguracji.
  • Przetwarzanie zbiorcze AJAX (przetwarzanie partiami): W nagłówku znajduje się przycisk („⚡ Przypisz wszystkie na tej stronie”), który przetwarza kolejno wszystkie wpisy widoczne na ekranie za pomocą kolejnych żądań asynchronicznych. Można to również zrobić ręcznie, korzystając z osobnego przycisku przy każdym wpisie na liście.
  • Filtruj wersje robocze: automatycznie pomija wpisy o statusie „wersja robocza”, dzięki czemu możesz skupić się wyłącznie na opublikowanych treściach.
  • Kolumna z opcją sortowania: umożliwia posortowanie listy tak, aby wszystkie wpisy bez obrazka wyróżnionego zostały zgrupowane razem na górze.

Dlaczego jest tak lekki i czy nie wpływa to na wydajność serwera?

Częstym powodem do obaw przy korzystaniu z tego typu zautomatyzowanych narzędzi jest obawa przed zablokowaniem bazy danych lub przeciążeniem pamięci PHP na stronach zawierających tysiące artykułów. Ten fragment kodu został zaprojektowany zgodnie z następującymi kryteriami optymalizacji:

1. Działa wyłącznie w sekcji administracyjnej (is_admin())

Kod jest przeznaczony wyłącznie dla ekranu wprowadzania danych w panelu administracyjnym (edit.php). Nie powoduje on dodania ani jednej linii kodu obciążającego żądania kierowane do publicznej części witryny. Nie wpłynie to w żaden sposób na użytkowników ani na wskaźniki szybkości działania.

2. Kieruj zapytania do pamięci podręcznej Meta Cache

Aby ustalić, czy dany wpis zawiera obraz, fragment kodu nie przeprowadza skomplikowanych wyszukiwań w bazie danych dla każdego wiersza. Wykorzystuje on klucz metadanych _thumbnail_id, który WordPress ładuje natywnie do pamięci za pośrednictwem pamięci podręcznej metadanych podczas renderowania listy. Tabela ładuje się niemal natychmiast.

3. Kaskadowe przetwarzanie asynchroniczne (sekwencyjna pętla AJAX)

Zamiast uruchamiać rozbudowane zapytanie do bazy danych w celu zaktualizowania 700 lub 1000 wpisów za jednym razem (co mogłoby spowodować przekroczenie limitu czasu lub wyczerpanie całej pamięci serwera), przycisk „Przypisz wszystko” przetwarza wpisy pojedynczo, wykorzystując obietnice JavaScript:

POST 1 (AJAX) ⟶ Odpowiedź „OK” ⟶ POST 2 (AJAX) ⟶ Odpowiedź „OK”…

Zapewnia to stabilne i spójne wykorzystanie zasobów, umożliwiając śledzenie postępów w czasie rzeczywistym, wiersz po wierszu.

4. Brak zduplikowanych plików

Skrypt ten nie pobiera, nie łączy ani nie powiela obrazów z dysku twardego. Po prostu pobiera identyfikator obrazu znajdującego się już w bibliotece multimediów i powiązuje go z wpisem, przypisując odpowiednią wartość meta_value w tabeli wp_postmeta.

Kod (PHP + JavaScript)

Możesz dodać ten kod do pliku functions.php swojego motywu potomnego, do własnej wtyczki lub za pomocą wtyczki zawierającej niestandardowe funkcje, takiej jak Code Snippets lub podobnej.

Uwaga: Pamiętaj, aby w wierszu 56 ustawić zmienną $default_image_url na pełny adres URL obrazu domyślnego, który chcesz przypisać do wpisów niezawierających żadnych obrazów w tekście.

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
}

Jak się tego używa?

  • Przejdź do listy „Wpisy” w panelu administracyjnym.
  • Otwórz zakładkę „Opcje wyświetlania” u góry i zmień liczbę wyświetlanych elementów na 50 lub 100 na stronę. (Zacznij od mniejszych wartości i stopniowo je zwiększaj, jeśli serwer dobrze na to reaguje.)

Masowe przypisywanie brakujących obrazów wyróżnionych w WordPressie. Opcje wyświetlania

  • Kliknij nagłówek kolumny „Brakujące zaznaczenia”, aby posortować i pogrupować zaległe wpisy. W filtrach możesz wybrać opcję „Zawiera”, „Brak” lub „Wszystkie”
  • Gdy upewnisz się, że jesteś gotowy do rozpoczęcia przypisywania brakujących obrazów wyróżnionych partiami, kliknij niebieski przycisk z napisem „Przypisz wszystkie na tej stronie” i poczekaj, aż proces kaskadowy zakończy przypisywanie miniatur dla tej partii. Jeśli wolisz, możesz przypisywać je pojedynczo.
  • Przejdź do następnej strony i powtarzaj tę czynność, aż zakończysz porządkowanie biblioteki.

Opcjonalnie: fragment kodu do automatycznego przypisywania

Aby uniknąć konieczności powtarzania tej czynności w przyszłości, możesz dodać ten krótki fragment kodu. Działa on w tle tylko wtedy, gdy zapisujesz lub publikujesz wpis: jeśli wykryje, że nie wybrałeś ręcznie obrazu wyróżnionego, automatycznie przypisuje pierwszy obraz z treści (lub domyślny obraz zastępczy, który musisz dodać w $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 );
        }
    }
}

Dodaj komentarz

Coś do powiedzenia?

Este blog se aloja en LucusHost

LucusHost, el mejor hosting