Czy mogę zmusić WP_Query do zwrócenia żadnych wyników?

23

Pracuję nad witryną z funkcją wyszukiwania, która pozwala użytkownikom przeszukiwać wiele meta postów. Istnieje określony wzorzec wyszukiwania, dla którego chciałbym siłą nie zwracać wyników. WP_Query technicznie znajdzie wyniki w bazie danych, ale chciałbym to przesłonić, aby wymusić, aby nie zwracał żadnych wyników, aby spowodować if( $example->have_posts() )niepowodzenie.

Czy istnieje jakiś parametr, który mogę przekazać do WP_Query 'force_no_results' => true, co zmusi go do zwrócenia żadnych wyników?

Brian
źródło
1
Wygląda na to, że pytasz o już określoną implementację, a nie pytasz, jak rozwiązać główny problem. Czytając między wierszami, myślę, że naprawdę powinieneś zapytać: jak sprawić, by określony wzorzec wyszukiwania był nie do zapytania? . Powodowanie WP_Query()braku wyników może, ale nie musi być najlepszym sposobem na udzielenie odpowiedzi na to pytanie. Pomocne może być również opisanie wzorca wyszukiwania, którego nie można zapytać. Znajomość wzorca wyszukiwania może pomóc w znalezieniu rozwiązania.
Chip Bennett,

Odpowiedzi:

28

Próbować

'post__in' => array(0)

Prosto i do rzeczy.

David Labbe
źródło
Moją pierwszą myślą było - to musi gdzieś pójść nie tak, ale po przejrzeniu powiązanego kodu powinno to właściwie działać całkiem nieźle. :)
Rarst
5
zero jest bardzo ważne, ponieważ tylko pusta tablica zwróci ostatnie posty.
Mark Kaplun,
Dzięki! To rozwiązało dla mnie błąd, podobnie jak post__inzwracanie postów po przekazaniu pustej tablicy ... array(0)działa świetnie! Jest to dziwne, ale można go przypisać do problemu, który pojawił się w jądrze WP jako błąd, ale został pozostawiony bez zmian,
EranSch
3

Co ciekawe, nie ma czystego / wyraźnego sposobu na zwarcie WP_Query.

Jeśli jest to główne zapytanie, możesz coś rozwiązać WP->parse_request(), wydaje się, że jest tam stosunkowo nowy (3.5) do_parse_requestfiltr.

Ale WP_Querysame w sobie brudne hacki są zwykle w porządku, takie jak zwieranie zapytań SQL przez dodanie AND 1=0przez posts_wherefiltr itp.

Rarst
źródło
2
Dzięki za informację. To była druga pętla. I właśnie skończyło się na robieniu brudnego hacka, "post_type" => "break_loop"który nie istnieje.
Brian
2

Problemy z ustawieniem parametru zapytania na nieistniejącą wartość to 2:

  • Zapytanie zostanie uruchomione, więc nawet jeśli już wiesz, że nie będzie wyników, musisz zapłacić niewielką cenę za wydajność
  • Zapytania WordPress mają 19 różnych 'posts_*'zaczepów filtrów ( 'posts_where', 'post_join'itp.), Które działają na zapytania, więc nigdy nie możesz być pewien, że nawet ustawienie nieistniejącego parametru zapytania nie zwróci żadnych wyników, prosta ORklauzula zwrócona przez filtr sprawi, że coś zwróci.

Potrzebujesz trochę hardcorowej rutyny, aby mieć pewność, że zapytanie nie zwróci żadnego wyniku i nie wystąpi (lub będzie bardzo minimalny) problem z wydajnością.

Aby uruchomić tę procedurę, możesz użyć każdej metody, technicznie możesz przekazać dowolny argument WP_Query, argumenty zdarzenia, które nie istnieją.

Więc jeśli lubisz coś takiego 'force_no_results' => true, możesz użyć tego w następujący sposób:

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

i dodaj wywołanie zwrotne działające na nim 'pre_get_posts', wykonując ciężką pracę:

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

To, co robi ten kod, jest uruchamiane 'pre_get_posts'tak późno, jak to możliwe. Jeśli w zapytaniu występuje argument „force_no_results”, wówczas:

  1. najpierw usuń wszystkie możliwe filtry, które mogą zakłócać zapytanie, i zapisz je w tablicy pomocniczej
  2. po upewnieniu się, że filtr jest uruchomiony, dodaj filtr, który zwraca takie żądanie: SELECT ID FROM wp_posts WHERE 0 = 1po usunięciu wszystkich filtrów nie ma możliwości zmiany tego zapytania i jest ono bardzo szybkie i na pewno nie ma żadnego wyniku
  3. natychmiast po uruchomieniu tego zapytania przywracane są wszystkie oryginalne filtry (jeśli takie istniały), a wszystkie kolejne zapytania działają zgodnie z oczekiwaniami.
gmazzap
źródło