J'essaie d'utiliser WP Redis pour mettre en cache l'intégralité de $ wp_query object with key est $ query_vars_hash .
Voici comment $wp_query
a été ajouté à $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');
}
});
Ensuite, je dois vérifier si une requête a déjà été mise en cache avant que WP_Query
puisse récupérer les publications:
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.
}
});
Problème :
return
ou exit
ne fonctionne pas dans ce cas. Ensuite, WP_Query
continuera à utiliser la base de données pour récupérer à nouveau les publications.
Question :
Quel que soit le plugin, est-il possible d'arrêter complètement la récupération de WP_Query
?
Pour le moment, ce n'est pas possible.
Lorsque 'pre_get_posts'
est exécuté, il est trop tard pour arrêter WP_Query
afin d'effectuer une requête.
WordPress lui-même, lorsque vous essayez d'interroger une taxonomie qui n'existe pas, ajoute AND (0 = 1)
à la clause WHERE
de la requête SQL, pour s'assurer qu'il ne renvoie aucun résultat très rapidement ...
Il existe un ticket trac avec un patch qui atterrira probablement dans le noyau avec WP 4.6, qui introduit un nouveau filtre: 'posts_pre_query'
. Si vous retournez un tableau sur ce filtre, WP_Query
arrête le traitement et utilise le tableau fourni en tant que tableau posts.
Cela pourrait en quelque sorte vous aider à mettre en œuvre ce que vous essayez de faire.
En attendant, tout ce que vous pouvez faire est en quelque sorte hackish , le trick core lui-même est plutôt hack.
Récemment, je commence à utiliser un trick lorsque je veux empêcher WordPress de faire des choses que je ne peux pas arrêter proprement: je lève une exception et je l’attrape pour continuer le flux d’applications.
Je vais vous montrer un exemple. Notez que tout le code ici est complètement non testé.
Tout d'abord, écrivons une exception personnalisée:
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'exception est conçue pour agir comme une sorte deDTOpour transporter un objet de requête, de sorte que vous puissiez obtenir et l'utiliser dans un bloc catch
.
Mieux expliqué avec le code:
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());
}
}
});
Cela devrait plus ou moins fonctionner, cependant, il y a beaucoup de hooks que vous n'allez pas utiliser, par exemple "the_posts"
et bien plus encore ... si vous avez du code qui utilise l'un de ces hooks pour le déclencher, il se cassera.
Vous pouvez utiliser la fonction cached_query_set
pour déclencher certains des points d'ancrage nécessaires à votre thème/plug-in.
C'est PHP question plus qu'une question WordPress.
Comme @Mark a commenté:
revenant de l'action ne revient pas par magie de la fonction appelante
C'est vrai. Placer return
dans function signifie quitter la fonction et placer return dans un fichier PHP signifie quitter le fichier. Ne vous trompez pas avec PHP construct exit()
: P (vous trouverez peut-être une meilleure réponse sur SO à propos de PHP return
).
Et pour répondre à ta question
Vous pouvez réduire la charge de la requête en récupérant une seule colonne au lieu d'une table complète. J'aime @birgire did here Supprimer la requête de page d'accueil
Peut-être une meilleure réponse à venir. Je viens de partager ce que je sais :)
Cela sera rendu possible en 4.6 (en supposant qu'il n'y ait pas de changement jusqu'à la sortie) avec le nouveau filtre posts_pre_query
/ https://core.trac.wordpress.org/ticket/36687
Oui, c'est possible en fonction de ce que vous voulez mettre en cache. J'ai fait la même chose pour cacher la boucle principale sur notre page d'accueil. Vous pouvez utiliser essentiellement les posts_request
et posts_results
pour pirater la requête et accéder au cache à la place, puis utilisez également found_posts
pour corriger la pagination.
Exemple très approximatif tiré de notre code (non testé), mais vous devriez vous aider à vous faire une idée:
<?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 );