È possibile interrompere completamente il recupero dei post da WP_Query?


8

Sto cercando di utilizzare WP Redis per memorizzare nella cache l'intero oggetto $ wp_query con chiave $ query_vars_hash .

Ecco come è $wp_querystato aggiunto a $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');
    }
});

Quindi, devo verificare se una query è già stata memorizzata nella cache prima di WP_Querypoter recuperare i post:

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

Problema :

returno exitnon funziona in questo caso. Quindi, WP_Querycontinuerà a colpire il database per recuperare nuovamente i post.

Domanda :

Indipendentemente dal plug-in, è possibile interrompere completamente il WP_Queryrecupero dei post?


Sento che il plugin dovrebbe gestire questo ... Sei sicuro di fare questo nel modo giusto? Hai chiesto di questo sui loro forum? Sui loro problemi github?
Howdy_McGee

@Howdy_McGee il plug-in utilizza le stesse funzionalità dell'API di cache di WordPress predefinita . L'unica differenza è che aiuta a connettersi al server Redis. Certo, sto anche cercando di trovare la strada giusta.
SarahCoding

non so perché pensi che la query non si attiverà. il ritorno dall'azione non ritorna per magia dalla funzione chiamante
Mark Kaplun,

@MarkKaplun Ho anche raddoppiato, ma returnpotrebbe essere l'unico comando che possiamo chiamare in questo caso.
SarahCoding

@Dan, non capisco cosa assumi, ovviamente presumi qualcosa che non è vero, probabilmente a livello di php
Mark Kaplun

Risposte:


11

Al momento non è possibile.

Quando 'pre_get_posts'viene eseguito, è troppo tardi per interrompere WP_Queryl'esecuzione di una query.

Lo stesso WordPress, quando si tenta di interrogare una tassonomia che non esiste, si aggiunge AND (0 = 1)alla WHEREclausola della query SQL, per assicurarsi che non restituisca risultati molto rapidamente ...

C'è un biglietto di trac con una patch che probabilmente terre nel core con WP 4.6, che introduce un nuovo filtro: 'posts_pre_query'. La restituzione di un array su quel filtro WP_Queryinterromperà l'elaborazione e utilizzerà l'array fornito come array di post.

Questo potrebbe in qualche modo aiutarti a implementare ciò che stai cercando di fare.

In attesa di ciò, tutto ciò che potresti fare è in qualche modo hacker , il trucco stesso stesso è anche abbastanza hacker.

Di recente, sto iniziando a usare un trucco quando voglio fermare WordPress per fare cose che non riesco a fermare in modo pulito: lancio un'eccezione e la prendo per continuare il flusso dell'applicazione.

Ti faccio un esempio. Nota tutto il codice qui è completamente non testato.

Prima di tutto, scriviamo un'eccezione personalizzata:

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

L'eccezione è progettata per agire come una sorta di DTO per trasportare un oggetto query, in modo che in un catchblocco sia possibile ottenerlo e utilizzarlo.

Meglio spiegato con il codice:

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

Questo dovrebbe funzionare più o meno, tuttavia, ci sono molti hook che non si "the_posts"desidera attivare , ad esempio e molto altro ... se si dispone di codice che utilizza uno di questi hook per attivarsi, si romperà.

È possibile utilizzare la cached_query_setfunzione per attivare alcuni degli hook che potrebbero essere richiesti dal proprio tema / plugin.


Perché non funziona con la classe di eccezioni predefinita? Mi mostra un errore di eccezione non rilevata?
Sumit

Dovrebbe funzionare con un'eccezione standard e una proprietà pubblica, ma devi catturare l'eccezione standard se la lanci @Sumit
gmazzap

Bene, l'ho fatto solo usando questo esempio. Ma ricevo un errore di eccezione non rilevato. Ho eseguito l'esempio più semplice di eccezione, ma sembra che do_actiondovrebbe essere in tryblocco.
Sumit

Approccio interessante che può essere applicato in vari luoghi in WordPress, lo terrò a mente ;-) ps: DTO = Data Transfer Object ?
birgire,

@birgire yes :)
gmazzap

2

Questa è una domanda PHP più che una domanda WordPress.

Come ha commentato @Mark :

il ritorno dall'azione non ritorna per magia dalla funzione chiamante

Questo è vero. Mettere returnin funzione significa uscire dalla funzione e posizionare il ritorno in un file PHP significa uscire dal file. Non confonderti con il costrutto PHP exit(): P (potresti trovare una risposta migliore su SO su PHP return).

E per rispondere alla tua domanda

È possibile ridurre il carico della query recuperando una singola colonna anziché l'intera tabella. Come ha fatto @birgire qui Rimuovere la query sulla home page

Potrebbe essere una risposta migliore ancora a venire. Ho appena condiviso quello che so :)


1
@Dan hai ricevuto molti hit dal database dopo aver neutralizzato la richiesta di query tramite il posts_requestfiltro? Con quell'approccio + colonna singola usciamo WP_Queryprima dell'uso del posts_pre_queryfiltro .. Fai attenzione anche ai post appiccicosi con posts_pre_queryma possiamo rimuoverli con $q->set( 'ignore_sticky_posts', 1 );ad esempio nell'esempio qui .
birgire,

@birgire Sembra posts_pre_queryche non aiuti. La tua soluzione è la migliore finora. :) Se sai come possiamo uscire dalla query subito dopo pre_get_posts, potrebbe essere fantastico. Grazie!
SarahCoding

@Dan posts_pre_querysarà disponibile dal 4.6;)
Sumit

Un altro approccio che viene in mente è quello di provare ad estendere la WP_Queryclasse con un get_posts()metodo personalizzato , con un possibile inizio precoce e che chiama parent::get_posts() e cerca di sovrascrivere la query rilevante con essa. Ma non so se questo avrebbe funzionato o avrebbe senso con il tuo caso qui ;-) @Dan
birgire

1
Forse un po 'di Aerosmith - Livin' On The Edge potrebbe essere d'aiuto in questo ;-) @Dan
birgire


2

Sì, è possibile a seconda di ciò che si desidera memorizzare nella cache. Ho fatto una cosa simile per memorizzare nella cache il ciclo principale sulla nostra homepage. Fondamentalmente puoi usare posts_requeste posts_resultsper dirottare la query e colpire la cache invece di usare anche found_postsper correggere l'impaginazione.

Esempio davvero approssimativo estratto dal nostro codice (non testato) ma dovresti aiutarti a ottenere l'idea:

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

Altro qui: https://www.reddit.com/r/Wordpress/comments/19crcn/best_practice_for_hijacking_main_loop_and_caching/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.