1. sayfa (Toplam 1 sayfa)

aynı yada benzeyen yazıları süzme

Gönderilme zamanı: Sal Şub 18, 2025 5:13 pm
gönderen muratca61
wordpress Murat klasörü içine bir uygulama yapalım yine Murat etiketi altında açılsın bu uygulama ile birbirine birbirine benzeyen yazıları süzelim. burada kullanıcı ince ayarı ile birbirine benzeyen yazılar tablo halinde sıralansın başlık kısmı ve bir miktar içerik yazısıda tabloda görünsün. düzenlemek için yeni sekme linki ve silmek için yol olsun.
50 saniye hakkında Akıl yürütüldü
Aşağıdaki örnek, WordPress içinde "Murat" adında bir admin menü öğesi altında açılacak, ayarlanabilir benzerlik eşiğine göre yazılar arasında benzerlik tespit edip, sonuçları tablo halinde listeleyen (başlık, içerik özet, düzenleme (yeni sekmede) ve silme bağlantıları bulunan) basit bir uygulamadır.

Not: Bu örnekte tüm yazılar üzerinden çift döngüyle benzerlik hesaplanmaktadır. Yazı sayınız çok fazla ise performans sorunları yaşayabilirsiniz. (Daha gelişmiş bir uygulamada veritabanı düzeyinde veya önbellekleme yöntemleriyle optimizasyon yapılabilir.)

Aşağıdaki kodu wp-content/plugins/Murat/

Kod:Tümünü seç

murat-similar-posts.php
şeklinde oluşturup, eklentiyi aktif edebilirsiniz:

Kod:Tümünü seç

<?php
/*
Plugin Name: Murat Benzer Yazılar Süzelgesi
Description: Yazılar arasında benzerlik tespit edip, benzer yazıları tablo halinde listeler. Her satırda yazı başlığı, içerik özetinin yanında; düzenleme (yeni sekmede) ve silme bağlantıları yer alır. 
Version: 1.0
Author: Murat
License: GPL2
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Murat_Similar_Posts {
    // Benzerlik eşiği ayarını saklamak için kullanılacak seçenek adı (varsayılan %50)
    private $option_name = 'murat_similar_threshold';

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'admin_init', array( $this, 'handle_actions' ) );
    }

    /**
     * "Murat" etiketi altında admin menü sayfasını ekler.
     */
    public function add_admin_menu() {
        add_menu_page(
            'Murat', // Sayfa başlığı
            'Murat', // Menü başlığı
            'manage_options',
            'murat-similar-posts',
            array( $this, 'admin_page' ),
            'dashicons-admin-post',
            25
        );
    }

    /**
     * Ayar güncelleme ve silme işlemlerini yönetir.
     */
    public function handle_actions() {
        // Ayar formu gönderilmişse: Benzerlik eşiğini güncelle
        if ( isset( $_POST['murat_similar_threshold_nonce'] ) && wp_verify_nonce( $_POST['murat_similar_threshold_nonce'], 'update_threshold' ) ) {
            $threshold = isset( $_POST['murat_similar_threshold'] ) ? intval( $_POST['murat_similar_threshold'] ) : 50;
            update_option( $this->option_name, $threshold );
            add_settings_error( 'murat_similar_messages', 'murat_similar_message', 'Ayarlar güncellendi.', 'updated' );
        }

        // GET üzerinden silme işlemi: ?action=delete_post&post_id=XX&murat_nonce=...
        if ( isset( $_GET['action'] ) && $_GET['action'] == 'delete_post' && isset( $_GET['post_id'] ) && isset( $_GET['murat_nonce'] ) ) {
            $post_id = intval( $_GET['post_id'] );
            if ( wp_verify_nonce( $_GET['murat_nonce'], 'delete_post_' . $post_id ) ) {
                wp_delete_post( $post_id, true );
                wp_redirect( admin_url( 'admin.php?page=murat-similar-posts' ) );
                exit;
            }
        }
    }

    /**
     * Yönetici sayfasını oluşturur.
     */
    public function admin_page() {
        // Ayar mesajlarını göster
        settings_errors( 'murat_similar_messages' );

        // Mevcut benzerlik eşiğini al (varsayılan %50)
        $threshold = get_option( $this->option_name, 50 );
        ?>
        <div class="wrap">
            <h1>Murat Benzer Yazılar Süzelgesi</h1>
            
            <!-- Ayar formu: Benzerlik eşiği ayarı -->
            <form method="post" action="">
                <?php wp_nonce_field( 'update_threshold', 'murat_similar_threshold_nonce' ); ?>
                <table class="form-table">
                    <tr>
                        <th scope="row">Benzerlik Eşiği (%)</th>
                        <td>
                            <input type="number" name="murat_similar_threshold" value="<?php echo esc_attr( $threshold ); ?>" min="0" max="100" />
                            <p class="description">0 ile 100 arasında bir değer girin. Örneğin, 70: %70 ve üzeri benzerlik tespit edilir.</p>
                        </td>
                    </tr>
                </table>
                <?php submit_button( 'Ayarları Kaydet' ); ?>
            </form>
            <hr>
            <h2>Benzer Yazılar</h2>
            <?php
            // Yayınlanmış tüm yazıları alıyoruz.
            $posts = get_posts( array(
                'numberposts' => -1,
                'post_status' => 'publish',
                'post_type'   => 'post'
            ) );

            if ( empty( $posts ) ) {
                echo "<p>Hiç yazı bulunamadı.</p>";
                return;
            }

            // Yazılar arasında çift döngüyle benzerlik hesaplıyoruz.
            $similar_pairs = array();
            $post_count = count( $posts );
            for ( $i = 0; $i < $post_count; $i++ ) {
                for ( $j = $i + 1; $j < $post_count; $j++ ) {
                    $sim = $this->compute_similarity( $posts[$i], $posts[$j] );
                    if ( $sim >= $threshold ) {
                        $similar_pairs[] = array(
                            'post1'      => $posts[$i],
                            'post2'      => $posts[$j],
                            'similarity' => $sim,
                        );
                    }
                }
            }

            if ( empty( $similar_pairs ) ) {
                echo "<p>Benzer yazı bulunamadı.</p>";
            } else {
                ?>
                <table class="widefat fixed striped">
                    <thead>
                        <tr>
                            <th>Yazı 1</th>
                            <th>Yazı 2</th>
                            <th>Benzerlik (%)</th>
                            <th>İşlemler</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ( $similar_pairs as $pair ) : ?>
                        <tr>
                            <td>
                                <strong><?php echo esc_html( $pair['post1']->post_title ); ?></strong><br>
                                <?php echo esc_html( wp_trim_words( $pair['post1']->post_content, 20, '...' ) ); ?>
                            </td>
                            <td>
                                <strong><?php echo esc_html( $pair['post2']->post_title ); ?></strong><br>
                                <?php echo esc_html( wp_trim_words( $pair['post2']->post_content, 20, '...' ) ); ?>
                            </td>
                            <td><?php echo round( $pair['similarity'], 2 ); ?>%</td>
                            <td>
                                <a href="<?php echo esc_url( get_edit_post_link( $pair['post1']->ID ) ); ?>" target="_blank">Düzenle 1</a> | 
                                <a href="<?php echo esc_url( get_edit_post_link( $pair['post2']->ID ) ); ?>" target="_blank">Düzenle 2</a><br>
                                <a href="<?php echo esc_url( $this->get_delete_link( $pair['post1']->ID ) ); ?>" onclick="return confirm('Bu yazıyı silmek istediğinize emin misiniz?');">Sil 1</a> | 
                                <a href="<?php echo esc_url( $this->get_delete_link( $pair['post2']->ID ) ); ?>" onclick="return confirm('Bu yazıyı silmek istediğinize emin misiniz?');">Sil 2</a>
                            </td>
                        </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
                <?php
            }
            ?>
        </div>
        <?php
    }

    /**
     * İki yazı arasında benzerlik oranını hesaplar.
     *
     * Bu örnekte, yazı başlıkları ve içeriklerinin benzerliğini %70-%30 oranında ağırlıklandırarak
     * ortalama benzerlik yüzdesini döndürür.
     *
     * @param WP_Post $post1
     * @param WP_Post $post2
     * @return float Benzerlik yüzdesi.
     */
    private function compute_similarity( $post1, $post2 ) {
        // Başlık benzerliği hesaplanıyor.
        similar_text( $post1->post_title, $post2->post_title, $title_sim );
        
        // İçerik benzerliğini, HTML etiketlerini kaldırarak hesaplıyoruz.
        $content1 = wp_strip_all_tags( $post1->post_content );
        $content2 = wp_strip_all_tags( $post2->post_content );
        similar_text( $content1, $content2, $content_sim );
        
        // Ağırlıklandırma: başlık %70, içerik %30
        $similarity = ( 0.7 * $title_sim ) + ( 0.3 * $content_sim );
        return $similarity;
    }

    /**
     * Belirtilen yazı ID'si için silme bağlantısı üretir (nonce ile korunur).
     *
     * @param int $post_id
     * @return string URL
     */
    private function get_delete_link( $post_id ) {
        $nonce = wp_create_nonce( 'delete_post_' . $post_id );
        $url = add_query_arg(
            array(
                'page'       => 'murat-similar-posts',
                'action'     => 'delete_post',
                'post_id'    => $post_id,
                'murat_nonce'=> $nonce,
            ),
            admin_url( 'admin.php' )
        );
        return $url;
    }
}

new Murat_Similar_Posts();
Nasıl Çalışır?
Admin Menü ve Ayarlar:

Eklenti, WordPress admin menüsünde "Murat" başlığı altında sayfa açar.
Üst kısımda, kullanıcı benzerlik eşiğini (0-100 arasında) ayarlayabilir. Ayar gönderildiğinde murat_similar_threshold seçeneği güncellenir.
Benzer Yazı Tespiti:

Yayınlanmış tüm yazılar alınır ve çift döngüyle her iki yazı arasındaki benzerlik, başlık ve içerik (HTML etiketleri temizlenmiş) üzerinden hesaplanır.
Ağırlıklandırmada başlığa %70, içeriğe %30 verilir. Eğer hesaplanan ortalama benzerlik, kullanıcı tarafından belirlenen eşik değerine eşit veya büyükse, bu yazı çifti listeye eklenir.
Tablo Görünümü:

