WordPress’te Otomatik Taslak Temizleme: functions.php ile Veritabanı Bakımı

WordPress sitenizin veritabanı zamanla şişmeye başlar ve bunun en büyük suçlularından biri taslak yazılardır. Bir içerik ekibiyle çalışıyorsanız, sık sık yazı oluşturup yarıda bırakıyorsanız ya da WooCommerce kullanan bir e-ticaret siteniz varsa, veritabanınızda binlerce gereksiz taslak birikmiş olabilir. Bu durum sorgu sürelerini uzatır, yedek dosyalarını şişirir ve genel site performansını olumsuz etkiler. functions.php dosyasına ekleyeceğiniz birkaç fonksiyonla bu işlemi tamamen otomatize edebilirsiniz.

Neden Taslak Temizliği Bu Kadar Önemli

WordPress, her kaydettiğinizde bir otomatik kayıt oluşturur. Bunun üstüne bir de revizyon sistemi var. Bir yazıyı taslak olarak bıraktığınızda wp_posts tablosunda post_status = 'draft' olarak işaretlenir. auto-draft durumundaki kayıtlar ise WordPress’in henüz siz bir şey yazmadan arka planda oluşturduğu geçici taslaklar.

Küçük bir blog için bu sorun gibi görünmeyebilir. Ama 5 yıllık bir WooCommerce mağazanız varsa ve yüzlerce ürünü yarıda bıraktıysanız, wp_posts tablonuzda on binlerce gereksiz kayıt olabilir. Bunu gördüğümde ilk refleksim her zaman veritabanını incelemek olur:

mysql -u root -p wordpress_db -e "SELECT post_status, COUNT(*) as sayi FROM wp_posts GROUP BY post_status ORDER BY sayi DESC;"

Bu sorgu size hangi durumda kaç kayıt olduğunu gösterir. draft ve auto-draft sayıları binleri geçiyorsa temizlik vakti gelmiş demektir.

functions.php’ye Girmeden Önce

Doğrudan konuya geçmeden önce bir uyarı: functions.php dosyasını düzenlemeden önce her zaman yedek alın. Bir hata site genelinde sorunlara yol açabilir. Child theme kullanıyorsanız değişikliklerinizi oraya yapın. Kullanmıyorsanız en azından aşağıdaki gibi bir yedek alın:

cp /var/www/html/wp-content/themes/aktif-tema/functions.php /var/www/html/wp-content/themes/aktif-tema/functions.php.yedek

Ayrıca veritabanını da yedekleyin:

mysqldump -u wordpress_user -p wordpress_db wp_posts wp_postmeta > /tmp/wp_posts_yedek_$(date +%Y%m%d).sql

Sadece wp_posts ve wp_postmeta tablolarını alıyoruz çünkü taslak temizliği bu iki tabloyu etkiler.

Temel Taslak Temizleme Fonksiyonu

En basit haliyle, belirli bir süreden eski taslakları silen bir fonksiyon yazalım. Bu fonksiyonu WordPress’in cron sistemiyle birleştireceğiz.

function otomatik_taslak_temizle() {
    global $wpdb;
    
    // 30 günden eski taslakları sil
    $limit_tarih = date('Y-m-d H:i:s', strtotime('-30 days'));
    
    // Önce silinecek post ID'lerini alalım
    $taslak_idler = $wpdb->get_col(
        $wpdb->prepare(
            "SELECT ID FROM {$wpdb->posts} 
             WHERE post_status IN ('draft', 'auto-draft') 
             AND post_modified < %s
             LIMIT 100",
            $limit_tarih
        )
    );
    
    if ( empty( $taslak_idler ) ) {
        return;
    }
    
    foreach ( $taslak_idler as $post_id ) {
        wp_delete_post( $post_id, true ); // true = çöp kutusuna taşımadan direkt sil
    }
    
    // Temizlik logla
    error_log( count( $taslak_idler ) . ' adet eski taslak silindi. Tarih: ' . date('Y-m-d H:i:s') );
}

