กำหนดภาพเด่นที่ขาดไปแบบรวมใน WordPress

No comments

05.09.2026|

No comments

Tiempo de lectura Lectura: 7 min, 12 s
Número de palabras Palabras: 1335
Número de visitas Visitas: 22
Icono de traducción
กำหนดภาพเด่นที่ขาดไปแบบรวมใน WordPress รายการที่แสดงทั้งภาพที่ขาดและภาพที่มีอยู่

เมื่อไม่กี่วันก่อน ผมได้เสร็จสิ้นงานที่ค้างมานานแล้ว แต่ผมได้เลื่อนการทำให้ล่าช้ามานานมาก ผมได้กู้คืนภาพเด่นที่ขาดหายไปทั้งหมดและเพิ่มข้อความ altที่ขาดหายไปให้กับภาพเหล่านั้น นอกจากนี้ ผมยังเขียนคำอธิบายเมตาที่ขาดหายไปทั้งหมดเสร็จสิ้นแล้วด้วย รวมทั้งหมด ผมได้เพิ่มหรือแก้ไขข้อมูลมากกว่า15,000รายการ โดยใช้สแนปเพ็ตสามตัวที่ผมสร้างขึ้นเองแบบเฉพาะกิจ วันนี้ ผมจะแบ่งปันสแนปเพ็ตสำหรับกู้คืนภาพเด่นที่ขาดหายไปนี้ ซึ่งผมยังใช้มันเพื่อดำเนินการงานนี้บนเว็บไซต์อื่น ๆ ด้วย

หากคุณเขียนบล็อกมาได้สักพักแล้ว มีความเป็นไปได้สูงว่าคุณจะมีบทความเก่าหลายชิ้นที่ยังไม่มีภาพหลักถูกกำหนดไว้ เรื่องนี้เคยเกิดขึ้นบ่อยมากในสมัยที่ใช้ตัวแก้ไขแบบคลาสสิก (Classic Editor) เมื่อเราแทรกภาพโดยตรงลงในเนื้อหาของบทความ แต่ลืมเลือกภาพนั้นในช่อง ‘ภาพหลัก’

การเผยแพร่บทความโดยไม่มีภาพหลักไม่ใช่เพียงปัญหาด้านความสวยงามเท่านั้น แต่ยังส่งผลโดยตรงต่อการปรับแต่งเว็บไซต์และประสบการณ์ของผู้ใช้ด้วย:

  1. Social SEO และ Open Graph:เมื่อแชร์โพสต์บนโซเชียลมีเดีย แอปส่งข้อความ หรือแพลตฟอร์มรวมเนื้อหา แท็ก Open Graph (og:image) ไม่สามารถหาแหล่งข้อมูลที่ชัดเจนมาแสดงได้ ซึ่งทำให้อัตราการคลิกผ่าน (CTR) ลดลงอย่างรุนแรง
  2. ความสอดคล้องทางภาพในธีม:เทมเพลตสมัยใหม่ (เช่น เทมเพลตที่ออกแบบด้วย GeneratePress หรือ GenerateBlocks) ใช้ภาพเด่นเพื่อสร้างตารางอาร์ไคฟ์ รายการบทความที่เกี่ยวข้อง หรือส่วนหัว หากไม่มีภาพเด่นนี้ การจัดวางจะผิดเพี้ยนหรือเกิดช่องว่างที่ไม่ตรงแนว
  3. การค้นพบและฟีด (RSS / Google Discover):เครื่องมือต่าง ๆ เช่น Google Discover หรือโปรแกรมอ่าน RSS จะให้ความสำคัญหรือกำหนดให้เนื้อหาต้องมีภาพตัวอย่างความละเอียดสูงเพื่อปรับปรุงการแสดงผลของบัตรบทความ

เพื่อแก้ไขปัญหานี้โดยไม่ต้องใช้ปลั๊กอิน (เช่นปลั๊กอินนี้) หรือกระบวนการที่ช้า ผมได้สร้างโค้ด PHP ที่เรียบง่ายและเบานี้ซึ่งสามารถตั้งภาพแรกในทุกบทความให้เป็นภาพหลักได้โดยอัตโนมัติโดยตรงจากแผงควบคุม WordPress หากบทความของคุณไม่มีภาพใดเลย คุณสามารถกำหนดภาพเริ่มต้นได้โดยเพิ่ม URL ของภาพนั้นลงในโค้ด

