Hromadné přiřazení chybějících hlavních obrázků ve WordPressu

Žádné komentáře

05.09.2026|

Žádné komentáře

Tiempo de lectura Lectura: 14 min, 24 s
Número de palabras Palabras: 2667
Número de visitas Visitas: 22
Icono de traducción
Hromadné přiřazení chybějících úvodních obrázků ve WordPressu. Seznam zobrazující jak chybějící, tak existující obrázky.

Před pár dny jsem dokončil úkol, který už dávno měl být hotový, ale odkládal jsem ho už věky.Doplnil jsem všechnychybějící úvodní obrázky a ke všem obrázkům jsem přidal chybějící alternativní text. Také jsem dopsal všechny chybějící meta popisy. Celkem jsem pomocí tří vlastních jednorázových skriptů přidal nebo upravil přes 15 000 záznamů. Dnes zde sdílím skript pro načtení chybějících hlavních obrázků, který jsem použil i k provedení tohoto úkolu na jiných webech.

Pokud už nějakou dobu blogujete, je docela pravděpodobné, že máte spoustu starších příspěvků, u nichž není přiřazen žádný hlavní obrázek. K tomu docházelo často v dobách klasického editoru, kdy jsme obrázek vkládali přímo do textu příspěvku, ale zapomněli jsme jej vybrat v poli „Hlavní obrázek“.

Zveřejňování příspěvků bez úvodního obrázku není jen otázkou estetiky, ale má mimo jiné i přímý vliv na optimalizaci vašeho webu a uživatelský zážitek:

  1. Sociální SEO a Open Graph: Při sdílení příspěvku na sociálních sítích, v aplikacích pro zasílání zpráv nebo na agregátorech obsahu nemohou tagy Open Graph (og:image) najít jasný zdroj, který by se měl zobrazit, což výrazně snižuje CTR (míru prokliku).
  2. Vizuální jednotnost šablony: Moderní šablony (jako jsou ty vytvořené pomocí GeneratePress nebo GenerateBlocks) využívají hlavní obrázek k vytváření mřížek archivů, seznamů souvisejících příspěvků nebo záhlaví. Bez něj dojde k narušení rozvržení nebo k vzniku nesouměrných mezer.
  3. Objevování a kanály (RSS / Google Discover): Nástroje jako Google Discover nebo čtečky RSS upřednostňují nebo vyžadují obsah s připojenou miniaturou ve vysokém rozlišení, aby tak vylepšily zobrazení karty článku.

Abych tento problém vyřešil bez nutnosti používat pluginy (jako je například tento) nebo pomalé procesy, vytvořil jsem tento jednoduchý a nenáročný úryvek kódu v PHP, který automaticky nastaví první obrázek v každém příspěvku jako hlavní obrázek, a to přímo z ovládacího panelu WordPressu. Pokud váš příspěvek neobsahuje žádné obrázky, můžete mu přiřadit výchozí obrázek tak, že do kódu přidáte jeho URL.

Co přesně tento úryvek kódu dělá?

Tento kód přidává do seznamu příspěvků WordPressu (wp-admin/edit.php) chytrý vestavěný nástroj pro hromadné zpracování:

  • Přidání vlastního sloupce: V seznamu příspěvků vytvořte sloupec „Chybějící hlavní obrázek“, abyste na první pohled viděli, u kterých příspěvků chybí miniatura.
  • Třífázové zpracování: Když u příspěvku povolíte přiřazení, systém vyhledá obrázek přesně v tomto pořadí:
    1. Zkontrolujte, zda jsou v knihovně médií nějaké obrázky připojené přímo k příspěvku.
    2. Pokud neexistují žádné přímé přílohy (například pokud byly vloženy prostřednictvím obrazového bloku nebo kódu HTML), prohledejte obsah a najděte první značku <img>, abyste pomocí funkce attachment_url_to_postid() získali její ID.
    3. Pokud záznam neobsahuje vůbec žádné obrázky, přiřadí se mu výchozí (náhradní) obrázek určený v konfiguraci.
  • Hromadné zpracování pomocí AJAXu (dávkové zpracování): V záhlaví se nachází tlačítko („⚡ Přiřadit vše na této stránce“), které postupně zpracovává všechny položky viditelné na obrazovce pomocí po sobě jdoucích asynchronních požadavků. Tuto akci můžete provést i ručně pomocí samostatného tlačítka u každého příspěvku v seznamu.
  • Filtr konceptů: Automaticky vylučuje položky ve stavu „koncept“, abyste se mohli soustředit výhradně na publikovaný obsah.
  • Sloupec pro třídění: Umožňuje seřadit seznam tak, aby se všechny položky bez vybraného obrázku seskupily dohromady v horní části.