Burada wp_delete_post() fonksiyonunu kullanıyoruz çünkü sadece wp_posts tablosunu değil, ilgili wp_postmeta kayıtlarını, yorum ilişkilerini ve term ilişkilerini de temizler. Ham SQL silme işlemi yapmak yerine her zaman WordPress’in kendi fonksiyonlarını tercih edin.

WordPress Cron ile Zamanlama

Fonksiyonu yazdık ama bunu düzenli olarak çalıştırmamız gerekiyor. WordPress’in dahili cron sistemi olan WP-Cron bunun için biçilmiş kaftan:

function taslak_temizleme_zamanla() {
    if ( ! wp_next_scheduled( 'haftalik_taslak_temizleme' ) ) {
        wp_schedule_event( time(), 'weekly', 'haftalik_taslak_temizleme' );
    }
}
add_action( 'wp', 'taslak_temizleme_zamanla' );

add_action( 'haftalik_taslak_temizleme', 'otomatik_taslak_temizle' );

Ancak WP-Cron’un önemli bir sınırlaması var: sadece site ziyaret edildiğinde tetiklenir. Trafiği az olan bir site için bu güvenilir olmayabilir. Bu durumda gerçek bir cron job kullanmak daha mantıklı:

# Crontab'a ekleyin: crontab -e
0 3 * * 0 curl -s "https://siteniz.com/wp-cron.php?doing_wp_cron" > /dev/null 2>&1

Ya da daha temiz bir yaklaşım olarak wp-config.php dosyasına şunu ekleyip WP-Cron’u devre dışı bırakabilir ve sisteminizin kendi cron’uyla çalıştırabilirsiniz:

# wp-config.php'ye eklenecek satır
define('DISABLE_WP_CRON', true);

# Sunucu cron'u (crontab -e ile):
*/15 * * * * php /var/www/html/wp-cron.php > /dev/null 2>&1

WooCommerce Ürün Taslakları için Özel Fonksiyon

Standart yazı taslaklarından farklı olarak WooCommerce ürün taslakları çok daha fazla postmeta verisi taşır. Fiyat, stok, resimler, varyasyonlar… Bunların hepsinin doğru şekilde temizlenmesi gerekir. WooCommerce ürünleri için özelleştirilmiş bir yaklaşım:

function woocommerce_urun_taslaklarini_temizle() {
    // WooCommerce aktif değilse çalışma
    if ( ! class_exists( 'WooCommerce' ) ) {
        return;
    }
    
    $args = array(
        'post_type'      => 'product',
        'post_status'    => array( 'draft', 'auto-draft' ),
        'posts_per_page' => 50,
        'date_query'     => array(
            array(
                'column' => 'post_modified',
                'before' => '60 days ago',
            ),
        ),
        'fields'         => 'ids',
    );
    
    $urun_idleri = get_posts( $args );
    
    if ( empty( $urun_idleri ) ) {
        return 0;
    }
    
    $silinen_sayisi = 0;
    
    foreach ( $urun_idleri as $urun_id ) {
        // Varyasyonları olan ürünler için önce varyasyonları temizle
        $variyasyonlar = get_posts( array(
            'post_type'   => 'product_variation',
            'post_parent' => $urun_id,
            'fields'      => 'ids',
            'numberposts' => -1,
        ) );
        
        foreach ( $variyasyonlar as $variyasyon_id ) {
            wp_delete_post( $variyasyon_id, true );
        }
        
        if ( wp_delete_post( $urun_id, true ) ) {
            $silinen_sayisi++;
        }
    }
    
    return $silinen_sayisi;
}

// Haftada bir çalıştır
add_action( 'wc_urun_taslak_temizleme', 'woocommerce_urun_taslaklarini_temizle' );