ส่วนโค้ดนี้ทำงานอย่างไรกันแน่?

โค้ดนี้เพิ่มเครื่องมือประมวลผลแบบกลุ่มที่อัจฉริยะและมาพร้อมระบบเข้าไปในรายการบทความของ WordPress (wp-admin/edit.php):

  • เพิ่มคอลัมน์ที่กำหนดเอง:สร้างคอลัมน์‘Missing FeaturedImage’ในรายการโพสต์ เพื่อดูได้ทันทีว่าโพสต์ใดไม่มีภาพตัวอย่าง
  • กระบวนการ 3 ขั้นตอน:เมื่อคุณเปิดใช้งานการกำหนดภาพสำหรับโพสต์ ระบบจะค้นหาภาพตามลำดับนี้อย่างแม่นยำ:
    1. ตรวจสอบว่ามีภาพใดที่แนบโดยตรงกับโพสต์ในคลังสื่อหรือไม่
    2. หากไม่มีไฟล์แนบโดยตรง (เช่น หากไฟล์ถูกแทรกผ่านบล็อกภาพหรือ HTML) ให้สแกนเนื้อหาเพื่อค้นหาแท็ก<img>ตัวแรก แล้วใช้ฟังก์ชันattachment_url_to_postid() เพื่อดึง ID ของแท็กนั้น
    3. หากรายการดังกล่าวไม่มีรูปภาพเลย ระบบจะกำหนดรูปภาพเริ่มต้น (fallback) ที่ระบุไว้ในไฟล์การตั้งค่า
  • การประมวลผลแบบกลุ่ม (batch processing) ด้วย AJAX:มีปุ่มในส่วนหัว (“⚡กำหนดทั้งหมดในหน้านี้”) ที่ใช้คำขอแบบไม่พร้อมกันต่อเนื่องเพื่อประมวลผลข้อมูลทั้งหมดที่แสดงบนหน้าจอตามลำดับ คุณยังสามารถทำสิ่งนี้ด้วยตนเองได้โดยใช้ปุ่มแยกสำหรับแต่ละโพสต์ในรายการ
  • กรองบทความร่าง:ระบบจะกรองบทความที่อยู่ในสถานะร่างออกโดยอัตโนมัติ เพื่อให้คุณสามารถมุ่งเน้นไปที่เนื้อหาที่เผยแพร่แล้วเท่านั้น
  • คอลัมน์ที่สามารถจัดเรียงได้:ช่วยให้คุณสามารถจัดเรียงรายการให้รายการทั้งหมดที่ไม่มีภาพเด่นถูกรวมกันไว้ด้านบน

ทำไมมันจึงเบามากขนาดนี้ และมันไม่ส่งผลต่อประสิทธิภาพของเซิร์ฟเวอร์หรือไม่?

ความกังวลที่มักเกิดขึ้นเมื่อใช้เครื่องมืออัตโนมัติประเภทนี้ คือความกังวลว่าฐานข้อมูลจะหยุดทำงาน หรือความกังวลว่าหน่วยความจำ PHP จะถูกใช้จนเกินขีดจำกัด บนเว็บไซต์ที่มีบทความหลายพันบทความ สคริปต์นี้ได้รับการออกแบบให้สอดคล้องกับเกณฑ์การปรับปรุงประสิทธิภาพดังต่อไปนี้:

1. มันทำงานได้เฉพาะในส่วน Admin (is_admin())

โค้ดนี้ถูกจัดเตรียมไว้โดยเฉพาะสำหรับหน้าจอป้อนข้อมูลของแผงควบคุม (edit.php)และไม่เพิ่มภาระการทำงานแม้แต่บรรทัดเดียวให้กับคำขอที่ส่งไปยังเว็บไซต์สาธารณะผู้ใช้และตัวชี้วัดความเร็วของคุณจะไม่ได้รับผลกระทบใดๆ ทั้งสิ้น

2. ส่งคำสอบถามไปยัง Meta Cache

เพื่อตรวจสอบว่าโพสต์นั้นมีรูปภาพหรือไม่ สนิปเพตนี้ไม่ดำเนินการค้นหาในฐานข้อมูลอย่างซับซ้อนสำหรับแต่ละแถว แต่ใช้คีย์เมตาดาต้า_thumbnail_idซึ่ง WordPress จะโหลดเข้าสู่หน่วยความจำโดยอัตโนมัติผ่านแคชเมตาดาต้า เมื่อแสดงรายการ ตารางข้อมูลจึงโหลดได้เกือบทันที