Proč je to tak lehké a neovlivňuje to výkon serveru?

Častým problémem při používání tohoto typu automatizovaných nástrojů je obava z zablokování databáze nebo přetížení paměti PHP na webech s tisíci článků. Tento kód byl navržen v souladu s následujícími optimalizačními kritérii:

1. Funguje pouze v sekci pro správce (is_admin())

Tento kód je určen výhradně pro vstupní obrazovku administračního panelu (edit.php). Nepřidává ani jediný řádek kódu k požadavkům na veřejném webu. Na vaše uživatele ani na ukazatele rychlosti to nebude mít žádný vliv.

2. Směřujte dotazy do mezipaměti Meta

Aby bylo možné zjistit, zda příspěvek obsahuje obrázek, tento úryvek neprovádí u každého řádku složité vyhledávání v databázi. Využívá klíč metadat _thumbnail_id, který WordPress při vykreslování seznamu nativně načítá do paměti prostřednictvím mezipaměti metadat. Tabulka se načte téměř okamžitě.

3. Kaskádové asynchronní zpracování (sekvenční smyčka AJAX)

Místo toho, aby se v databázi spustil rozsáhlý dotaz k aktualizaci 700 nebo 1 000 záznamů najednou (což by vedlo k vypršení časového limitu nebo k vyčerpání celé paměti serveru), tlačítko „Přiřadit vše“ zpracovává záznamy jeden po druhém pomocí slibů JavaScriptu:

POST 1 (AJAX)⟶Odpověď OK⟶POST 2 (AJAX)⟶Odpověď OK…

Tím je zajištěno stabilní a konzistentní využívání zdrojů, což vám umožňuje sledovat průběh v reálném čase, řádek po řádku.

4. Žádné duplicitní soubory

Tento kód nestahuje, neslučuje ani nekopíruje obrázky na váš pevný disk. Pouze převezme ID obrázku, který se již nachází ve vaší knihovně médií, a propojí jej s příspěvkem tak, že v tabulce wp_postmeta přiřadí odpovídající hodnotu meta_value.

Kód (PHP + JavaScript)

Tento kód můžete přidat do souboru functions.php svého podřízeného motivu, do vlastního pluginu nebo prostřednictvím pluginu s vlastními funkcemi, jako je například Code Snippets či podobný.

Poznámka: Nezapomeňte v řádku 56 nastavit proměnnou $default_image_url na úplnou URL adresu obecného obrázku, který chcete přiřadit k příspěvkům, jejichž text neobsahuje žádné obrázky.

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 se to používá?

  • Přejděte do seznamu „Příspěvky“ ve vašem administračním panelu.
  • Otevřete záložku „Možnosti zobrazení“ v horní části a změňte počet položek na stránce na 50 nebo 100. (Začněte s menšími čísly a postupně je zvyšujte, pokud server reaguje dobře.)

Hromadné přiřazení chybějících hlavních obrázků ve WordPressu. Možnosti zobrazení

  • Kliknutím na záhlaví sloupce „Chybějící zvýraznění“ můžete seřadit a seskupit dosud nevyřešené položky. Ve filtrech si můžete vybrat mezi možnostmi „Obsahuje“, „Chybí“ nebo „Vše“.
  • Jakmile si budete jisti, že jste připraveni začít hromadně přiřazovat chybějící hlavní obrázky, klikněte na modré tlačítko s nápisem „Assign all on this page“ a nechte kaskádový proces dokončit přiřazování miniatur pro danou dávku. Pokud chcete, můžete je přiřazovat i po jednom.
  • Přejděte na další stránku a postup opakujte, dokud nedokončíte úklid své knihovny.

Volitelné: úryvek pro automatické přiřazení

Abyste se v budoucnu vyhnuli opakování tohoto úkonu, můžete přidat tento krátký kód. Spustí se na pozadí pouze při uložení nebo zveřejnění příspěvku: pokud zjistí, že jste ručně nevybrali hlavní obrázek, automaticky přiřadí první obrázek v obsahu (nebo výchozí náhradní obrázek, který budete muset přidat do $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 );
        }
    }
}

Související články

Napsat komentář

Chcete k tomu něco říct?

Este blog se aloja en LucusHost

LucusHost, el mejor hosting