function wc_taslak_temizleme_zamanla() {
    if ( ! wp_next_scheduled( 'wc_urun_taslak_temizleme' ) ) {
        wp_schedule_event( time(), 'weekly', 'wc_urun_taslak_temizleme' );
    }
}
add_action( 'wp', 'wc_taslak_temizleme_zamanla' );

Özel Gönderi Tipleri için Esnek Temizleyici

Bazı sitelerde yazı ve ürünlerin yanı sıra özel gönderi tipleri de olur. Etkinlikler, portföy çalışmaları, ilanlar gibi. Bunları tek tek ele almak yerine yapılandırılabilir bir temizleyici yazmak çok daha sürdürülebilir:

function esnek_taslak_temizleyici( $ayarlar = array() ) {
    $varsayilan = array(
        'post_types'     => array( 'post', 'page' ),
        'gun_limiti'     => 30,
        'batch_boyutu'   => 100,
        'log_etkin'      => true,
        'kuru_calistir'  => false, // true ise silmez, sadece sayar
    );
    
    $ayarlar = wp_parse_args( $ayarlar, $varsayilan );
    
    $args = array(
        'post_type'      => $ayarlar['post_types'],
        'post_status'    => array( 'draft', 'auto-draft' ),
        'posts_per_page' => $ayarlar['batch_boyutu'],
        'date_query'     => array(
            array(
                'column' => 'post_modified',
                'before' => $ayarlar['gun_limiti'] . ' days ago',
            ),
        ),
        'fields'         => 'ids',
        'no_found_rows'  => true, // Sayfalama için gereksiz sorguyu engelle
    );
    
    $post_idleri = get_posts( $args );
    
    if ( empty( $post_idleri ) ) {
        if ( $ayarlar['log_etkin'] ) {
            error_log( '[Taslak Temizleyici] Silinecek kayıt bulunamadı.' );
        }
        return 0;
    }
    
    $silinen = 0;
    
    if ( $ayarlar['kuru_calistir'] ) {
        if ( $ayarlar['log_etkin'] ) {
            error_log( '[Taslak Temizleyici] Kuru çalıştırma: ' . count( $post_idleri ) . ' kayıt silinecekti.' );
        }
        return count( $post_idleri );
    }
    
    foreach ( $post_idleri as $id ) {
        if ( wp_delete_post( $id, true ) ) {
            $silinen++;
        }
    }
    
    if ( $ayarlar['log_etkin'] ) {
        error_log( sprintf(
            '[Taslak Temizleyici] %d kayıt silindi. Post tipleri: %s. Tarih: %s',
            $silinen,
            implode( ', ', $ayarlar['post_types'] ),
            current_time( 'mysql' )
        ) );
    }
    
    return $silinen;
}

// Kullanım örneği:
function gunluk_taslak_temizligi() {
    // Yazılar için 30 gün
    esnek_taslak_temizleyici( array(
        'post_types' => array( 'post' ),
        'gun_limiti' => 30,
    ) );
    
    // Sayfalar için daha uzun süre tut (90 gün)
    esnek_taslak_temizleyici( array(
        'post_types' => array( 'page' ),
        'gun_limiti' => 90,
    ) );
    
    // Özel gönderi tipi
    esnek_taslak_temizleyici( array(
        'post_types' => array( 'etkinlik', 'ilan' ),
        'gun_limiti' => 14,
        'batch_boyutu' => 50,
    ) );
}
add_action( 'gunluk_otomatik_temizlik', 'gunluk_taslak_temizligi' );

Temizlik Öncesi Koşul Kontrolü

Gerçek dünya senaryolarında her zaman bazı taslakları korumak istersiniz. Örneğin belirli bir yazara ait taslaklar veya belirli bir kategorideki yazılar silinmemeli. Bunu bir filtre sistemiyle yönetebilirsiniz:

