Có thể dừng hoàn toàn việc truy xuất WP_Query không?


8

Tôi đang cố gắng sử dụng WP Redis để lưu trữ toàn bộ đối tượng $ wp_query với khóa là $ query_vars_hash .

Đây là cách $wp_queryđã được thêm vào $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');
    }
});

Sau đó, tôi cần kiểm tra xem một truy vấn đã được lưu trong bộ nhớ cache chưa trước khi WP_Querycó thể truy xuất các bài đăng:

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.
    }
});

Vấn đề :

returnhoặc exitkhông hoạt động trong trường hợp này. Sau đó, WP_Queryvẫn sẽ nhấn cơ sở dữ liệu để lấy lại bài viết.

Câu hỏi :

Bất kể các plugin, có thể hoàn toàn ngừng WP_Querylấy bài viết?


Tôi cảm thấy như plugin nên xử lý việc này ... Bạn có chắc là bạn đang đi đúng hướng? Bạn đã hỏi về điều này trên diễn đàn của họ? Về vấn đề github của họ?
Howdy_McGee

@Howdy_McGee plugin sử dụng các chức năng tương tự như API bộ đệm ẩn mặc định của WordPress . Sự khác biệt duy nhất là nó giúp kết nối với máy chủ Redis. Tất nhiên, tôi cũng đang cố gắng tìm đúng cách.
SarahCoding

không chắc chắn tại sao bạn nghĩ rằng truy vấn sẽ không kích hoạt. trở về từ hành động không trở lại bằng phép thuật từ chức năng gọi
Mark Kaplun

@MarkKaplun Tôi cũng nhân đôi về điều đó nhưng returncó thể là lệnh duy nhất chúng ta có thể gọi trong trường hợp này.
SarahCoding

@Dan, tôi không hiểu những gì bạn giả định, rõ ràng bạn cho rằng điều gì đó không đúng, có lẽ ở cấp độ php
Mark Kaplun

Câu trả lời:


11

Hiện tại, điều đó là không thể.

Khi 'pre_get_posts'chạy, quá muộn để dừng lại WP_Queryđể thực hiện một truy vấn.

Bản thân WordPress, khi bạn cố gắng truy vấn một nguyên tắc phân loại không tồn tại, sẽ thêm AND (0 = 1)vào WHEREmệnh đề của truy vấn SQL, để đảm bảo nó không trả về kết quả nhanh chóng ...

Có một vé trac với một bản vá có thể sẽ rơi vào cốt lõi với WP 4.6, giới thiệu một bộ lọc mới : 'posts_pre_query'. Trả về một mảng trên bộ lọc đó sẽ khiến việc WP_Querydừng xử lý và sử dụng mảng được cung cấp như mảng bài viết của nó.

Điều này bằng cách nào đó có thể giúp bạn trong việc thực hiện những gì bạn đang cố gắng làm.

Chờ đợi điều này, bất cứ điều gì bạn có thể làm là bằng cách nào đó hackish , bản thân lõi lừa sử dụng cũng khá hackish.

Gần đây, tôi bắt đầu sử dụng một mẹo khi tôi muốn dừng WordPress để làm những việc mà tôi không thể dừng lại một cách sạch sẽ: Tôi ném một ngoại lệ và bắt nó để tiếp tục dòng ứng dụng.

Tôi sẽ chỉ cho bạn một ví dụ. Lưu ý tất cả các mã ở đây là hoàn toàn chưa được kiểm tra.

Trước hết, hãy viết một ngoại lệ tùy chỉnh:

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;
   }
}

Ngoại lệ được thiết kế để hoạt động như một loại DTO để vận chuyển một đối tượng truy vấn, để trong một catchkhối bạn có thể lấy và sử dụng nó.

Giải thích rõ hơn với mã:

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());
        }
    }
});

Điều này sẽ ít nhiều hoạt động, tuy nhiên, có rất nhiều hook mà bạn sẽ không kích hoạt, ví dụ "the_posts"và nhiều hơn nữa ... nếu bạn có mã sử dụng một trong những hook đó để kích hoạt, nó sẽ bị hỏng.

Bạn có thể sử dụng cached_query_setchức năng để bắn một số móc mà chủ đề / plugin của bạn có thể yêu cầu.


Tại sao không hoạt động với lớp ngoại lệ mặc định? Nó cho tôi thấy một lỗi của ngoại lệ chưa bị bắt?
Sumit