Ekranda her satırda iki yazının başlıkları, içerik özetleri ve benzerlik yüzdesi gösterilir.
Her yazı için "Düzenle" bağlantısı (yeni sekmede açılır) ve "Sil" bağlantısı (nonce korumalı) yer alır.
Bu örnek, benzer yazıları tespit etme ve yönetim işlemleri için temel bir yapı sunar. İhtiyacınıza göre algoritmayı, ağırlıkları veya arayüzü daha da özelleştirebilirsiniz.

Re: aynı yada benzeyen yazıları süzme

Gönderilme zamanı: Sal Şub 18, 2025 5:21 pm
gönderen muratca61
uygulama uzun süre sorgu döndürdü ve sayfa çöktü önce admin ve ayar sayfası açılsın ve kullanıcı sorgulama yapsın her seferinde belirlenen adet kadar sonuç göstersin
1m 14s hakkında Akıl yürütüldü
Aşağıdaki örnekte, benzerlik sorgusunun ağır hesaplamalarını AJAX ile ayrı bir işlem olarak yapıp, sonuçları sayfalı (örneğin her seferinde 10 adet sonuç) gösterecek şekilde bir yapı kurguladık. Böylece, önce ayar sayfası açılır; kullanıcı ayarları (örneğin benzerlik eşiği) düzenler, ardından “Benzer Yazıları Sorgula” butonuna tıklayarak sorguyu tetikler. Sorgu, tüm yazılar arasında benzerlik hesaplamasını (örneğin transient ile 10 dakika cache’lenmiş) yapar ve sonuçları sayfalı olarak getirir.

Eklenti dosyası:
wp-content/plugins/Murat/

Kod:Tümünü seç

murat-similar-posts.php

Kod:Tümünü seç

<?php
/*
Plugin Name: Murat Benzer Yazılar Süzelgesi
Description: Yazılar arasında benzerlik tespit edip, benzer yazıları ayarlanabilir eşiğe göre sayfalı (örneğin, her sayfada 10 sonuç) listeleyen uygulama. Her satırda yazı başlığı, içerik özetinin yanında; düzenleme (yeni sekmede) ve silme linkleri yer alır.
Version: 1.0
Author: Murat
License: GPL2
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Murat_Similar_Posts {
    // Ayar için benzerlik eşiği (varsayılan %50)
    private $option_name = 'murat_similar_threshold';
    // Her sayfada gösterilecek sonuç sayısı
    private $results_per_page = 10;
    
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'admin_init', array( $this, 'handle_actions' ) );
        add_action( 'wp_ajax_murat_get_similar_posts', array( $this, 'ajax_get_similar_posts' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
    }
    
    /**
     * "Murat" etiketi altında admin menüsüne sayfa ekler.
     */
    public function add_admin_menu() {
        add_menu_page(
            'Murat',
            'Murat',
            'manage_options',
            'murat-similar-posts',
            array( $this, 'admin_page' ),
            'dashicons-admin-post',
            25
        );
    }
    
    /**
     * Admin sayfasına özel JavaScript ve CSS dosyalarını ekler.
     */
    public function enqueue_admin_scripts( $hook ) {
        if ( $hook == 'toplevel_page_murat-similar-posts' ) {
            wp_enqueue_script(
                'murat-admin-js',
                plugin_dir_url( __FILE__ ) . 'murat-admin.js',
                array( 'jquery' ),
                '1.0',
                true
            );
            wp_localize_script( 'murat-admin-js', 'murat_ajax', array(
                'ajax_url' => admin_url( 'admin-ajax.php' )
            ) );
        }
    }
    
    /**
     * Ayar güncelleme ve silme işlemlerini yönetir.
     */
    public function handle_actions() {
        // Ayar formu gönderildiyse: Benzerlik eşiğini güncelle
        if ( isset( $_POST['murat_similar_threshold_nonce'] ) && wp_verify_nonce( $_POST['murat_similar_threshold_nonce'], 'update_threshold' ) ) {
            $threshold = isset( $_POST['murat_similar_threshold'] ) ? intval( $_POST['murat_similar_threshold'] ) : 50;
            update_option( $this->option_name, $threshold );
            add_settings_error( 'murat_similar_messages', 'murat_similar_message', 'Ayarlar güncellendi.', 'updated' );
        }
        
        // GET üzerinden silme işlemi: ?action=delete_post&post_id=XX&murat_nonce=...
        if ( isset( $_GET['action'] ) && $_GET['action'] == 'delete_post' && isset( $_GET['post_id'] ) && isset( $_GET['murat_nonce'] ) ) {
            $post_id = intval( $_GET['post_id'] );
            if ( wp_verify_nonce( $_GET['murat_nonce'], 'delete_post_' . $post_id ) ) {
                wp_delete_post( $post_id, true );
                wp_redirect( admin_url( 'admin.php?page=murat-similar-posts' ) );
                exit;
            }
        }
    }
    
    /**
     * Yönetici sayfasını oluşturur.
     */
    public function admin_page() {
        // Ayar mesajlarını göster
        settings_errors( 'murat_similar_messages' );
        $threshold = get_option( $this->option_name, 50 );
        ?>
        <div class="wrap">
            <h1>Murat Benzer Yazılar Süzelgesi</h1>
            
            <!-- Ayar Formu -->
            <form method="post" action="">
                <?php wp_nonce_field( 'update_threshold', 'murat_similar_threshold_nonce' ); ?>
                <table class="form-table">
                    <tr>
                        <th scope="row">Benzerlik Eşiği (%)</th>
                        <td>
                            <input type="number" name="murat_similar_threshold" value="<?php echo esc_attr( $threshold ); ?>" min="0" max="100" />
                            <p class="description">0 ile 100 arasında bir değer. Örneğin, 70: %70 ve üzeri benzerlik tespit edilir.</p>
                        </td>
                    </tr>
                </table>
                <?php submit_button( 'Ayarları Kaydet' ); ?>
            </form>
            <hr>
            
            <!-- Sorgu Bölümü -->
            <h2>Benzer Yazılar Sorgulama</h2>
            <button id="murat-query-btn" class="button button-primary">Benzer Yazıları Sorgula</button>
            <div id="murat-results">
                <!-- AJAX sonuçları buraya yüklenecek -->
            </div>
            <div id="murat-pagination">
                <!-- AJAX ile pagination kontrolü buraya yüklenecek -->
            </div>
        </div>
        <?php
    }
    
    /**
     * AJAX ile benzer yazı sorgusunu çalıştırır.
     * Önce transient'den (10 dakika süreyle cache) sonuçlar okunur; yoksa tüm yayın yazılar üzerinde
     * çift döngüyle benzerlik hesaplanır.
     */
    public function ajax_get_similar_posts() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => 'Yetkisiz erişim.' ) );
        }
        
        $page = isset( $_POST['page'] ) ? intval( $_POST['page'] ) : 1;
        $threshold = get_option( $this->option_name, 50 );
        $results_per_page = $this->results_per_page;
        
        $transient_key = 'murat_similar_posts_data';
        $similar_posts = get_transient( $transient_key );
        if ( false === $similar_posts ) {
            $posts = get_posts( array(
                'numberposts' => -1,
                'post_status' => 'publish',
                'post_type'   => 'post'
            ) );
            $similar_posts = array();
            $n = count( $posts );
            // Çift döngüyle tüm yazılar arasında benzerlik hesaplama (n² işlem)
            for ( $i = 0; $i < $n; $i++ ) {
                for ( $j = $i + 1; $j < $n; $j++ ) {
                    $title_sim = 0;
                    similar_text( $posts[$i]->post_title, $posts[$j]->post_title, $title_sim );
                    
                    $content1 = wp_strip_all_tags( $posts[$i]->post_content );
                    $content2 = wp_strip_all_tags( $posts[$j]->post_content );
                    $content_sim = 0;
                    similar_text( $content1, $content2, $content_sim );
                    
                    // Ağırlıklandırma: başlık %70, içerik %30
                    $similarity = ( 0.7 * $title_sim ) + ( 0.3 * $content_sim );
                    if ( $similarity >= $threshold ) {
                        $similar_posts[] = array(
                            'post1'      => $posts[$i],
                            'post2'      => $posts[$j],
                            'similarity' => $similarity,
                        );
                    }
                }
            }
            // Sonuçları 10 dakika boyunca cache'liyoruz.
            set_transient( $transient_key, $similar_posts, 10 * MINUTE_IN_SECONDS );
        }
        
        // Toplam sonuç ve sayfa sayısı hesaplama
        $total_results = count( $similar_posts );
        $total_pages = ceil( $total_results / $results_per_page );
        $page = max( 1, min( $page, $total_pages ) );
        $offset = ( $page - 1 ) * $results_per_page;
        $paged_results = array_slice( $similar_posts, $offset, $results_per_page );
        
        // Tablo HTML'sini oluşturuyoruz.
        ob_start();
        if ( empty( $paged_results ) ) {
            echo "<p>Benzer yazı bulunamadı.</p>";
        } else {
            ?>
            <table class="widefat fixed striped">
                <thead>
                    <tr>
                        <th>Yazı 1</th>
                        <th>Yazı 2</th>
                        <th>Benzerlik (%)</th>
                        <th>İşlemler</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ( $paged_results as $pair ) : ?>
                    <tr>
                        <td>
                            <strong><?php echo esc_html( $pair['post1']->post_title ); ?></strong><br>
                            <?php echo esc_html( wp_trim_words( $pair['post1']->post_content, 20, '...' ) ); ?>
                        </td>
                        <td>
                            <strong><?php echo esc_html( $pair['post2']->post_title ); ?></strong><br>
                            <?php echo esc_html( wp_trim_words( $pair['post2']->post_content, 20, '...' ) ); ?>
                        </td>
                        <td><?php echo round( $pair['similarity'], 2 ); ?>%</td>
                        <td>
                            <a href="<?php echo esc_url( get_edit_post_link( $pair['post1']->ID ) ); ?>" target="_blank">Düzenle 1</a> | 
                            <a href="<?php echo esc_url( get_edit_post_link( $pair['post2']->ID ) ); ?>" target="_blank">Düzenle 2</a><br>
                            <a href="<?php echo esc_url( $this->get_delete_link( $pair['post1']->ID ) ); ?>" onclick="return confirm('Bu yazıyı silmek istediğinize emin misiniz?');">Sil 1</a> | 
                            <a href="<?php echo esc_url( $this->get_delete_link( $pair['post2']->ID ) ); ?>" onclick="return confirm('Bu yazıyı silmek istediğinize emin misiniz?');">Sil 2</a>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
            <?php
        }
        $html = ob_get_clean();
        
        // Pagination kontrolü
        ob_start();
        if ( $total_pages > 1 ) {
            echo '<div class="tablenav"><div class="tablenav-pages">';
            if ( $page > 1 ) {
                echo '<a href="#" class="murat-page-link" data-page="' . ( $page - 1 ) . '">&laquo; Önceki</a> ';
            }
            echo " Sayfa $page / $total_pages ";
            if ( $page < $total_pages ) {
                echo ' <a href="#" class="murat-page-link" data-page="' . ( $page + 1 ) . '">Sonraki &raquo;</a>';
            }
            echo '</div></div>';
        }
        $pagination_html = ob_get_clean();
        
        wp_send_json_success( array(
            'html'       => $html,
            'pagination' => $pagination_html
        ) );
    }
    
    /**
     * Belirtilen yazı ID'si için silme bağlantısı oluşturur (nonce ile korunur).
     *
     * @param int $post_id
     * @return string URL
     */
    private function get_delete_link( $post_id ) {
        $nonce = wp_create_nonce( 'delete_post_' . $post_id );
        $url = add_query_arg(
            array(
                'page'        => 'murat-similar-posts',
                'action'      => 'delete_post',
                'post_id'     => $post_id,
                'murat_nonce' => $nonce,
            ),
            admin_url( 'admin.php' )
        );
        return $url;
    }
}