function korunan_taslak_mi( $post_id ) {
    $post = get_post( $post_id );
    
    if ( ! $post ) {
        return false;
    }
    
    // Yönetici yazılarını koru
    $yazar = get_userdata( $post->post_author );
    if ( $yazar && in_array( 'administrator', $yazar->roles ) ) {
        return true;
    }
    
    // "onemli" etiketine sahip taslakları koru
    $etiketler = wp_get_post_tags( $post_id, array( 'fields' => 'slugs' ) );
    if ( in_array( 'onemli', $etiketler ) ) {
        return true;
    }
    
    // Özel bir meta değeri varsa koru
    if ( get_post_meta( $post_id, '_taslak_koru', true ) === '1' ) {
        return true;
    }
    
    return false;
}

function akilli_taslak_temizleyici() {
    $args = array(
        'post_type'      => array( 'post', 'product' ),
        'post_status'    => array( 'draft', 'auto-draft' ),
        'posts_per_page' => 200,
        'date_query'     => array(
            array(
                'column' => 'post_modified',
                'before' => '45 days ago',
            ),
        ),
        'fields'         => 'ids',
        'no_found_rows'  => true,
    );
    
    $adaylar = get_posts( $args );
    $silinen = 0;
    $atlanan = 0;
    
    foreach ( $adaylar as $post_id ) {
        if ( korunan_taslak_mi( $post_id ) ) {
            $atlanan++;
            continue;
        }
        
        if ( wp_delete_post( $post_id, true ) ) {
            $silinen++;
        }
    }
    
    error_log( sprintf(
        '[Akıllı Temizleyici] Silinen: %d, Atlanan (korunan): %d',
        $silinen,
        $atlanan
    ) );
}

Temizlik Sonrası Veritabanı Optimizasyonu

Binlerce kayıt sildiğinizde MySQL tablosunda boş alanlar kalır. wp_posts tablosunu optimize etmek performans için önemlidir:

function taslak_temizlik_sonrasi_optimize() {
    global $wpdb;
    
    // Temizlikten sonra tabloları optimize et
    $tablolar = array(
        $wpdb->posts,
        $wpdb->postmeta,
        $wpdb->term_relationships,
    );
    
    foreach ( $tablolar as $tablo ) {
        $wpdb->query( "OPTIMIZE TABLE {$tablo}" );
    }
    
    // WordPress object cache'ini temizle
    wp_cache_flush();
    
    error_log( '[DB Optimizasyon] wp_posts, wp_postmeta ve term_relationships optimize edildi. ' . current_time( 'mysql' ) );
}

// Temizlik action'ına bağla
add_action( 'haftalik_taslak_temizleme', 'taslak_temizlik_sonrasi_optimize', 20 );

Bu fonksiyonu OPTIMIZE TABLE ile birleştirmek küçük ve orta ölçekli sitelerde oldukça etkilidir. Ancak çok büyük tablolarda (milyonlarca satır) OPTIMIZE TABLE uzun sürebilir ve tablonuzu kilitleyebilir. Böyle bir durumda bunu gece 3-4 gibi çalıştırmak veya pt-online-schema-change gibi araçlar kullanmak daha güvenlidir.

Komut satırından tablo boyutunu kontrol etmek için:

mysql -u root -p wordpress_db -e "
SELECT 
    table_name, 
    ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Boyut (MB)',
    table_rows AS 'Yaklaşık Satır Sayısı'
FROM information_schema.tables 
WHERE table_schema = 'wordpress_db' 
AND table_name IN ('wp_posts', 'wp_postmeta')
ORDER BY (data_length + index_length) DESC;"

Admin Paneline Temizlik Butonu Eklemek

Otomatik temizlik harika ama bazen elle tetiklemek istersiniz. WordPress admin paneline bir buton ekleyebilirsiniz:

// Admin araç çubuğuna hızlı temizlik butonu ekle
function admin_taslak_temizlik_menusu( $wp_admin_bar ) {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    
    $nonce = wp_create_nonce( 'elle_taslak_temizle' );
    
    $wp_admin_bar->add_node( array(
        'id'    => 'taslak-temizle',
        'title' => 'Taslakları Temizle',
        'href'  => admin_url( 'admin-post.php?action=elle_taslak_temizle&_wpnonce=' . $nonce ),
        'meta'  => array( 'title' => 'Eski taslakları hemen temizle' ),
    ) );
}
add_action( 'admin_bar_menu', 'admin_taslak_temizlik_menusu', 100 );

