web-dev-qa-db-fra.com

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

Je travaille sur un site Web doté d'une fonction de recherche qui permet aux utilisateurs d'effectuer une recherche dans de nombreuses méta-publications. Il existe un modèle de recherche spécifique pour lequel je ne souhaite renvoyer aucun résultat. Techniquement, WP_Query trouvera des résultats dans la base de données, mais je voudrais l’ignorer pour le forcer à ne renvoyer aucun résultat, ce qui entraînerait l’échec de if( $example->have_posts() ).

Existe-t-il une sorte de paramètre que je peux transmettre à WP_Query, tel que 'force_no_results' => true, qui l'obligera à ne renvoyer aucun résultat?

21
Brian

Essayer

'post__in' => array(0)

Simple et au point.

26
David Labbe

Curieusement, il n’existe aucun moyen propre/explicite de court-circuiter WP_Query.

Si c'est main query vous pourriez résoudre quelque chose autour de WP->parse_request(), il semble y avoir un filtre relativement récent (3.5) do_parse_request.

Mais pour WP_Query, les hacks sont corrects, par exemple court-circuiter une requête SQL en ajoutant AND 1=0 via le filtre posts_where, etc.

3
Rarst

Les problèmes pour définir 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
  • Les requêtes WordPress ont 19 hooks 'posts_*' différents ('posts_where', 'post_join', etc.) qui agissent sur la requête, vous ne pouvez donc jamais être sûr que même si vous définissez un paramètre non inexistant, la requête ne renvoie aucun résultat, une simple clause OR renvoyée par un filtre .

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

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

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

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

et ajoutez un callback exécuté sur '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 code ne s'exécute que sur 'pre_get_posts' le plus tard possible. Si l'argument 'force_no_results' est présent dans la requête, alors:

  1. commencez par supprimer tous les filtres susceptibles d'interférer avec la requête et stockez-les dans un tableau d'assistance
  2. après vous être assuré que les filtres sont déclenchés, ajoutez un filtre qui renvoie ce type de requête: SELECT ID FROM wp_posts WHERE 0 = 1 une fois tous les filtres supprimés, il n'y a plus de possibilité que cette requête soit modifiée, elle est très rapide et n'a aucun résultat à coup 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 fonctionneront comme prévu.
2
gmazzap