new Murat_Similar_Posts();
JavaScript Dosyası:
wp-content/plugins/Murat/

Kod:Tümünü seç

murat-admin.js

Kod:Tümünü seç

jQuery(document).ready(function($) {
    // "Benzer Yazıları Sorgula" butonuna tıklandığında sorguyu başlat
    $('#murat-query-btn').on('click', function(e) {
        e.preventDefault();
        loadMuratResults(1);
    });
    
    // Pagination linklerine tıklama (delegasyon)
    $('#murat-results, #murat-pagination').on('click', '.murat-page-link', function(e) {
        e.preventDefault();
        var page = $(this).data('page');
        loadMuratResults(page);
    });
    
    function loadMuratResults(page) {
        $('#murat-results').html('<p>Sonuçlar yükleniyor...</p>');
        $.post(murat_ajax.ajax_url, { action: 'murat_get_similar_posts', page: page }, function(response) {
            if(response.success) {
                $('#murat-results').html(response.data.html);
                $('#murat-pagination').html(response.data.pagination);
            } else {
                $('#murat-results').html('<p>Hata: ' + response.data.message + '</p>');
            }
        });
    }
});
Açıklamalar
Admin Sayfası & Ayarlar:

Üst kısımda benzerlik eşiği ayarlanabilir. Ayarlar formu gönderildiğinde kaydedilir.
Sorgu İşlemi (AJAX):

“Benzer Yazıları Sorgula” butonuna tıklandığında AJAX çağrısı yapılır.
Sorgu, tüm yayın yazılar üzerinde çift döngüyle benzerlik hesaplar (ağır işlem olduğu için transient ile 10 dakika cache’lenir).
Sonuçlar, belirlenen sayfa başına 10 adet olacak şekilde dilimlenir.
Tablo halinde her satırda iki yazı (başlık ve içerik özet), benzerlik yüzdesi, düzenleme ve silme linkleri gösterilir.
Pagination kontrolleri AJAX ile yüklenir.
Bu yapı sayesinde, admin ve ayar sayfası hızlıca açılır; kullanıcı sorguyu istediği zaman çalıştırır ve sonuçlar her seferinde belirlenen adet kadar (örneğin 10) sayfalı olarak gösterilir. Böylece ağır sorgu işlemi tek seferde tüm sonucu getirmek yerine parça parça yüklendiğinden sayfa çökmesi önlenir.

Re: aynı yada benzeyen yazıları süzme

Gönderilme zamanı: Sal Şub 18, 2025 5:32 pm
gönderen muratca61
sorgulama bir satırda kullanıcıya gösterilsin anlık olarak tek saturda neyi sorguluyorsa göstersin bir de sorgulamayı durdurma ekleyelim
1m 11s hakkında Akıl yürütüldü
Aşağıdaki örnekte, önce ayarlar (örneğin benzerlik eşiği) ve sorgulama başlat/durdur kontrollerinin bulunduğu bir admin sayfası oluşturuyoruz.

Özellikler:

Admin sayfasında ayar formu (opsiyonel) yer alıyor.
"Sorgulamayı Başlat" butonuna tıklandığında, AJAX üzerinden her çağrıda belirli sayıda (batch) yazı karşılaştırması yapılıyor.
Anlık olarak, tek satırda hangi yazıların karşılaştırıldığı ve hesaplanan benzerlik yüzdesi gösteriliyor.
"Sorgulamayı Durdur" butonu sayesinde kullanıcı sorgulamayı durdurabiliyor.
Aşağıdaki kodları, örneğin eklenti klasörünüzde (örneğin wp-content/plugins/Murat/) iki dosya oluşturacak şekilde kullanabilirsiniz:
1. PHP Dosyası:

Kod:Tümünü seç

murat-similar-progress.php

Kod:Tümünü seç

<?php
/*
Plugin Name: Murat Benzer Yazılar Gerçek Zamanlı Sorgulama
Description: Yazılar arasında benzerlik tespitini gerçek zamanlı, tek satırda gösterir. Kullanıcı sorgulamayı başlatıp, anlık olarak hangi yazıların karşılaştırıldığını görebilir ve isteğe bağlı olarak sorgulamayı durdurabilir.
Version: 1.0
Author: Murat
License: GPL2
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Murat_RealTime_Similar_Posts {
    // Ayar için benzerlik eşiği (opsiyonel; burada ileride kullanılabilir, varsayılan %50)
    private $option_name = 'murat_similar_threshold';
    // Her AJAX çağrısında yapılacak karşılaştırma adedi (batch boyutu)
    private $batch_size = 5;

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'wp_ajax_murat_query_progress', array( $this, 'ajax_query_progress' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
        // Ayar formu gönderilmişse güncelleme (opsiyonel)
        add_action( 'admin_init', array( $this, 'handle_settings' ) );
    }

    public function add_admin_menu() {
        add_menu_page(
            'Murat',
            'Murat',
            'manage_options',
            'murat-similar-progress',
            array( $this, 'admin_page' ),
            'dashicons-admin-post',
            25
        );
    }

    public function enqueue_admin_scripts( $hook ) {
        if ( $hook == 'toplevel_page_murat-similar-progress' ) {
            wp_enqueue_script(
                'murat-admin-js',
                plugin_dir_url( __FILE__ ) . 'murat-admin.js',
                array( 'jquery' ),
                '1.0',
                true
            );
            wp_localize_script( 'murat-admin-js', 'murat_ajax', array(
                'ajax_url' => admin_url( 'admin-ajax.php' )
            ) );
        }
    }

    /**
     * Ayar formu gönderildiyse benzerlik eşiğini günceller.
     */
    public function handle_settings() {
        if ( isset( $_POST['murat_similar_threshold_nonce'] ) && wp_verify_nonce( $_POST['murat_similar_threshold_nonce'], 'update_threshold' ) ) {
            $threshold = isset( $_POST['murat_similar_threshold'] ) ? intval( $_POST['murat_similar_threshold'] ) : 50;
            update_option( $this->option_name, $threshold );
            add_settings_error( 'murat_similar_messages', 'murat_similar_message', 'Ayarlar güncellendi.', 'updated' );
        }
    }

    /**
     * Admin sayfasını oluşturur.
     */
    public function admin_page() {
        settings_errors( 'murat_similar_messages' );
        $threshold = get_option( $this->option_name, 50 );
        ?>
        <div class="wrap">
            <h1>Murat Gerçek Zamanlı Benzer Yazılar Sorgulama</h1>
            <!-- Opsiyonel ayar formu -->
            <form method="post" action="">
                <?php wp_nonce_field( 'update_threshold', 'murat_similar_threshold_nonce' ); ?>
                <table class="form-table">
                    <tr>
                        <th scope="row">Benzerlik Eşiği (%)</th>
                        <td>
                            <input type="number" name="murat_similar_threshold" value="<?php echo esc_attr( $threshold ); ?>" min="0" max="100" />
                            <p class="description">Örneğin, 70 girilirse %70 ve üzeri benzerlikte sorgulama yapılır.</p>
                        </td>
                    </tr>
                </table>
                <?php submit_button( 'Ayarları Kaydet' ); ?>
            </form>
            <hr>
            <h2>Sorgulama</h2>
            <!-- Anlık ilerleme bilgisinin gösterileceği alan -->
            <p id="murat-progress">Henüz sorgulama başlatılmadı.</p>
            <button id="murat-start-btn" class="button button-primary">Sorgulamayı Başlat</button>
            <button id="murat-stop-btn" class="button" disabled>Sorgulamayı Durdur</button>
        </div>
        <?php
    }

    /**
     * AJAX: Gerçek zamanlı sorgulama işlemi
     *
     * Bu fonksiyon, tüm yayın yazıları üzerinden çift döngüyle (i,j) karşılaştırma yapar.
     * Her AJAX çağrısında batch_size kadar karşılaştırma yapılır ve anlık olarak
     * hangi iki yazının karşılaştırıldığı, hesaplanan benzerlik yüzde değeri ile birlikte gönderilir.
     *
     * Gelen parametreler: outer_index, inner_index
     */
    public function ajax_query_progress() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => 'Yetkisiz erişim.' ) );
        }
        
        $outer_index = isset( $_POST['outer_index'] ) ? intval( $_POST['outer_index'] ) : 0;
        $inner_index = isset( $_POST['inner_index'] ) ? intval( $_POST['inner_index'] ) : 1;
        
        $posts = get_posts( array(
            'numberposts' => -1,
            'post_status' => 'publish',
            'post_type'   => 'post'
        ) );
        $n = count( $posts );
        if ( $n < 2 ) {
            wp_send_json_error( array( 'message' => 'Yeterli yazı bulunamadı.' ) );
        }
        
        // Toplam karşılaştırma sayısı (isteğe bağlı hesaplanabilir)
        $total_comparisons = ($n * ($n - 1)) / 2;
        
        $message = '';
        // Belirlenen batch_size kadar karşılaştırma yapıyoruz.
        for ( $i = 0; $i < $this->batch_size; $i++ ) {
            // Eğer outer_index n-1 veya daha fazlaysa sorgulama tamamlanmıştır.
            if ( $outer_index >= $n - 1 ) {
                wp_send_json_success( array(
                    'message'      => 'Sorgulama tamamlandı. Toplam ' . round($total_comparisons) . ' karşılaştırma yapıldı.',
                    'finished'     => true,
                    'outer_index'  => $outer_index,
                    'inner_index'  => $inner_index,
                ) );
            }
            $post1 = $posts[$outer_index];
            $post2 = $posts[$inner_index];
            
            // Benzerlik hesaplaması: başlık ve içerik (basit örnek)
            similar_text( $post1->post_title, $post2->post_title, $title_sim );
            $content1 = wp_strip_all_tags( $post1->post_content );
            $content2 = wp_strip_all_tags( $post2->post_content );
            similar_text( $content1, $content2, $content_sim );
            $similarity = ( 0.7 * $title_sim ) + ( 0.3 * $content_sim );
            
            $message = "Şu anda: \"{$post1->post_title}\" ile \"{$post2->post_title}\" karşılaştırılıyor. Benzerlik: " . round($similarity,2) . "%";
            
            // İleriye geçelim: inner_index++
            $inner_index++;
            if ( $inner_index >= $n ) {
                $outer_index++;
                $inner_index = $outer_index + 1;
            }
        }
        
        wp_send_json_success( array(
            'message'      => $message,
            'finished'     => false,
            'outer_index'  => $outer_index,
            'inner_index'  => $inner_index,
        ) );
    }
}