// Butona tıklandığında çalışacak handler
function elle_taslak_temizle_handler() {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( 'Yetkiniz yok.' );
    }
    
    check_admin_referer( 'elle_taslak_temizle' );
    
    $silinen = esnek_taslak_temizleyici( array(
        'post_types' => array( 'post', 'page', 'product' ),
        'gun_limiti' => 7, // Elle çalıştırmada 7 gün
        'batch_boyutu' => 500,
    ) );
    
    wp_redirect( admin_url( 'index.php?taslak_temizlendi=' . $silinen ) );
    exit;
}
add_action( 'admin_post_elle_taslak_temizle', 'elle_taslak_temizle_handler' );

// Admin dashboard'unda bildirim göster
function taslak_temizlik_bildirimi() {
    if ( ! isset( $_GET['taslak_temizlendi'] ) ) {
        return;
    }
    
    $sayi = intval( $_GET['taslak_temizlendi'] );
    echo '<div class="notice notice-success is-dismissible"><p><strong>' . $sayi . ' adet eski taslak başarıyla silindi.</strong></p></div>';
}
add_action( 'admin_notices', 'taslak_temizlik_bildirimi' );

Gerçek Dünya Senaryosu: Büyük WooCommerce Mağazası

Yakın zamanda 50.000’den fazla ürün kaydı olan bir WooCommerce mağazasında çalışırken şunu gördüm: wp_posts tablosu 2 GB’ı aşmıştı ve bunun %40’ı taslak ve auto-draft kayıtlarıydı. Anlık silme yapmak yerine toplu ve aşamalı bir yaklaşım benimsedik:

# Önce durumu analiz et
mysql -u root -p eticaret_db -e "
SELECT 
    post_status, 
    post_type, 
    COUNT(*) as adet,
    MIN(post_modified) as en_eski,
    MAX(post_modified) as en_yeni
FROM wp_posts 
GROUP BY post_status, post_type 
ORDER BY adet DESC
LIMIT 20;"

Bu sorgu bize hangi kombinasyonun en çok yer kapladığını gösterdi. product + auto-draft kombinasyonu 18.000 kayıtla açık ara öndeydi. PHP’den toplu silme yerine, bakım penceresi sırasında doğrudan MySQL’den temizlik yaptık ve ardından WordPress fonksiyonlarını devreye aldık:

# Bakım modu aç
wp maintenance-mode activate --path=/var/www/html

# 90 günden eski product auto-draft'ları sil (önce postmeta)
mysql -u root -p eticaret_db -e "
DELETE pm FROM wp_postmeta pm
INNER JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.post_status = 'auto-draft' 
AND p.post_type = 'product'
AND p.post_modified < DATE_SUB(NOW(), INTERVAL 90 DAY);"

# Sonra posts tablosundan sil
mysql -u root -p eticaret_db -e "
DELETE FROM wp_posts 
WHERE post_status = 'auto-draft' 
AND post_type = 'product'
AND post_modified < DATE_SUB(NOW(), INTERVAL 90 DAY);"

# Tabloları optimize et
mysql -u root -p eticaret_db -e "OPTIMIZE TABLE wp_posts, wp_postmeta;"

# Bakım modunu kapat
wp maintenance-mode deactivate --path=/var/www/html

Bu temizliğin ardından sayfa yükleme süreleri ortalama %23 iyileşti ve yedek boyutu 2 GB’dan 1.1 GB’a düştü.

Temizlik Loglarını İzlemek

Hangi fonksiyonların ne zaman çalıştığını ve kaç kayıt sildiğini takip etmek için basit bir loglama mekanizması kurabilirsiniz:

