Apakah mungkin untuk benar-benar menghentikan WP_Query mengambil kiriman?

8

Saya mencoba menggunakan WP Redis untuk me- cache seluruh objek $ wp_query dengan kuncinya adalah $ query_vars_hash .

Ini adalah bagaimana $wp_queryditambahkan ke $wp_object_cache:

add_action('wp', function($wp)
{
    if ( is_admin() ) return;

    global $wp_query;

    if ( !wp_cache_get($wp_query->query_vars_hash, 'globals') )
    {
        wp_cache_add($wp_query->query_vars_hash, $wp_query, 'globals');
    }
});

Lalu, saya perlu memeriksa apakah kueri telah di-cache sebelum WP_Querydapat mengambil posting:

add_action('pre_get_posts', function($query)
{
    if ( is_admin() ) return;

    $cached_query = wp_cache_get($query->query_vars_hash, 'globals');

    if ($cached_query)
    {
        $GLOBALS['wp_query'] = &$cached_query;

        return; // Return immediately to prevent retrieving posts again.
    }
});

Masalah :

returnatau exittidak berfungsi dalam kasus ini. Kemudian, WP_Querymasih akan mencapai database untuk mengambil posting lagi

Pertanyaan :

Terlepas dari plugin, apakah mungkin untuk sepenuhnya berhenti WP_Querymengambil posting?

SarahCoding
sumber
Saya merasa plugin harus menangani ini ... Apakah Anda yakin Anda akan melakukannya dengan cara yang benar? Sudahkah Anda bertanya tentang ini di forum mereka? Tentang masalah github mereka?
Howdy_McGee
@Howdy_McGee plugin menggunakan fungsi yang sama dengan API caching WordPress standar . Satu-satunya perbedaan adalah itu membantu terhubung ke server Redis. Tentu saja, saya juga berusaha mencari jalan yang benar.
SarahCoding
tidak yakin mengapa Anda berpikir bahwa kueri tidak akan menyala. kembali dari aksinya jangan kembali dengan sihir dari fungsi pemanggilan
Mark Kaplun
@ MarkKaplun Saya juga menggandakan tentang itu tetapi returnmungkin satu-satunya perintah yang bisa kita panggil dalam kasus ini.
SarahCoding
@Dan, saya tidak mengerti apa yang Anda asumsikan, Anda jelas mengasumsikan sesuatu yang tidak benar, mungkin di level php
Mark Kaplun

Jawaban:

11

Saat ini, itu tidak mungkin.

Saat 'pre_get_posts'berjalan, sudah terlambat untuk berhenti WP_Querymelakukan kueri.

WordPress sendiri, ketika Anda mencoba untuk query taksonomi yang tidak ada, menambahkan AND (0 = 1)ke WHEREklausul dari query SQL, untuk memastikan tidak mengembalikan hasil yang sangat cepat ...

Ada tiket trac dengan patch yang mungkin akan mendarat di inti dengan WP 4.6, yang memperkenalkan filter baru: 'posts_pre_query'. Mengembalikan array pada filter itu akan membuat WP_Queryberhenti memproses dan menggunakan array yang disediakan sebagai array postingnya.

Ini bisa membantu Anda dalam mengimplementasikan apa yang Anda coba lakukan.

Menunggu ini, apa pun yang bisa Anda lakukan adalah entah bagaimana meretas , inti trik itu sendiri menggunakan juga cukup meretas.

Baru-baru ini, saya mulai menggunakan trik ketika saya ingin menghentikan WordPress untuk melakukan hal-hal yang saya tidak bisa berhenti dengan cara yang bersih: Saya melemparkan pengecualian dan menangkapnya untuk melanjutkan aliran aplikasi.

Saya akan tunjukkan contoh. Perhatikan bahwa semua kode di sini benar-benar belum teruji.

Pertama-tama, mari kita tulis pengecualian khusus:

class My_StopWpQueryException extends Exception {

   private $query;

   public static forQuery(WP_Query $query) {
     $instance = new static();
     $instance->query = $query;

     return $instance;
   }

   public function wpQuery() {
     return $this->query;
   }
}

Pengecualian dirancang untuk bertindak sebagai semacam DTO untuk mengangkut objek kueri, sehingga dalam catchblok Anda bisa mendapatkan dan menggunakannya.

Lebih baik dijelaskan dengan kode:

function maybe_cached_query(WP_Query $query) {
    $cached_query = wp_cache_get($query->query_vars_hash, 'globals');
    if ($cached_query instanceof WP_Query)
       throw My_StopWpQueryException::forQuery($cached_query);
}

function cached_query_set(WP_Query $query) {
    $GLOBALS['wp_query'] = $query;
    $GLOBALS['wp_the_query'] = $query;
    // maybe some more fine-tuning here...
}

add_action('pre_get_posts', function(WP_Query $query) {
    if ($query->is_main_query() && ! is_admin()) {
        try {
           maybe_cached_query($query);
        } catch(My_StopWpQueryException $e) {
           cached_query_set($e->wpQuery());
        }
    }
});

Ini seharusnya lebih atau kurang berfungsi, namun, ada banyak kait yang tidak akan Anda aktifkan, misalnya "the_posts"dan banyak lagi ... jika Anda memiliki kode yang menggunakan salah satu kait untuk memicu, itu akan rusak.

Anda dapat menggunakan cached_query_setfungsi ini untuk mengaktifkan beberapa kait yang mungkin diperlukan tema / plugin Anda.