new Murat_RealTime_Similar_Posts();
2. JavaScript Dosyası:

Kod:Tümünü seç

murat-admin.js

Kod:Tümünü seç

jQuery(document).ready(function($) {
    var stopQuery = false;
    var outer_index = 0;
    var inner_index = 1;

    // Başlat butonuna tıklanınca sorgulamayı başlat
    $('#murat-start-btn').on('click', function(e) {
        e.preventDefault();
        stopQuery = false;
        outer_index = 0;
        inner_index = 1;
        $('#murat-start-btn').attr('disabled', true);
        $('#murat-stop-btn').attr('disabled', false);
        $('#murat-progress').text('Sorgulama başlatılıyor...');
        startQuery();
    });

    // Durdur butonuna tıklanınca sorgulamayı durdur
    $('#murat-stop-btn').on('click', function(e) {
        e.preventDefault();
        stopQuery = true;
        $('#murat-start-btn').attr('disabled', false);
        $('#murat-stop-btn').attr('disabled', true);
        $('#murat-progress').append(' - Sorgulama durduruldu.');
    });

    // Sorgulamayı AJAX ile batch batch devam ettiriyoruz
    function startQuery() {
        if(stopQuery) {
            return;
        }
        $.post(murat_ajax.ajax_url, {
            action: 'murat_query_progress',
            outer_index: outer_index,
            inner_index: inner_index
        }, function(response) {
            if(response.success) {
                $('#murat-progress').text(response.data.message);
                outer_index = response.data.outer_index;
                inner_index = response.data.inner_index;
                if(response.data.finished) {
                    $('#murat-progress').append(' - Sorgulama tamamlandı.');
                    $('#murat-start-btn').attr('disabled', false);
                    $('#murat-stop-btn').attr('disabled', true);
                } else {
                    // 500ms sonra bir sonraki batch çağrısı yapılır.
                    setTimeout(startQuery, 500);
                }
            } else {
                $('#murat-progress').text('Hata: ' + response.data.message);
                $('#murat-start-btn').attr('disabled', false);
                $('#murat-stop-btn').attr('disabled', true);
            }
        });
    }
});
Açıklamalar
PHP Tarafı:

ajax_query_progress() fonksiyonu, gelen outer_index ve inner_index değerlerine göre yazılar arasındaki karşılaştırmalardan batch (örneğin 5 adet) yapar ve anlık olarak hangi iki yazının karşılaştırıldığını, hesaplanan benzerlik yüzdesini içeren bir mesaj döner.
Eğer tüm karşılaştırmalar tamamlandıysa, finished değeri true olarak döner.
JS Tarafı:

"Sorgulamayı Başlat" butonuna tıklandığında global indeksler sıfırlanır, "Stop" butonu aktif edilir ve startQuery() fonksiyonu AJAX ile sorgulamayı başlatır.
Her AJAX çağrısı, gelen ilerleme mesajını günceller; sonrasında 500ms gecikmeyle bir sonraki çağrı yapılır.
"Sorgulamayı Durdur" butonu tıklandığında global stopQuery değişkeni true olur ve sorgulama durdurulur.
Bu yapı sayesinde, kullanıcı admin sayfasında önce ayarları (opsiyonel) görebilir; ardından "Sorgulamayı Başlat" butonuyla gerçek zamanlı olarak (tek satırda) hangi yazıların karşılaştırıldığını görebilir ve istenirse sorgulamayı "Durdur" butonuyla iptal edebilir.

Re: aynı yada benzeyen yazıları süzme

Gönderilme zamanı: Sal Şub 18, 2025 5:50 pm
gönderen muratca61
bu şekilde değil burada kullanıcıdan Başlık , İçerik , İçerik/Başlık ayarı seçmesi istenecek. seçilen ayar ile arama fonksiyonu kullanılarak eşleşen benzeşme anında listelenecek. hızlı olması için arama fonksiyonunu kullanalım
Aşağıdaki örnekte, kullanıcıya bir arama formu sunuluyor. Bu formda;

Arama Terimi
Arama Yöntemi (radio butonları: yalnızca Başlık, yalnızca İçerik veya Hem Başlık Hem İçerik)
seçimi yapılıyor. Kullanıcı arama terimini girdiğinde (veya tuşa bastığında) AJAX üzerinden sorgulama yapılıyor. Sorgulama; WordPress’in arama fonksiyonu (WP_Query) ve “posts_search” filtresi kullanılarak, seçime göre yalnızca post_title, yalnızca post_content veya her ikisinde arama yapacak şekilde özelleştiriliyor. Sonuçlar; her satırda yazı başlığı, içerik özetinin yanında “Düzenle (yeni sekmede)” ve “Sil” linkleri şeklinde listeleniyor.

Ayrıca, “Sil” linki tıklanınca ilgili yazı siliniyor (nonce ile korunuyor).

Aşağıdaki dosyaları, örneğin eklenti klasörünüzde (ör. wp-content/plugins/Murat/) oluşturabilirsiniz.
1. PHP Dosyası:

Kod:Tümünü seç

murat-similar-search.php

Kod:Tümünü seç

