Puis-je forcer WP_Query à ne retourner aucun résultat?

23

Je travaille sur un site Web avec une fonction de recherche qui permet aux utilisateurs de rechercher à travers un grand nombre de méta post. Il existe un modèle de recherche spécifique pour lequel je ne voudrais renvoyer de force aucun résultat. Techniquement, le WP_Query trouvera des résultats dans la base de données, mais j'aimerais outrepasser cela d'une manière ou d'une autre pour le forcer à ne retourner aucun résultat pour déclencher l' if( $example->have_posts() )échec.

Y a-t-il une sorte de paramètre que je peux transmettre à WP_Query comme 'force_no_results' => truecela le forcerait à ne retourner aucun résultat?

Brian
la source
1
Il semble que vous demandiez une implémentation déjà déterminée, plutôt que de savoir comment résoudre le problème racine. En lisant entre les lignes, je pense que ce que vous devriez vraiment demander est: comment puis-je rendre un modèle de recherche spécifique impossible à interroger? . Causer WP_Query()de ne retourner aucun résultat peut ou non être la meilleure façon de répondre à cette question. Il pourrait également être utile de décrire le modèle de recherche que vous souhaitez rendre impossible à interroger. Connaître le modèle de recherche pourrait aider à trouver une solution.
Chip Bennett

Réponses:

28

Essayer

'post__in' => array(0)

Simple et précis.

David Labbe
la source
Ma première pensée a été - ça doit mal tourner quelque part, mais en parcourant le code associé, cela devrait fonctionner plutôt bien. :)
Rarst
5
le zéro est très important car juste un tableau vide renverra les messages récents.
Mark Kaplun
Merci! Cela a résolu un bogue pour moi, tout comme le post__inrenvoi de messages lors du passage d'un tableau vide ... array(0)fonctionne très bien! C'est bizarre, mais cela peut en fait être attribué à un problème qui est survenu dans WP core en tant que bogue, mais qui a ensuite été laissé tel quel
EranSch
3

Curieusement, il n'y a pas de moyen clair / explicite de court-circuiter WP_Query.

Si c'est la requête principale que vous pourriez résoudre WP->parse_request(), il semble y avoir un do_parse_requestfiltre relativement récent (3.5) .

Mais pour WP_Querylui-même, les hacks sales sont généralement en ordre, comme court-circuiter la requête SQL en l'ajoutant AND 1=0via un posts_wherefiltre, etc.

Rarst
la source
2
Merci pour l'info. C'était une boucle secondaire. Et je viens de finir par faire un hack sale comme "post_type" => "break_loop"un type de message inexistant.
Brian
2

Les problèmes liés à la définition d'un paramètre de requête sur une valeur inexistante sont 2:

  • La requête s'exécutera, donc même si vous savez déjà qu'il n'y aura aucun résultat, il y a un petit prix de performance à payer
  • Requêtes WordPress a 19 différents 'posts_*'crochets de filtre ( 'posts_where', 'post_join', etc ..) qui agissent sur requête, de sorte que vous ne pouvez jamais être sûr que même la mise en unexistent param la requête ne renvoie aucun résultat, un simple ORarticle retourné par un filtre quelque chose faire de retour.

Vous avez besoin d'un peu de routine hardcore pour être sûr qu'une requête ne renvoie aucun résultat et qu'il n'y a pas de problème de performance (ou très minimun).

Pour déclencher cette routine, vous pouvez utiliser toutes les méthodes, techniquement vous pouvez passer n'importe quel argument à des arguments d' WP_Queryévénement qui n'existent pas.

Donc, si vous aimez quelque chose comme ça 'force_no_results' => true, vous pouvez l'utiliser comme ceci:

$a = new WP_Query( array( 's' => 'foo', 'force_no_results' => true ) );

et ajoutez un rappel en cours d'exécution 'pre_get_posts'qui fait le travail dur:

add_action( 'pre_get_posts', function( $q ) {
  if (array_key_exists('force_no_results', $q->query) && $q->query['force_no_results']) {
    $q->query = $q->query_vars = array();
    $added = array();
    $filters = array(
      'where', 'where_paged', 'join', 'join_paged', 'groupby', 'orderby', 'distinct',
      'limits', 'fields', 'request', 'clauses', 'where_request', 'groupby_request',
      'join_request', 'orderby_request', 'distinct_request','fields_request',
      'limits_request', 'clauses_request'
    );
    // remove all possible interfering filter and save for later restore
    foreach ( $filters as $f ) {
      if ( isset($GLOBALS['wp_filter']["posts_{$f}"]) ) {
        $added["posts_{$f}"] = $GLOBALS['wp_filter']["posts_{$f}"];
        unset($GLOBALS['wp_filter']["posts_{$f}"]);
      }
    }
    // be sure filters are not suppressed
    $q->set( 'suppress_filters', FALSE );
    $done = 0;
    // use a filter to return a non-sense request
    add_filter('posts_request', function( $r ) use( &$done ) {
      if ( $done === 0 ) { $done = 1;
        $r = "SELECT ID FROM {$GLOBALS['wpdb']->posts} WHERE 0 = 1";
      }
      return $r;
    });
    // restore any filter that was added and we removed
    add_filter('posts_results', function( $posts ) use( &$done, $added ) {
      if ( $done === 1 ) { $done = 2;
        foreach ( $added as $hook => $filters ) {
          $GLOBALS['wp_filter'][$hook] = $filters;
        }
      }
      return $posts;
    });
  }
}, PHP_INT_MAX );

Ce que fait ce code est exécuté le 'pre_get_posts'plus tard possible. Si l'argument 'force_no_results' est présent dans la requête, alors:

  1. supprimez d'abord tous les filtres possibles susceptibles d'interférer avec la requête, puis stockez-les dans un tableau d'aide
  2. après avoir vérifié que le filtre est déclenché, ajoutez un filtre qui retourne ce genre de requête: SELECT ID FROM wp_posts WHERE 0 = 1une fois tous les filtres supprimés, il n'y a aucune possibilité que cette requête soit modifiée et elle est très rapide, et n'a aucun résultat sûr
  3. immédiatement après l'exécution de cette requête, tous les filtres d'origine (le cas échéant) sont restaurés et toutes les requêtes suivantes fonctionnent comme prévu.
gmazzap
la source