Nó nên hoạt động với ngoại lệ tiêu chuẩn và tài sản công cộng, nhưng bạn cần nắm bắt ngoại lệ tiêu chuẩn nếu bạn ném nó @Sumit
gmazzap

Vâng, tôi đã làm nó chỉ bằng cách sử dụng ví dụ này . Nhưng tôi nhận được lỗi ngoại lệ. Tôi đã chạy ví dụ đơn giản nhất về ngoại lệ nhưng có vẻ như do_actionnên ở trong trykhối.
Sumit

Cách tiếp cận thú vị có thể được áp dụng ở nhiều nơi trong WordPress, tôi sẽ ghi nhớ nó ;-) ps: DTO = Đối tượng truyền dữ liệu ?
bạch dương

@birgire có :)
gmazzap

2

Đây là câu hỏi PHP nhiều hơn một câu hỏi WordPress.

Như @Mark đã nhận xét:

trở về từ hành động không trở lại bằng phép thuật từ chức năng gọi

Điều đó đúng. Đặt returnvào hàm có nghĩa là thoát khỏi hàm và đặt return trong tệp PHP nghĩa là thoát tệp. Đừng nhầm lẫn với cấu trúc PHP exit(): P (Bạn có thể tìm thấy câu trả lời tốt hơn về SO về PHP return).

Và để trả lời câu hỏi của bạn

Bạn có thể giảm tải truy vấn bằng cách tìm nạp một cột duy nhất thay vì bảng đầy đủ. Giống như @birgire đã làm ở đây Xóa Truy vấn Trang chủ

Có thể là một câu trả lời tốt hơn chưa đến. Tôi chỉ chia sẻ rằng những gì tôi biết :)


1
@Dan bạn có nhận được nhiều lượt truy cập cơ sở dữ liệu sau khi vô hiệu hóa yêu cầu truy vấn qua posts_requestbộ lọc không? Với cách tiếp cận + cột đơn đó, chúng tôi thoát WP_Querysớm hơn so với sử dụng posts_pre_querybộ lọc .. Ngoài ra, hãy coi chừng các bài đăng dính với posts_pre_querynhưng chúng tôi có thể xóa nó bằng $q->set( 'ignore_sticky_posts', 1 );ví dụ ở đây .
bạch dương

@birgire Hình như posts_pre_querykhông giúp được gì. Giải pháp của bạn là tốt nhất cho đến nay. :) Nếu bạn biết làm thế nào chúng ta có thể thoát truy vấn ngay sau pre_get_postsđó, điều đó có thể là tuyệt vời. Cảm ơn bạn!
SarahCoding

@Dan posts_pre_querysẽ có sẵn từ 4.6;)
Sumit

Một cách tiếp cận khác xuất hiện trong đầu là thử mở rộng WP_Querylớp bằng một get_posts()phương thức tùy chỉnh , với sự tồn tại sớm có thể và đó gọi parent::get_posts() và cố gắng ghi đè truy vấn có liên quan với nó. Nhưng tôi không biết điều đó có hiệu quả hay hợp lý với trường hợp của bạn ở đây không ;-)
@Dan

1
Có lẽ một chút Aerosmith - Livin 'On The Edge có thể giúp với điều đó ;-)
@Dan

2

Nó sẽ được thực hiện trong 4.6 (giả sử không có thay đổi nào cho đến khi phát hành) với posts_pre_querybộ lọc mới https://core.trac.wordpress.org/ticket/36687


@Dan, đây là những gì xảy ra khi bạn muốn hoàn thành suy nghĩ bạn đã có trước khi đi ngủ và không đọc các câu trả lời khác trước;)
Mark Kaplun

Bro, bây giờ đã quá muộn. Tôi sẽ đọc những câu trả lời sau ;-)
SarahCoding

2

Đúng là có thể tùy thuộc vào những gì bạn muốn lưu trữ. Tôi đã làm một điều tương tự để lưu trữ vòng lặp chính trên trang chủ của chúng tôi. Về cơ bản, bạn có thể sử dụng posts_requestposts_resultsđể chiếm quyền điều khiển truy vấn và nhấn bộ đệm thay vào đó sau đó cũng sử dụng found_postsđể sửa lỗi phân trang.

Ví dụ thực sự thô sơ được lấy từ mã của chúng tôi (chưa được kiểm tra) nhưng bạn sẽ giúp bạn có được ý tưởng:

<?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 );

Xem thêm tại đây: https://www.reddit.com/r/Wordpress/comments/19crcn/best_practice_for_hijacking_main_loop_and_caching/

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.