<?php
/*
Plugin Name: Murat Benzer Yazılar Arama
Description: Kullanıcının seçimine göre (Başlık, İçerik, İçerik/Başlık) yazılar arasında arama yaparak eşleşen sonuçları anında listeleyen uygulama. Her satırda yazı başlığı, içerik özeti, düzenleme (yeni sekmede) ve silme linkleri yer alır.
Version: 1.0
Author: Murat
License: GPL2
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Murat_Similar_Search {

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'wp_ajax_murat_search_posts', array( $this, 'ajax_search_posts' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
        add_action( 'admin_init', array( $this, 'handle_deletion' ) );
        add_filter( 'posts_search', array( $this, 'custom_posts_search' ), 10, 2 );
    }
    
    public function add_admin_menu() {
        add_menu_page(
            'Murat',
            'Murat',
            'manage_options',
            'murat-similar-search',
            array( $this, 'admin_page' ),
            'dashicons-search',
            25
        );
    }
    
    public function enqueue_admin_scripts( $hook ) {
        if ( $hook == 'toplevel_page_murat-similar-search' ) {
            wp_enqueue_script(
                'murat-search-js',
                plugin_dir_url( __FILE__ ) . 'murat-search.js',
                array( 'jquery' ),
                '1.0',
                true
            );
            wp_localize_script( 'murat-search-js', 'murat_search', array(
                'ajax_url' => admin_url( 'admin-ajax.php' ),
                'nonce'    => wp_create_nonce( 'murat_search_nonce' )
            ) );
            wp_enqueue_style(
                'murat-search-css',
                plugin_dir_url( __FILE__ ) . 'murat-search.css',
                array(),
                '1.0'
            );
        }
    }
    
    public function admin_page() {
        ?>
        <div class="wrap">
            <h1>Murat Benzer Yazılar Arama</h1>
            <form id="murat-search-form">
                <table class="form-table">
                    <tr>
                        <th scope="row">Arama Terimi</th>
                        <td>
                            <input type="text" id="murat_search_term" name="murat_search_term" value="" placeholder="Arama terimi girin" style="width:300px;">
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Arama Yöntemi</th>
                        <td>
                            <label><input type="radio" name="murat_search_mode" value="title" checked> Başlık</label> &nbsp;
                            <label><input type="radio" name="murat_search_mode" value="content"> İçerik</label> &nbsp;
                            <label><input type="radio" name="murat_search_mode" value="both"> İçerik/Başlık</label>
                        </td>
                    </tr>
                </table>
                <p>
                    <button id="murat_search_btn" class="button button-primary" type="button">Ara</button>
                </p>
            </form>
            <hr>
            <div id="murat-search-results">
                <!-- Arama sonuçları buraya yüklenecek -->
            </div>
        </div>
        <?php
    }
    
    /**
     * AJAX: Arama sorgusunu çalıştırır.
     */
    public function ajax_search_posts() {
        check_ajax_referer( 'murat_search_nonce', 'nonce' );
        $search_term = isset( $_POST['search_term'] ) ? sanitize_text_field( $_POST['search_term'] ) : '';
        $search_mode = isset( $_POST['search_mode'] ) ? sanitize_text_field( $_POST['search_mode'] ) : 'title';
        
        if ( empty( $search_term ) ) {
            wp_send_json_error( array( 'message' => 'Arama terimi boş.' ) );
        }
        
        // WP_Query'ye özel sorgu parametresi ekliyoruz.
        $args = array(
            's'               => $search_term,
            'post_status'     => 'publish',
            'post_type'       => 'post',
            'murat_search_mode' => $search_mode, // Özel parametre
            'posts_per_page'  => 20,
        );
        
        $query = new WP_Query( $args );
        if ( $query->have_posts() ) {
            ob_start();
            ?>
            <table class="widefat fixed striped">
                <thead>
                    <tr>
                        <th>Başlık</th>
                        <th>İçerik Özeti</th>
                        <th>İşlemler</th>
                    </tr>
                </thead>
                <tbody>
            <?php
            while ( $query->have_posts() ) {
                $query->the_post();
                $post_id = get_the_ID();
                ?>
                <tr>
                    <td><?php the_title(); ?></td>
                    <td><?php echo esc_html( wp_trim_words( get_the_content(), 20, '...' ) ); ?></td>
                    <td>
                        <a href="<?php echo esc_url( get_edit_post_link( $post_id ) ); ?>" target="_blank">Düzenle</a> | 
                        <a href="<?php echo esc_url( $this->get_delete_link( $post_id ) ); ?>" onclick="return confirm('Bu yazıyı silmek istediğinize emin misiniz?');">Sil</a>
                    </td>
                </tr>
                <?php
            }
            ?>
                </tbody>
            </table>
            <?php
            wp_reset_postdata();
            $html = ob_get_clean();
            wp_send_json_success( array( 'html' => $html ) );
        } else {
            wp_send_json_success( array( 'html' => '<p>Arama kriterlerine uygun yazı bulunamadı.</p>' ) );
        }
    }
    
    /**
     * "posts_search" filtresini, seçilen arama yöntemine göre (title, content, both) özelleştirir.
     */
    public function custom_posts_search( $search, $wp_query ) {
        global $wpdb;
        $mode = $wp_query->get( 'murat_search_mode' );
        if ( ! $mode || ! $wp_query->is_search() ) {
            return $search;
        }
        $search_term = $wp_query->get( 's' );
        if ( empty( $search_term ) ) {
            return $search;
        }
        
        $like = '%' . $wpdb->esc_like( $search_term ) . '%';
        if ( 'title' === $mode ) {
            $search = $wpdb->prepare( " AND ({$wpdb->posts}.post_title LIKE %s) ", $like );
        } elseif ( 'content' === $mode ) {
            $search = $wpdb->prepare( " AND ({$wpdb->posts}.post_content LIKE %s) ", $like );
        } elseif ( 'both' === $mode ) {
            $search = $wpdb->prepare( " AND (({$wpdb->posts}.post_title LIKE %s) OR ({$wpdb->posts}.post_content LIKE %s)) ", $like, $like );
        }
        return $search;
    }
    
    /**
     * GET üzerinden gelen silme isteğini işler.
     */
    public function handle_deletion() {
        if ( isset( $_GET['action'] ) && $_GET['action'] == 'delete_post' && isset( $_GET['post_id'] ) && isset( $_GET['murat_nonce'] ) ) {
            $post_id = intval( $_GET['post_id'] );
            if ( wp_verify_nonce( $_GET['murat_nonce'], 'delete_post_' . $post_id ) ) {
                wp_delete_post( $post_id, true );
                wp_redirect( admin_url( 'admin.php?page=murat-similar-search' ) );
                exit;
            }
        }
    }
    
    /**
     * Belirtilen yazı ID'si için silme linki üretir (nonce ile korunur).
     */
    private function get_delete_link( $post_id ) {
        $nonce = wp_create_nonce( 'delete_post_' . $post_id );
        $url = add_query_arg(
            array(
                'action'      => 'delete_post',
                'post_id'     => $post_id,
                'murat_nonce' => $nonce,
            ),
            admin_url( 'admin-post.php' )
        );
        return $url;
    }
}

new Murat_Similar_Search();
2. JavaScript Dosyası:

Kod:Tümünü seç

murat-search.js

Kod:Tümünü seç

jQuery(document).ready(function($) {
    $('#murat_search_btn').on('click', function(e) {
        e.preventDefault();
        performSearch();
    });
    
    // İsteğe bağlı: yazarken aramayı tetiklemek (debounce)
    var timeout;
    $('#murat_search_term').on('keyup', function() {
        clearTimeout(timeout);
        timeout = setTimeout(function() {
            performSearch();
        }, 500);
    });
    
    function performSearch() {
        var searchTerm = $('#murat_search_term').val();
        var searchMode = $('input[name="murat_search_mode"]:checked').val();
        $('#murat-search-results').html('<p>Aranıyor...</p>');
        $.post(murat_search.ajax_url, {
            action: 'murat_search_posts',
            nonce: murat_search.nonce,
            search_term: searchTerm,
            search_mode: searchMode
        }, function(response) {
            if(response.success) {
                $('#murat-search-results').html(response.data.html);
            } else {
                $('#murat-search-results').html('<p>Hata: ' + response.data.message + '</p>');
            }
        });
    }
});
3. CSS Dosyası (Opsiyonel):

Kod:Tümünü seç

murat-search.css

Kod:Tümünü seç

/* Basit stil düzenlemeleri */
#murat-search-results table {
    margin-top: 20px;
}
Nasıl Çalışır?
Admin Sayfası:

Menüden "Murat" seçeneği altında sayfa açılır.
Kullanıcı, arama terimini girer ve arama yöntemini (başlık, içerik veya her ikisi) seçer.
AJAX Arama:

Kullanıcı “Ara” butonuna tıkladığında (veya yazarken), JavaScript AJAX isteği gönderir.
İstek, seçilen arama yöntemi ve terimi ile WP_Query çalıştırmak üzere PHP tarafında “murat_search_posts” aksiyonuna yönlendirilir.
"posts_search" filtresi, seçilen yönteme göre sorgunun SQL kısmını özelleştirir.
Sonuçların Listeleme:

Elde edilen sonuçlar, her satırda yazı başlığı, içerik özeti, düzenleme (yeni sekmede) ve silme linkleri şeklinde tablo halinde döndürülür.
Silme linkine tıklandığında ilgili yazı nonce doğrulamasıyla silinir.
Bu yapı sayesinde kullanıcı, seçtiği arama yöntemine göre anlık olarak eşleşen yazıları hızlıca görebilir.

Re: aynı yada benzeyen yazıları süzme

Gönderilme zamanı: Sal Şub 18, 2025 6:02 pm
gönderen muratca61
arama terimi girilmeyecek. burada başlık , içerik veya ham başlık hemde içeriğe göre kullanıcının seçeceği benzeşme oranına göre arama yöntemiyle her yazıyı veritabanında arayarak benzeşen yazıları listeleyecek
Aşağıdaki örnekte, admin sayfasında kullanıcıya hangi alan(lar)a göre (başlık, içerik ya da her ikisine) benzerlik aranacağı ve hangi benzeşme eşiğinin (yüzde olarak) kullanılacağı soruluyor. Kullanıcı bu ayarları seçtikten sonra “Sorgula” butonuna tıklıyor. Arka planda AJAX ile veritabanındaki tüm yayın yazılarında (küçük siteler için uygundur) çift döngüyle karşılaştırma yapılarak, eşik değerini aşan çiftler anlık (batch) olarak sonuç tablosuna ekleniyor. Ayrıca, “Sorgulamayı Durdur” butonuyla işlem durdurulabiliyor.

Aşağıdaki dosyaları eklenti klasörünüzde (ör. wp-content/plugins/Murat/) oluşturabilirsiniz.
1. PHP Dosyası:

Kod:Tümünü seç

murat-similar-search.php

Kod:Tümünü seç