gmazzap
sumber
Mengapa tidak bekerja dengan kelas pengecualian default? Ini menunjukkan kepada saya kesalahan pengecualian yang tidak tertangkap?
Sumit
Ini harus bekerja dengan pengecualian standar dan properti publik, tetapi Anda harus menangkap pengecualian standar jika Anda melemparkannya @Sumit
gmazzap
Yah saya melakukannya hanya dengan menggunakan contoh ini . Tapi saya mendapatkan kesalahan pengecualian tanpa tertangkap. Saya menjalankan contoh pengecualian paling sederhana tetapi sepertinya do_actionharus di tryblok.
Sumit
Pendekatan menarik yang dapat diterapkan di berbagai tempat di WordPress, saya akan mengingatnya ;-) ps: DTO = Obyek Transfer Data ?
birgire
@Birgire ya :)
gmazzap
2

Ini adalah pertanyaan PHP lebih dari pertanyaan WordPress.

Seperti yang dikomentari @Mark :

kembali dari aksi tidak kembali dengan sihir dari fungsi panggilan

Itu benar. Menempatkan returnfungsi berarti keluar dari fungsi dan menempatkan kembali dalam file PHP berarti keluar dari file. Jangan bingung dengan konstruk PHP exit(): P (Anda mungkin menemukan jawaban yang lebih baik tentang SO tentang PHP return).

Dan untuk menjawab pertanyaan Anda

Anda dapat mengurangi beban kueri dengan mengambil satu kolom, bukan tabel lengkap. Seperti @birgire lakukan di sini Hapus Permintaan Beranda

Mungkin jawaban yang lebih baik belum datang. Saya baru saja membagikan apa yang saya tahu :)

Sumit
sumber
1
@Apakah Anda mendapatkan banyak hit basis data setelah menetralkan permintaan permintaan melalui posts_requestfilter? Dengan itu + pendekatan satu kolom kita keluar WP_Querylebih awal daripada menggunakan posts_pre_queryfilter .. Juga hati-hati untuk posting lengket dengan posts_pre_querytetapi kita dapat menghapusnya dengan $q->set( 'ignore_sticky_posts', 1 );misalnya dalam contoh di sini .
birgire
@ Borgire Sepertinya posts_pre_querytidak membantu. Solusi Anda adalah yang terbaik sejauh ini. :) Jika Anda tahu bagaimana kami dapat keluar dari kueri segera pre_get_posts, itu bisa jadi hebat. Terima kasih!
SarahCoding
@Dan posts_pre_queryakan tersedia mulai 4,6;)
Sumit
Pendekatan lain yang muncul dalam pikiran adalah mencoba memperluas WP_Querykelas dengan get_posts()metode kustom , dengan kemungkinan awal yang ada dan yang memanggil parent::get_posts() dan mencoba untuk menimpa kueri yang relevan dengannya. Tapi saya tidak tahu apakah itu akan berhasil atau masuk akal dengan kasus Anda di sini ;-)
@Dan
1
Mungkin sedikit Aerosmith - Livin 'On The Edge dapat membantu ;-)
@Dan
2

Ini akan dimungkinkan di 4.6 (dengan asumsi tidak ada perubahan sampai rilis) dengan posts_pre_queryfilter baru https://core.trac.wordpress.org/ticket/36687

Mark Kaplun
sumber
@Dan, inilah yang terjadi ketika Anda ingin menyelesaikan pemikiran yang Anda miliki sebelum tidur dan tidak membaca jawaban lain terlebih dahulu;)
Mark Kaplun
Bro, sudah terlambat sekarang. Saya akan membaca jawaban itu nanti ;-)
SarahCoding
2

Yap itu mungkin tergantung pada apa yang ingin Anda cache. Saya telah melakukan hal yang sama untuk men-cache loop utama di beranda kami Pada dasarnya Anda dapat menggunakan posts_requestdan posts_resultsuntuk membajak kueri dan menekan cache, alih-alih juga digunakan found_postsuntuk memperbaiki pagination.

Contoh yang sangat kasar diambil dari kode kami (belum diuji) tetapi Anda harus membantu Anda mendapatkan ide:

<?php
/**
 * Kill the query if we have the result in the cache
 * @var [type]
 */
add_filter( 'posts_request', function( $request, $query ) {
    if ( is_home() && $query->is_main_query() ) {

        $page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

        $key = 'homepage_query_cache_' . $page;

        if ( wp_cache_get( $key, 'cache_group' ) )
            $request = null;

    }

    return $request;
}, 10, 2 );

/**
 * Get the result from the cache and set it as the query result
 * Or add the query result to the cache if it's not there
 * @var [type]
 */
add_filter( 'posts_results', function( $posts, $query ) {

    if ( is_home() && $query->is_main_query() ) {

        $page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

        $key = 'homepage_query_cache_' . $page;

        if ( $cached_posts = wp_cache_get( $key, 'cache_group' ) ) {
            $posts = $cached_posts;
        } else {
            wp_cache_set( $key . '_found_posts', $query->found_posts, 'cache_group', HOUR_IN_SECONDS );
            wp_cache_set( $key, $posts, 'cache_group', HOUR_IN_SECONDS );
        }
    }

    return $posts;

}, 10, 2 );

/**
 * Correct the found posts number if we've hijacked the query results
 * @var [type]
 */
add_filter( 'found_posts', function( $num, $query ) {
    if ( is_home() && $query->is_main_query() ) {
        $page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

        $key = 'homepage_query_cache_' . $page;

        if ( $found_posts = wp_cache_get( $key . '_found_posts', 'cache_group' ) )
            $num = $found_posts;
    }

    return $num;
}, 10, 2 );

Lebih lanjut di sini: https://www.reddit.com/r/Wordpress/comments/19crcn/best_practice_for_hijacking_main_loop_and_caching/

OzTheGreat
sumber