3. การประมวลผลแบบไม่พร้อมกันแบบชั้น (วงจร AJAX แบบลำดับ)

แทนที่จะดำเนินการค้นหาข้อมูลขนาดใหญ่ในฐานข้อมูลเพื่ออัปเดตข้อมูล 700 หรือ 1,000 รายการในครั้งเดียว (ซึ่งอาจทำให้ระบบหมดเวลาหรือใช้หน่วยความจำของเซิร์ฟเวอร์จนหมด) ปุ่ม ‘Assign All’ จะประมวลผลข้อมูลทีละรายการโดยใช้ JavaScript promises:

POST 1 (AJAX)⟶ตอบกลับ OK⟶POST 2 (AJAX)⟶ตอบกลับ OK…

สิ่งนี้ช่วยให้การใช้ทรัพยากรเป็นไปอย่างเสถียรและสม่ำเสมอ ทำให้คุณสามารถติดตามความคืบหน้าได้แบบเรียลไทม์ แถวต่อแถว

4. ไม่มีการซ้ำไฟล์

สแนปช็อตนี้ไม่ดาวน์โหลด ไม่รวมใหม่ และไม่คัดลอกภาพบนฮาร์ดดิสก์ของคุณมัน เพียงแต่ใช้ ID ของภาพที่มีอยู่ในไลบรารีสื่อของคุณอยู่แล้ว และเชื่อมโยงภาพนั้นกับโพสต์ โดยการกำหนดค่า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
}

ใช้อย่างไร?

  • ไปสู่รายการ‘Posts’ในแผงควบคุมผู้ดูแลระบบของคุณ
  • เปิดแท็บ‘Display Options’ที่ด้านบน และปรับจำนวนรายการที่จะแสดงให้เหลือ50 หรือ 100 รายการต่อหน้า(ลองเริ่มด้วยจำนวนที่น้อยก่อน แล้วค่อยเพิ่มทีละน้อยหากเซิร์ฟเวอร์ทำงานได้ดี)

กำหนดภาพเด่นที่ขาดไปแบบรวมใน WordPress ตัวเลือกการแสดง

  • คลิกที่หัวคอลัมน์‘Missing Highlights’เพื่อจัดเรียงและจัดกลุ่มข้อมูลที่ยังไม่ได้บันทึก ในส่วนตัวกรอง คุณสามารถเลือกระหว่าง ‘Has’, ‘Missing’ หรือ ‘All’
  • เมื่อคุณมั่นใจแล้วว่าพร้อมที่จะเริ่มกำหนดภาพเด่นที่ขาดอยู่เป็นกลุ่ม ให้คลิกปุ่มสีฟ้า ที่มีข้อความ ‘Assign all on this page’และรอให้กระบวนการแบบต่อเนื่องเสร็จสิ้นการกำหนดภาพขนาดย่อสำหรับกลุ่มนั้น หากคุณต้องการ คุณสามารถกำหนดภาพเหล่านั้นทีละภาพได้
  • ไปหน้าถัดไปและทำซ้ำจนกว่าการจัดระเบียบห้องสมุดของคุณจะเสร็จสิ้น

ตัวเลือก: การกำหนดโค้ดตัวอย่างอัตโนมัติ

เพื่อหลีกเลี่ยงการต้องทำซ้ำงานนี้ในอนาคต คุณสามารถเพิ่มโค้ดสั้นๆ นี้ได้ โค้ดนี้จะทำงานในพื้นหลังเฉพาะเมื่อคุณบันทึกหรือเผยแพร่โพสต์: หากมันตรวจพบว่ายังไม่ได้เลือกภาพเด่นด้วยมือ มันจะกำหนดภาพแรกในเนื้อหาให้เป็นภาพเด่นโดยอัตโนมัติ (หรือภาพสำรองตามค่าเริ่มต้น ซึ่งคุณต้องเพิ่มใน$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 );
        }
    }
}

Leave a Comment

ฝากความเห็นไว้ที่นี่

Este blog se aloja en LucusHost

LucusHost, el mejor hosting