<?php
/*
Plugin Name: Murat Benzer Yazılar Arama (Otomatik)
Description: Kullanıcı tarafından seçilen arama yöntemi (Başlık, İçerik veya Hem Başlık Hem İçerik) ve benzeşme eşiğine göre, veritabanındaki tüm yayın yazıları arasında benzerlik tespitini çalıştırarak eşleşen yazı çiftlerini listeleyen uygulama. Her satırda yazı başlığı, içerik özeti, düzenleme (yeni sekmede) ve silme linkleri yer alır.
Version: 1.0
Author: Murat
License: GPL2
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Murat_Similar_Search_Auto {

    // Her AJAX çağrısında kaç karşılaştırma yapılacak (batch boyutu)
    private $batch_size = 5;

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'wp_ajax_murat_similar_query', array( $this, 'ajax_similar_query' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
        add_action( 'admin_init', array( $this, 'handle_deletion' ) );
    }

    /**
     * Admin menüsüne "Murat" başlığı altında sayfa ekler.
     */
    public function add_admin_menu() {
        add_menu_page(
            'Murat',
            'Murat',
            'manage_options',
            'murat-similar-search',
            array( $this, 'admin_page' ),
            'dashicons-search',
            25
        );
    }

    /**
     * Admin sayfası için JS/CSS dosyalarını ekler.
     */
    public function enqueue_admin_scripts( $hook ) {
        if ( $hook == 'toplevel_page_murat-similar-search' ) {
            wp_enqueue_script(
                'murat-similar-search-js',
                plugin_dir_url( __FILE__ ) . 'murat-similar-search.js',
                array( 'jquery' ),
                '1.0',
                true
            );
            wp_localize_script( 'murat-similar-search-js', 'murat_similar', array(
                'ajax_url' => admin_url( 'admin-ajax.php' ),
                'nonce'    => wp_create_nonce( 'murat_similar_nonce' )
            ) );
            wp_enqueue_style(
                'murat-similar-search-css',
                plugin_dir_url( __FILE__ ) . 'murat-similar-search.css',
                array(),
                '1.0'
            );
        }
    }

    /**
     * Admin sayfasını oluşturur.
     */
    public function admin_page() {
        ?>
        <div class="wrap">
            <h1>Murat Benzer Yazılar Arama</h1>
            <form id="murat-similar-form">
                <table class="form-table">
                    <tr>
                        <th scope="row">Arama Yöntemi</th>
                        <td>
                            <label><input type="radio" name="murat_search_mode" value="title" checked> Sadece Başlık</label><br>
                            <label><input type="radio" name="murat_search_mode" value="content"> Sadece İçerik</label><br>
                            <label><input type="radio" name="murat_search_mode" value="both"> Hem Başlık Hem İçerik</label>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Benzeşme Eşiği (%)</th>
                        <td>
                            <input type="number" name="murat_threshold" id="murat_threshold" value="70" min="0" max="100" style="width:80px;">
                            <p class="description">Yüzde olarak; örneğin 70 girilirse %70 ve üzeri benzerlikte sonuç listelenecektir.</p>
                        </td>
                    </tr>
                </table>
                <p>
                    <button id="murat-start-btn" class="button button-primary" type="button">Sorgulamayı Başlat</button>
                    <button id="murat-stop-btn" class="button" type="button" disabled>Sorgulamayı Durdur</button>
                </p>
            </form>
            <hr>
            <div id="murat-similar-progress">
                <!-- Anlık ilerleme bilgisi burada gösterilecek -->
            </div>
            <div id="murat-similar-results">
                <!-- Eşleşen yazı çiftleri buraya eklenecek -->
            </div>
        </div>
        <?php
    }

    /**
     * AJAX: Belirtilen arama yöntemi ve eşik değerine göre yazılar arasındaki benzerlik tespitini batch olarak çalıştırır.
     * Gönderilen parametreler: outer_index, inner_index, search_mode, threshold
     */
    public function ajax_similar_query() {
        check_ajax_referer( 'murat_similar_nonce', 'nonce' );

        $outer_index = isset( $_POST['outer_index'] ) ? intval( $_POST['outer_index'] ) : 0;
        $inner_index = isset( $_POST['inner_index'] ) ? intval( $_POST['inner_index'] ) : 0;
        $search_mode = isset( $_POST['search_mode'] ) ? sanitize_text_field( $_POST['search_mode'] ) : 'title';
        $threshold   = isset( $_POST['threshold'] ) ? floatval( $_POST['threshold'] ) : 70;

        $posts = get_posts( array(
            'numberposts' => -1,
            'post_status' => 'publish',
            'post_type'   => 'post',
            'orderby'     => 'ID',
            'order'       => 'ASC'
        ) );
        $n = count( $posts );
        if ( $n < 2 ) {
            wp_send_json_error( array( 'message' => 'Yeterli yazı bulunamadı.' ) );
        }

        $result_html = '';
        // Her çağrıda batch_size kadar karşılaştırma yapalım.
        for ( $i = 0; $i < $this->batch_size; $i++ ) {

            // Eğer outer_index n-1 veya daha fazlaysa tüm karşılaştırmalar bitti.
            if ( $outer_index >= $n - 1 ) {
                wp_send_json_success( array(
                    'html'         => $result_html,
                    'finished'     => true,
                    'outer_index'  => $outer_index,
                    'inner_index'  => $inner_index
                ) );
            }

            // Ayarlanmış karşılaştırma: iki yazı arasında
            $post1 = $posts[ $outer_index ];
            $post2 = $posts[ $inner_index ];

            // Seçilen yönteme göre karşılaştırma yapalım:
            if ( 'title' === $search_mode ) {
                similar_text( $post1->post_title, $post2->post_title, $sim );
            } elseif ( 'content' === $search_mode ) {
                $content1 = wp_strip_all_tags( $post1->post_content );
                $content2 = wp_strip_all_tags( $post2->post_content );
                similar_text( $content1, $content2, $sim );
            } else { // 'both'
                $combined1 = $post1->post_title . ' ' . wp_strip_all_tags( $post1->post_content );
                $combined2 = $post2->post_title . ' ' . wp_strip_all_tags( $post2->post_content );
                similar_text( $combined1, $combined2, $sim );
            }

            // Eğer benzeşme eşik değerine eşit veya üstüyse listeye ekle.
            if ( $sim >= $threshold ) {
                $result_html .= '<tr>';
                $result_html .= '<td><strong>' . esc_html( $post1->post_title ) . '</strong><br>' . esc_html( wp_trim_words( $post1->post_content, 15, '...' ) ) . '</td>';
                $result_html .= '<td><strong>' . esc_html( $post2->post_title ) . '</strong><br>' . esc_html( wp_trim_words( $post2->post_content, 15, '...' ) ) . '</td>';
                $result_html .= '<td>' . round( $sim, 2 ) . '%</td>';
                $result_html .= '<td><a href="' . esc_url( get_edit_post_link( $post1->ID ) ) . '" target="_blank">Düzenle 1</a> | <a href="' . esc_url( $this->get_delete_link( $post1->ID ) ) . '" onclick="return confirm(\'Bu yazıyı silmek istediğinize emin misiniz?\');">Sil 1</a><br>';
                $result_html .= '<a href="' . esc_url( get_edit_post_link( $post2->ID ) ) . '" target="_blank">Düzenle 2</a> | <a href="' . esc_url( $this->get_delete_link( $post2->ID ) ) . '" onclick="return confirm(\'Bu yazıyı silmek istediğinize emin misiniz?\');">Sil 2</a></td>';
                $result_html .= '</tr>';
            }

            // İleriye geçelim: inner_index artıyor.
            $inner_index++;
            if ( $inner_index >= $n ) {
                $outer_index++;
                $inner_index = $outer_index + 1;
            }
        }

        wp_send_json_success( array(
            'html'         => $result_html,
            'finished'     => false,
            'outer_index'  => $outer_index,
            'inner_index'  => $inner_index
        ) );
    }

    /**
     * Belirtilen yazı ID'si için silme linki üretir (nonce ile korunur).
     */
    private function get_delete_link( $post_id ) {
        $nonce = wp_create_nonce( 'delete_post_' . $post_id );
        $url = add_query_arg(
            array(
                'page'       => 'murat-similar-search',
                'action'     => 'delete_post',
                'post_id'    => $post_id,
                'murat_nonce'=> $nonce,
            ),
            admin_url( 'admin.php' )
        );
        return $url;
    }

    /**
     * GET üzerinden gelen silme isteğini işler.
     */
    public function handle_deletion() {
        if ( isset( $_GET['action'] ) && $_GET['action'] == 'delete_post' && isset( $_GET['post_id'] ) && isset( $_GET['murat_nonce'] ) ) {
            $post_id = intval( $_GET['post_id'] );
            if ( wp_verify_nonce( $_GET['murat_nonce'], 'delete_post_' . $post_id ) ) {
                wp_delete_post( $post_id, true );
                wp_redirect( admin_url( 'admin.php?page=murat-similar-search' ) );
                exit;
            }
        }
    }
}

new Murat_Similar_Search_Auto();
2. JavaScript Dosyası:

Kod:Tümünü seç

murat-similar-search.js

Kod:Tümünü seç

jQuery(document).ready(function($) {

    var stopQuery = false;
    // Global indeksler; işlem tüm yazılar üzerinden çift döngü şeklinde yapılacak
    var outer_index = 0;
    var inner_index = 1;

    // Sorgulamayı başlat butonuna tıklanınca:
    $('#murat-start-btn').on('click', function(e) {
        e.preventDefault();
        stopQuery = false;
        // Ayarları oku:
        outer_index = 0;
        inner_index = 1;
        var threshold = $('#murat_threshold').val();
        var search_mode = $('input[name="murat_search_mode"]:checked').val();

        $('#murat-start-btn').attr('disabled', true);
        $('#murat-stop-btn').attr('disabled', false);
        $('#murat-similar-progress').html('<p>Sorgulama başlatılıyor…</p>');
        $('#murat-similar-results').html('<table class="widefat striped"><thead><tr><th>Yazı 1</th><th>Yazı 2</th><th>Benzerlik</th><th>İşlemler</th></tr></thead><tbody></tbody></table>');
        startQuery(threshold, search_mode);
    });

    // Sorgulamayı durdur butonuna tıklanınca:
    $('#murat-stop-btn').on('click', function(e) {
        e.preventDefault();
        stopQuery = true;
        $('#murat-start-btn').attr('disabled', false);
        $('#murat-stop-btn').attr('disabled', true);
        $('#murat-similar-progress').append('<p>Sorgulama durduruldu.</p>');
    });

    function startQuery(threshold, search_mode) {
        if ( stopQuery ) {
            return;
        }
        $.post(murat_similar.ajax_url, {
            action: 'murat_similar_query',
            nonce: murat_similar.nonce,
            outer_index: outer_index,
            inner_index: inner_index,
            threshold: threshold,
            search_mode: search_mode
        }, function(response) {
            if ( response.success ) {
                // Sonuçları mevcut tabloya ekle:
                if ( response.data.html ) {
                    $('#murat-similar-results table tbody').append(response.data.html);
                }
                // Güncel indeksleri al:
                outer_index = response.data.outer_index;
                inner_index = response.data.inner_index;
                // İlerleme bilgisini güncelle:
                if ( response.data.finished ) {
                    $('#murat-similar-progress').html('<p>Sorgulama tamamlandı.</p>');
                    $('#murat-start-btn').attr('disabled', false);
                    $('#murat-stop-btn').attr('disabled', true);
                } else {
                    $('#murat-similar-progress').html('<p>Devam ediyor… (outer_index: ' + outer_index + ', inner_index: ' + inner_index + ')</p>');
                    // Kısa süre sonra sonraki batch çalışsın.
                    setTimeout(function() {
                        startQuery(threshold, search_mode);
                    }, 500);
                }
            } else {
                $('#murat-similar-progress').html('<p>Hata: ' + response.data.message + '</p>');
                $('#murat-start-btn').attr('disabled', false);
                $('#murat-stop-btn').attr('disabled', true);
            }
        });
    }
});
3. CSS Dosyası (Opsiyonel):

Kod:Tümünü seç

murat-similar-search.css

Kod:Tümünü seç

/* Basit stil düzenlemeleri */
#murat-similar-results {
    margin-top: 20px;
}
#wrapper .widefat th,
#wrapper .widefat td {
    padding: 10px;
}
Açıklamalar
Admin Sayfası:

Kullanıcıya “Arama Yöntemi” (radio: Sadece Başlık / Sadece İçerik / Hem Başlık Hem İçerik) ve “Benzeşme Eşiği (%)” (örneğin 70) girme alanı sunulur.
“Sorgulamayı Başlat” butonuyla işlem başlatılır; “Durdur” butonuyla istenirse iptal edilebilir.
AJAX İşlemi (PHP Tarafı):

Tüm yayın yazıları alınır ve çift döngüyle (outer_index, inner_index) belirlenen batch boyutunda karşılaştırma yapılır.
Seçilen arama yöntemine göre (başlık, içerik veya her ikisi) benzerlik hesaplanır (PHP’nin similar_text fonksiyonu kullanılarak).
Hesaplanan benzerlik, kullanıcı tarafından belirlenen eşiği aşarsa, sonuç (iki yazı için başlık, içerik özeti, benzerlik yüzdesi ve düzenleme/silme linkleri) HTML olarak üretilir ve AJAX yanıtında gönderilir.
İlerleme bilgisi (global indeksler) de gönderilir; tüm yazılar tarandığında finished true döner.
JS Tarafı:

Global indeksler tutulur ve batch batch AJAX çağrısı yapılır.
Her AJAX çağrısında gelen sonuçlar anlık olarak sonuç tablosuna eklenir.
Kullanıcı “Sorgulamayı Durdur” butonuna tıklarsa işlem iptal edilir.
Bu yapı sayesinde, kullanıcı herhangi bir arama terimi girmeden, sadece hangi alana göre arama yapılacağını ve benzeşme eşiğini belirleyerek, veritabanındaki tüm yayın yazılarında benzerlik tespiti yapabilir ve eşleşen sonuçları anlık olarak listeleyebilir.







Re: aynı yada benzeyen yazıları süzme

Gönderilme zamanı: Sal Şub 18, 2025 6:29 pm
gönderen muratca61
bu şekilde işlem çok uzun sürüyor. mantık olarak şöyle örneğin arama yapılacak ilk yazımızın başlığı "bugün havalar çok sıcak" ve %50 seçili ise "bugün havalar" kelime grubunu tüm veri yazıların başlığında arayacak eğer eşleşen sonuç varsa listeleyecek ve diğer başlığa geçerek aynı oranda kelime ile başlıklarda arama yapacak
24 saniye hakkında Akıl yürütüldü
Aşağıdaki örnekte, kullanıcıya hangi alanda (Başlık, İçerik veya Hem Başlık Hem İçerik) arama yapılacağını ve benzeşme eşiğini (%) seçme imkânı tanınır. Mantık şu şekilde çalışır:

Her yazı için, seçilen alanda (örneğin; başlık) yazının kelimeleri alınır.
Belirlenen eşiğe göre (örneğin %50) kelime sayısının hesaplanmasıyla, ilk N kelime (örneğin 4 kelimelik başlık için 2 kelime) “arama kelime grubu” olarak elde edilir.
Bu kelime grubunu diğer tüm yazıların ilgili alanında (seçime göre) ararız. Eğer bu metin, diğer yazının alanında bulunuyorsa eşleşme olarak listeye eklenir.
Sonuçlar, her eşleşme için; kaynak yazı, kullanılan arama kelime grubu, eşleşen hedef yazı ve düzenle/sil bağlantıları şeklinde tabloya eklenir.
Aşağıdaki kodları, örneğin eklenti klasörünüzde (ör. wp-content/plugins/Murat/)
murat-similar-search.php
adlı dosya olarak oluşturup eklentiyi aktif edebilirsiniz. Ayrıca aynı klasörde murat-similar-search.js (JS) ve opsiyonel murat-similar-search.css (CSS) dosyalarını oluşturmanız gerekiyor.
1. PHP Dosyası:

Kod:Tümünü seç

murat-similar-search.php

Kod:Tümünü seç

<?php
/*
Plugin Name: Murat Benzer Yazılar Arama (Yeni Yöntem)
Description: Kullanıcının seçimine göre (Başlık, İçerik veya Hem Başlık Hem İçerik) ve belirlenen benzeşme eşiğine göre, veritabanındaki yazıları karşılaştırarak eşleşenleri listeleyen uygulama. Her yazı için, seçilen alandan ilk N kelime (N = ceil(toplam kelime * eşik/100)) alınır ve bu kelime grubu diğer yazılarda aranır.
Version: 1.0
Author: Murat
License: GPL2
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Murat_Similar_Search_New {

    // Her AJAX çağrısında yapılacak karşılaştırma adedi (batch boyutu)
    private $batch_size = 5;

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'wp_ajax_murat_similar_query', array( $this, 'ajax_similar_query' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
        add_action( 'admin_init', array( $this, 'handle_deletion' ) );
    }

    /**
     * "Murat" başlığı altında admin menüsüne sayfa ekler.
     */
    public function add_admin_menu() {
        add_menu_page(
            'Murat',
            'Murat',
            'manage_options',
            'murat-similar-search',
            array( $this, 'admin_page' ),
            'dashicons-search',
            25
        );
    }

    /**
     * Admin sayfası için JS/CSS dosyalarını ekler.
     */
    public function enqueue_admin_scripts( $hook ) {
        if ( $hook == 'toplevel_page_murat-similar-search' ) {
            wp_enqueue_script(
                'murat-similar-search-js',
                plugin_dir_url( __FILE__ ) . 'murat-similar-search.js',
                array( 'jquery' ),
                '1.0',
                true
            );
            wp_localize_script( 'murat-similar-search-js', 'murat_similar', array(
                'ajax_url' => admin_url( 'admin-ajax.php' ),
                'nonce'    => wp_create_nonce( 'murat_similar_nonce' )
            ) );
            wp_enqueue_style(
                'murat-similar-search-css',
                plugin_dir_url( __FILE__ ) . 'murat-similar-search.css',
                array(),
                '1.0'
            );
        }
    }

    /**
     * Admin sayfasını oluşturur.
     */
    public function admin_page() {
        ?>
        <div class="wrap">
            <h1>Murat Benzer Yazılar Arama</h1>
            <form id="murat-similar-form">
                <table class="form-table">
                    <tr>
                        <th scope="row">Arama Yöntemi</th>
                        <td>
                            <label><input type="radio" name="murat_search_mode" value="title" checked> Sadece Başlık</label><br>
                            <label><input type="radio" name="murat_search_mode" value="content"> Sadece İçerik</label><br>
                            <label><input type="radio" name="murat_search_mode" value="both"> Hem Başlık Hem İçerik</label>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Benzeşme Eşiği (%)</th>
                        <td>
                            <input type="number" id="murat_threshold" name="murat_threshold" value="50" min="0" max="100" style="width:80px;">
                            <p class="description">Örneğin, %50 seçilirse, her yazının ilgili alanından alınan kelimelerin %50'si kadarını (ilk N kelime) arama metni olarak kullanır.</p>
                        </td>
                    </tr>
                </table>
                <p>
                    <button id="murat-start-btn" class="button button-primary" type="button">Sorgulamayı Başlat</button>
                    <button id="murat-stop-btn" class="button" type="button" disabled>Sorgulamayı Durdur</button>
                </p>
            </form>
            <hr>
            <div id="murat-similar-progress">
                <!-- Anlık ilerleme bilgisi burada gösterilecek -->
            </div>
            <div id="murat-similar-results">
                <!-- Eşleşen yazı çiftleri burada listelenecek -->
                <table class="widefat striped">
                    <thead>
                        <tr>
                            <th>Kaynak Yazı</th>
                            <th>Arama Kelime Grubu</th>
                            <th>Eşleşen Yazı</th>
                            <th>Benzerlik (%)</th>
                            <th>İşlemler</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </div>
        </div>
        <?php
    }

    /**
     * Belirlenen metinden, eşik oranına göre (yüzde) ilk N kelimeyi döndürür.
     *
     * @param string $text
     * @param float $threshold (örneğin 50 için %50)
     * @return string Arama kelime grubu
     */
    private function get_search_phrase( $text, $threshold ) {
        $text = trim( $text );
        if ( empty( $text ) ) {
            return '';
        }
        // Kelimeleri ayır (birden fazla boşluk, newline vb. göz önünde bulundurulur)
        $words = preg_split( '/\s+/', $text );
        $total = count( $words );
        if ( $total == 0 ) {
            return '';
        }
        $num = ceil( $total * ($threshold/100) );
        // En az 1 kelime olsun:
        $num = max( 1, $num );
        $phrase = implode( ' ', array_slice( $words, 0, $num ) );
        return $phrase;
    }

    /**
     * AJAX: Belirlenen arama yöntemi ve eşik değerine göre, veritabanındaki yazıları karşılaştırarak benzerlik tespitini batch olarak yapar.
     *
     * Beklenen POST parametreleri: outer_index, inner_index, search_mode, threshold
     */
    public function ajax_similar_query() {
        check_ajax_referer( 'murat_similar_nonce', 'nonce' );

        $outer_index = isset( $_POST['outer_index'] ) ? intval( $_POST['outer_index'] ) : 0;
        $inner_index = isset( $_POST['inner_index'] ) ? intval( $_POST['inner_index'] ) : 0;
        $search_mode = isset( $_POST['search_mode'] ) ? sanitize_text_field( $_POST['search_mode'] ) : 'title';
        $threshold   = isset( $_POST['threshold'] ) ? floatval( $_POST['threshold'] ) : 50;

        // Tüm yayın yazıları alınıyor (ID, başlık, içerik)
        $posts = get_posts( array(
            'numberposts' => -1,
            'post_status' => 'publish',
            'post_type'   => 'post',
            'orderby'     => 'ID',
            'order'       => 'ASC'
        ) );
        $n = count( $posts );
        if ( $n < 2 ) {
            wp_send_json_error( array( 'message' => 'Yeterli yazı bulunamadı.' ) );
        }

        $result_html = '';
        // Belirlenen batch_size kadar karşılaştırma yapalım
        for ( $i = 0; $i < $this->batch_size; $i++ ) {

            // Tüm karşılaştırmalar bitti mi?
            if ( $outer_index >= $n - 1 ) {
                wp_send_json_success( array(
                    'html'        => $result_html,
                    'finished'    => true,
                    'outer_index' => $outer_index,
                    'inner_index' => $inner_index
                ) );
            }

            // İlgili iki yazı:
            $source = $posts[ $outer_index ];
            $target = $posts[ $inner_index ];

            // Aynı yazıyı karşılaştırmayalım
            if ( $source->ID == $target->ID ) {
                $inner_index++;
                if ( $inner_index >= $n ) {
                    $outer_index++;
                    $inner_index = $outer_index + 1;
                }
                continue;
            }

            // Seçilen yönteme göre arama kelime grubunu belirleyelim:
            if ( 'title' === $search_mode ) {
                $phrase = $this->get_search_phrase( $source->post_title, $threshold );
                $haystack = $target->post_title;
            } elseif ( 'content' === $search_mode ) {
                $phrase = $this->get_search_phrase( wp_strip_all_tags( $source->post_content ), $threshold );
                $haystack = wp_strip_all_tags( $target->post_content );
            } else { // both
                // Hem başlık hem içerik: birleşik metin
                $combined = $source->post_title . ' ' . wp_strip_all_tags( $source->post_content );
                $phrase = $this->get_search_phrase( $combined, $threshold );
                // Hedefte de birleşik metin
                $haystack = $target->post_title . ' ' . wp_strip_all_tags( $target->post_content );
            }

            // Arama kelime grubu boş ise atla
            if ( empty( $phrase ) ) {
                // İleriye geçelim:
                $inner_index++;
                if ( $inner_index >= $n ) {
                    $outer_index++;
                    $inner_index = $outer_index + 1;
                }
                continue;
            }

            // Basit substring araması (case-insensitive)
            if ( stripos( $haystack, $phrase ) !== false ) {
                // Eşleşme bulundu; benzerlik olarak, burada kelime grubunun uzunluğunu (%) gösterebiliriz.
                // Örneğin: (kelime grubu kelime sayısı / kaynak kelime sayısı)*100
                $source_words = preg_split( '/\s+/', trim( $source->post_title ) );
                $source_word_count = count( $source_words );
                $phrase_word_count = count( preg_split( '/\s+/', trim($phrase) ) );
                $similarity = ( $phrase_word_count / $source_word_count ) * 100;
                $similarity = round( $similarity, 2 );
                
                $result_html .= '<tr>';
                $result_html .= '<td><strong>' . esc_html( $source->post_title ) . '</strong></td>';
                $result_html .= '<td>' . esc_html( $phrase ) . '</td>';
                $result_html .= '<td><strong>' . esc_html( $target->post_title ) . '</strong></td>';
                $result_html .= '<td>' . $similarity . '%</td>';
                $result_html .= '<td>';
                $result_html .= '<a href="' . esc_url( get_edit_post_link( $source->ID ) ) . '" target="_blank">Düzenle 1</a> | ';
                $result_html .= '<a href="' . esc_url( $this->get_delete_link( $source->ID ) ) . '" onclick="return confirm(\'Bu yazıyı silmek istediğinize emin misiniz?\');">Sil 1</a><br>';
                $result_html .= '<a href="' . esc_url( get_edit_post_link( $target->ID ) ) . '" target="_blank">Düzenle 2</a> | ';
                $result_html .= '<a href="' . esc_url( $this->get_delete_link( $target->ID ) ) . '" onclick="return confirm(\'Bu yazıyı silmek istediğinize emin misiniz?\');">Sil 2</a>';
                $result_html .= '</td>';
                $result_html .= '</tr>';
            }

            // İleriye geçelim: inner_index++
            $inner_index++;
            if ( $inner_index >= $n ) {
                $outer_index++;
                $inner_index = $outer_index + 1;
            }
        }

        wp_send_json_success( array(
            'html'        => $result_html,
            'finished'    => false,
            'outer_index' => $outer_index,
            'inner_index' => $inner_index
        ) );
    }

    /**
     * Belirtilen yazı ID'si için silme bağlantısı üretir (nonce ile korunur).
     */
    private function get_delete_link( $post_id ) {
        $nonce = wp_create_nonce( 'delete_post_' . $post_id );
        $url = add_query_arg(
            array(
                'page'       => 'murat-similar-search',
                'action'     => 'delete_post',
                'post_id'    => $post_id,
                'murat_nonce'=> $nonce,
            ),
            admin_url( 'admin.php' )
        );
        return $url;
    }

    /**
     * GET üzerinden gelen silme isteğini işler.
     */
    public function handle_deletion() {
        if ( isset( $_GET['action'] ) && $_GET['action'] == 'delete_post' && isset( $_GET['post_id'] ) && isset( $_GET['murat_nonce'] ) ) {
            $post_id = intval( $_GET['post_id'] );
            if ( wp_verify_nonce( $_GET['murat_nonce'], 'delete_post_' . $post_id ) ) {
                wp_delete_post( $post_id, true );
                wp_redirect( admin_url( 'admin.php?page=murat-similar-search' ) );
                exit;
            }
        }
    }
}

new Murat_Similar_Search_New();
2. JavaScript Dosyası:

Kod:Tümünü seç

murat-similar-search.js

Kod:Tümünü seç

jQuery(document).ready(function($) {
    var stopQuery = false;
    // Global indeksler; tüm yazılar üzerinden çift döngü
    var outer_index = 0;
    var inner_index = 1;

    // Sorgulamayı başlat butonuna tıklanınca:
    $('#murat-start-btn').on('click', function(e) {
        e.preventDefault();
        stopQuery = false;
        outer_index = 0;
        inner_index = 1;
        var threshold = $('#murat_threshold').val();
        var search_mode = $('input[name="murat_search_mode"]:checked').val();

        $('#murat-start-btn').attr('disabled', true);
        $('#murat-stop-btn').attr('disabled', false);
        $('#murat-similar-progress').html('<p>Sorgulama başlatılıyor…</p>');
        // Sonuçlar tablosındaki mevcut satırlar sıfırlanıyor.
        $('#murat-similar-results table tbody').html('');
        startQuery(threshold, search_mode);
    });

    // Sorgulamayı durdur butonuna tıklanınca:
    $('#murat-stop-btn').on('click', function(e) {
        e.preventDefault();
        stopQuery = true;
        $('#murat-start-btn').attr('disabled', false);
        $('#murat-stop-btn').attr('disabled', true);
        $('#murat-similar-progress').append('<p>Sorgulama durduruldu.</p>');
    });

    function startQuery(threshold, search_mode) {
        if ( stopQuery ) {
            return;
        }
        $.post(murat_similar.ajax_url, {
            action: 'murat_similar_query',
            nonce: murat_similar.nonce,
            outer_index: outer_index,
            inner_index: inner_index,
            threshold: threshold,
            search_mode: search_mode
        }, function(response) {
            if ( response.success ) {
                if ( response.data.html ) {
                    // Yeni sonuç satırları mevcut tabloya eklenir.
                    $('#murat-similar-results table tbody').append(response.data.html);
                }
                outer_index = response.data.outer_index;
                inner_index = response.data.inner_index;
                if ( response.data.finished ) {
                    $('#murat-similar-progress').html('<p>Sorgulama tamamlandı.</p>');
                    $('#murat-start-btn').attr('disabled', false);
                    $('#murat-stop-btn').attr('disabled', true);
                } else {
                    $('#murat-similar-progress').html('<p>Devam ediyor… (outer_index: ' + outer_index + ', inner_index: ' + inner_index + ')</p>');
                    setTimeout(function() {
                        startQuery(threshold, search_mode);
                    }, 500);
                }
            } else {
                $('#murat-similar-progress').html('<p>Hata: ' + response.data.message + '</p>');
                $('#murat-start-btn').attr('disabled', false);
                $('#murat-stop-btn').attr('disabled', true);
            }
        });
    }
});
3. CSS Dosyası (Opsiyonel):

Kod:Tümünü seç

murat-similar-search.css

Kod:Tümünü seç

/* Basit stil düzenlemeleri */
#murat-similar-results {
    margin-top: 20px;
}
#murat-similar-results table {
    width: 100%;
}
#murat-similar-results th,
#murat-similar-results td {
    padding: 10px;
    text-align: left;
}
Açıklamalar
PHP Tarafı:

Admin sayfasında kullanıcı, “Arama Yöntemi” (radio butonları) ve “Benzeşme Eşiği (%)” değerini seçer.
AJAX çağrısında, tüm yayın yazıları alınır. Her seferinde global indeksler (outer_index, inner_index) kullanılarak çift döngü şeklinde ilerlenir.
Seçilen alana göre (title, content veya both) ilgili yazının metninden, eşik değerine göre ilk N kelime (örn. %50 için ilk 50%'si) "arama kelime grubu" olarak belirlenir.
Bu kelime grubu, diğer yazıların ilgili alanında (case-insensitive) aranır (stripos kullanılarak).
Eşleşme varsa, kaynak yazı, aranan kelime grubu, eşleşen hedef yazı, hesaplanan benzerlik yüzdesi (basit oran) ve düzenle/sil linkleri tabloya eklenir.
İşlem batch’ler halinde AJAX ile çalışır; “Sorgulamayı Durdur” butonu ile işlem iptal edilebilir.
JS Tarafı:

Global indeksler tutulur ve her AJAX çağrısı belirlenen batch boyutunda (örneğin 5 karşılaştırma) işlem yapar.
Gelen sonuçlar anlık olarak sonuç tablosuna eklenir ve ilerleme bilgisi güncellenir.
Bu yapı sayesinde, kullanıcı herhangi bir arama terimi girmeden yalnızca hangi alanda arama yapılacağını ve benzeşme eşiğini belirleyip; veritabanındaki tüm yazılar arasında, örneğin; ilk yazının başlığı "bugün havalar çok sıcak" ise ve %50 seçili ise "bugün havalar" kelime grubu diğer yazıların başlıklarında aranır. Eşleşenler anlık olarak listelenir. Böylece, benzerlik kontrolü daha hızlı (substring araması) ve istenildiği gibi satır satır (anlık) görüntülenir.