# WordPress hata logunu izle
tail -f /var/log/nginx/wordpress_error.log | grep "Taslak Temizleyici"

# Ya da wp-content/debug.log dosyasını izle
tail -f /var/www/html/wp-content/debug.log | grep "[Taslak"

wp-config.php dosyanızda debug loglama aktifse (define('WP_DEBUG_LOG', true);) tüm error_log() çıktıları wp-content/debug.log dosyasına düşer. Bunu düzenli olarak kontrol etmek iyi bir alışkanlık.

Önerilen Temizlik Takvimi

Sitenizin büyüklüğüne ve içerik üretim hızınıza göre farklı takvimler işe yarar:

  • Aktif blog/haber sitesi: Haftalık, 14 günden eski taslaklar
  • Kurumsal site: Aylık, 60 günden eski taslaklar
  • WooCommerce mağazası: Haftalık auto-draft, aylık draft ürünler
  • Çok yazarlı platform: Yazarlara önce bildirim gönder, 30 gün sonra sil

Bildirim göndermek için basit bir yaklaşım:

function taslak_sahiplerine_bildirim_gonder() {
    $sinir_tarih = date( 'Y-m-d', strtotime( '+7 days' ) ); // 7 gün sonra silinecekler
    $uyari_tarihi = date( 'Y-m-d H:i:s', strtotime( '-23 days' ) ); // 23 gün önce değiştirilmiş
    
    $taslaklar = get_posts( array(
        'post_type'      => 'post',
        'post_status'    => 'draft',
        'posts_per_page' => -1,
        'date_query'     => array(
            array(
                'column' => 'post_modified',
                'before' => $uyari_tarihi,
            ),
        ),
        'fields'         => 'ids',
    ) );
    
    $yazarlara_gonderildi = array();
    
    foreach ( $taslaklar as $post_id ) {
        $yazar_id = get_post_field( 'post_author', $post_id );
        
        if ( in_array( $yazar_id, $yazarlara_gonderildi ) ) {
            continue;
        }
        
        $yazar = get_userdata( $yazar_id );
        
        if ( $yazar ) {
            wp_mail(
                $yazar->user_email,
                '30 günlük taslaklar silinecek',
                sprintf(
                    'Merhaba %s, %s tarihinden önce değiştirilen taslak yazılarınız %s tarihinde otomatik olarak silinecek. Korumak istediğiniz taslakları lütfen güncelleyin.',
                    $yazar->display_name,
                    '23 gün önce',
                    $sinir_tarih
                )
            );
            
            $yazarlara_gonderildi[] = $yazar_id;
        }
    }
}
add_action( 'haftalik_taslak_temizleme', 'taslak_sahiplerine_bildirim_gonder', 5 );

Sonuç

Otomatik taslak temizliği, göz ardı edilmesi kolay ama uzun vadede ciddi fark yaratan bir veritabanı bakım rutinidir. functions.php dosyasına ekleyeceğiniz birkaç fonksiyon ve düzgün yapılandırılmış bir cron sistemiyle bu işi tamamen unutabilir ve sitenizin kendiliğinden temiz kalmasını sağlayabilirsiniz.

Önemli noktalara tekrar değinmek gerekirse:

  • Her zaman yedek alın, özellikle ilk kurulumda
  • wp_delete_post() kullanın, ham SQL ile silmekten kaçının
  • Büyük sitelerde toplu silme işlemlerini gece yapın
  • WooCommerce ürünleri için varyasyonları da temizlemeyi unutmayın
  • Temizlik sonrası OPTIMIZE TABLE çalıştırın
  • Log tutun, neyin silindiğini takip edin

Bu alışkanlığı bir kez oturttuğunuzda veritabanı boyutunuzun zamanla küçüldüğünü, yedek sürelerinin kısaldığını ve genel sorgu performansının iyileştiğini göreceksiniz. Küçük bir functions.php yatırımı için oldukça tatmin edici bir getiri.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir